Commit 8ea90796 authored by Dominic Mazzoni's avatar Dominic Mazzoni Committed by Commit Bot

Add class for snapshotting the accessibility tree.

For exporting tagged PDFs, we need the ability to
take a snapshot of the accessibility tree for a
RenderFrame, and to have the accessibility context
stay alive long enough that IDs in the snapshot,
and IDs sent to the printing system, are consistent.

We already had code to do AX tree snapshotting,
so this patch is just some refactoring to make it
a class instead, and to have it return a public
type rather than a content-specific type.

This will be used in the follow-up change that
implements support for tagged PDFs:
http://crrev.com/c/1970742

Bug: 607777
Change-Id: I5163561c2cabcb2dd3579ce9d7a2de8ac96106d4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1970579
Commit-Queue: Dominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarAaron Leventhal <aleventhal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#727483}
parent 10ebfb7d
......@@ -25,6 +25,7 @@
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/web/web_navigation_policy.h"
#include "ui/accessibility/ax_mode.h"
#include "ui/accessibility/ax_tree_update.h"
namespace blink {
class AssociatedInterfaceProvider;
......@@ -67,6 +68,21 @@ struct ContextMenuParams;
struct WebPluginInfo;
struct WebPreferences;
// A class that takes a snapshot of the accessibility tree. Accessibility
// support in Blink is enabled for the lifetime of this object, which can
// be useful if you need consistent IDs between multiple snapshots.
class AXTreeSnapshotter {
public:
AXTreeSnapshotter() = default;
// Return in |accessibility_tree| a snapshot of the accessibility tree
// for the frame with the given accessibility mode. If |max_node_count|
// is nonzero, return no more than that many nodes.
virtual void Snapshot(ui::AXMode ax_mode,
size_t max_node_count,
ui::AXTreeUpdate* accessibility_tree) = 0;
virtual ~AXTreeSnapshotter() = default;
};
// This interface wraps functionality, which is specific to frames, such as
// navigation. It provides communication with a corresponding RenderFrameHost
// in the browser process.
......@@ -116,6 +132,9 @@ class CONTENT_EXPORT RenderFrame : public IPC::Listener,
// Return the RenderAccessibility associated with this frame.
virtual RenderAccessibility* GetRenderAccessibility() = 0;
// Return an object that can take a snapshot of the accessibility tree.
virtual std::unique_ptr<AXTreeSnapshotter> CreateAXTreeSnapshotter() = 0;
// Get the routing ID of the frame.
virtual int GetRoutingID() = 0;
......
......@@ -91,38 +91,66 @@ namespace content {
// usage.
const size_t kMaxSnapshotNodeCount = 5000;
// static
void RenderAccessibilityImpl::SnapshotAccessibilityTree(
RenderFrameImpl* render_frame,
AXContentTreeUpdate* response,
ui::AXMode ax_mode) {
TRACE_EVENT0("accessibility",
"RenderAccessibilityImpl::SnapshotAccessibilityTree");
AXTreeSnapshotterImpl::AXTreeSnapshotterImpl(RenderFrameImpl* render_frame)
: render_frame_(render_frame) {
DCHECK(render_frame->GetWebFrame());
blink::WebDocument document_ = render_frame->GetWebFrame()->GetDocument();
context_ = std::make_unique<WebAXContext>(document_);
}
DCHECK(render_frame);
DCHECK(response);
if (!render_frame->GetWebFrame())
return;
AXTreeSnapshotterImpl::~AXTreeSnapshotterImpl() = default;
void AXTreeSnapshotterImpl::Snapshot(ui::AXMode ax_mode,
size_t max_node_count,
ui::AXTreeUpdate* response) {
// Get a snapshot of the accessibility tree as an AXContentNodeData.
AXContentTreeUpdate content_tree;
SnapshotContentTree(ax_mode, max_node_count, &content_tree);
// As a sanity check, node_id_to_clear and event_from should be uninitialized
// if this is a full tree snapshot. They'd only be set to something if
// this was indeed a partial update to the tree (which we don't want).
DCHECK_EQ(0, content_tree.node_id_to_clear);
DCHECK_EQ(ax::mojom::EventFrom::kNone, content_tree.event_from);
// We now have a complete serialization of the accessibility tree, but it
// includes a few fields we don't want to export outside of content/,
// so copy it into a more generic ui::AXTreeUpdate instead.
response->root_id = content_tree.root_id;
response->nodes.resize(content_tree.nodes.size());
response->node_id_to_clear = content_tree.node_id_to_clear;
response->event_from = content_tree.event_from;
// AXNodeData is a superclass of AXContentNodeData, so we can convert
// just by assigning.
response->nodes.assign(content_tree.nodes.begin(), content_tree.nodes.end());
}
WebDocument document = render_frame->GetWebFrame()->GetDocument();
WebAXContext context(document);
WebAXObject root = context.Root();
void AXTreeSnapshotterImpl::SnapshotContentTree(ui::AXMode ax_mode,
size_t max_node_count,
AXContentTreeUpdate* response) {
WebAXObject root = context_->Root();
if (!root.UpdateLayoutAndCheckValidity())
return;
BlinkAXTreeSource tree_source(render_frame, ax_mode);
BlinkAXTreeSource tree_source(render_frame_, ax_mode);
tree_source.SetRoot(root);
ScopedFreezeBlinkAXTreeSource freeze(&tree_source);
BlinkAXTreeSerializer serializer(&tree_source);
serializer.set_max_node_count(kMaxSnapshotNodeCount);
if (serializer.SerializeChanges(context.Root(), response))
// The serializer returns an AXContentTreeUpdate, which can store a complete
// or a partial accessibility tree. AXTreeSerializer is stateful, but the
// first time you serialize from a brand-new tree you're guaranteed to get a
// complete tree.
BlinkAXTreeSerializer serializer(&tree_source);
if (max_node_count)
serializer.set_max_node_count(max_node_count);
if (serializer.SerializeChanges(root, response))
return;
// It's possible for the page to fail to serialize the first time due to
// aria-owns rearranging the page while it's being scanned. Try a second
// time.
*response = AXContentTreeUpdate();
if (serializer.SerializeChanges(context.Root(), response))
if (serializer.SerializeChanges(root, response))
return;
// It failed again. Clear the response object because it might have errors.
......@@ -130,6 +158,22 @@ void RenderAccessibilityImpl::SnapshotAccessibilityTree(
LOG(WARNING) << "Unable to serialize accessibility tree.";
}
// static
void RenderAccessibilityImpl::SnapshotAccessibilityTree(
RenderFrameImpl* render_frame,
AXContentTreeUpdate* response,
ui::AXMode ax_mode) {
TRACE_EVENT0("accessibility",
"RenderAccessibilityImpl::SnapshotAccessibilityTree");
DCHECK(render_frame);
DCHECK(response);
if (!render_frame->GetWebFrame())
return;
AXTreeSnapshotterImpl snapshotter(render_frame);
snapshotter.SnapshotContentTree(ax_mode, kMaxSnapshotNodeCount, response);
}
RenderAccessibilityImpl::RenderAccessibilityImpl(RenderFrameImpl* render_frame,
ui::AXMode mode)
: RenderFrameObserver(render_frame),
......
......@@ -14,6 +14,7 @@
#include "content/common/content_export.h"
#include "content/public/renderer/plugin_ax_tree_source.h"
#include "content/public/renderer/render_accessibility.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/renderer/accessibility/blink_ax_tree_source.h"
#include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h"
......@@ -39,6 +40,33 @@ namespace content {
class AXImageAnnotator;
class RenderFrameImpl;
using BlinkAXTreeSerializer = ui::
AXTreeSerializer<blink::WebAXObject, AXContentNodeData, AXContentTreeData>;
class AXTreeSnapshotterImpl : public AXTreeSnapshotter {
public:
explicit AXTreeSnapshotterImpl(RenderFrameImpl* render_frame);
~AXTreeSnapshotterImpl() override;
// AXTreeSnapshotter implementation.
void Snapshot(ui::AXMode ax_mode,
size_t max_node_count,
ui::AXTreeUpdate* accessibility_tree) override;
// Same as above, but returns in |accessibility_tree| a AXContentTreeUpdate
// with content-specific metadata, instead of an AXTreeUpdate.
void SnapshotContentTree(ui::AXMode ax_mode,
size_t max_node_count,
AXContentTreeUpdate* accessibility_tree);
private:
RenderFrameImpl* render_frame_;
std::unique_ptr<blink::WebAXContext> context_;
AXTreeSnapshotterImpl(const AXTreeSnapshotterImpl&) = delete;
AXTreeSnapshotterImpl& operator=(const AXTreeSnapshotterImpl&) = delete;
};
// The browser process implements native accessibility APIs, allowing
// assistive technology (e.g., screen readers, magnifiers) to access and
// control the web contents with high-level APIs. These APIs are also used
......@@ -201,10 +229,6 @@ class CONTENT_EXPORT RenderAccessibilityImpl
BlinkAXTreeSource tree_source_;
// The serializer that sends accessibility messages to the browser process.
using BlinkAXTreeSerializer =
ui::AXTreeSerializer<blink::WebAXObject,
AXContentNodeData,
AXContentTreeData>;
BlinkAXTreeSerializer serializer_;
using PluginAXTreeSerializer = ui::AXTreeSerializer<const ui::AXNode*,
......
......@@ -2913,6 +2913,10 @@ RenderAccessibility* RenderFrameImpl::GetRenderAccessibility() {
return render_accessibility_;
}
std::unique_ptr<AXTreeSnapshotter> RenderFrameImpl::CreateAXTreeSnapshotter() {
return std::make_unique<AXTreeSnapshotterImpl>(this);
}
int RenderFrameImpl::GetRoutingID() {
return routing_id_;
}
......
......@@ -434,6 +434,7 @@ class CONTENT_EXPORT RenderFrameImpl
// RenderFrame implementation:
RenderView* GetRenderView() override;
RenderAccessibility* GetRenderAccessibility() override;
std::unique_ptr<AXTreeSnapshotter> CreateAXTreeSnapshotter() override;
int GetRoutingID() override;
blink::WebLocalFrame* GetWebFrame() override;
const WebPreferences& GetWebkitPreferences() override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment