summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/browser/accessibility/browser_accessibility_manager.cc2
-rw-r--r--content/browser/accessibility/browser_accessibility_manager_unittest.cc18
-rw-r--r--content/browser/accessibility/browser_accessibility_win_unittest.cc14
-rw-r--r--content/browser/renderer_host/render_view_host_impl.cc8
-rw-r--r--content/common/accessibility_messages.h11
-rw-r--r--content/content_renderer.gypi4
-rw-r--r--content/renderer/accessibility/accessibility_node_serializer.h23
-rw-r--r--content/renderer/accessibility/blink_ax_tree_source.cc (renamed from content/renderer/accessibility/accessibility_node_serializer.cc)162
-rw-r--r--content/renderer/accessibility/blink_ax_tree_source.h46
-rw-r--r--content/renderer/accessibility/renderer_accessibility_browsertest.cc104
-rw-r--r--content/renderer/accessibility/renderer_accessibility_complete.cc297
-rw-r--r--content/renderer/accessibility/renderer_accessibility_complete.h47
-rw-r--r--content/renderer/accessibility/renderer_accessibility_focus_only.cc6
-rw-r--r--ui/accessibility/ax_tree_serializer.h23
-rw-r--r--ui/accessibility/ax_tree_source.h2
15 files changed, 281 insertions, 486 deletions
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 8481b07..d212931 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -131,7 +131,7 @@ void BrowserAccessibilityManager::OnAccessibilityEvents(
// Process all changes to the accessibility tree first.
for (uint32 index = 0; index < params.size(); index++) {
const AccessibilityHostMsg_EventParams& param = params[index];
- if (!UpdateNodes(param.nodes))
+ if (!UpdateNodes(param.update.nodes))
return;
// Set initial focus when a page is loaded.
diff --git a/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
index 22d877b..5e238a0 100644
--- a/content/browser/accessibility/browser_accessibility_manager_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -267,8 +267,8 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
params.push_back(AccessibilityHostMsg_EventParams());
AccessibilityHostMsg_EventParams* msg = &params[0];
msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
- msg->nodes.push_back(tree2_root);
- msg->nodes.push_back(tree2_child0);
+ msg->update.nodes.push_back(tree2_root);
+ msg->update.nodes.push_back(tree2_child0);
msg->id = tree2_root.id;
manager->OnAccessibilityEvents(params);
@@ -444,9 +444,9 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
params.push_back(AccessibilityHostMsg_EventParams());
AccessibilityHostMsg_EventParams* msg = &params[0];
msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
- msg->nodes.push_back(tree2_container);
- msg->nodes.push_back(tree2_child0);
- msg->nodes.push_back(tree2_grandchild0);
+ msg->update.nodes.push_back(tree2_container);
+ msg->update.nodes.push_back(tree2_child0);
+ msg->update.nodes.push_back(tree2_grandchild0);
msg->id = tree2_container.id;
manager->OnAccessibilityEvents(params);
@@ -551,10 +551,10 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
params.push_back(AccessibilityHostMsg_EventParams());
AccessibilityHostMsg_EventParams* msg = &params[0];
msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
- msg->nodes.push_back(tree2_1);
- msg->nodes.push_back(tree2_4);
- msg->nodes.push_back(tree2_5);
- msg->nodes.push_back(tree2_6);
+ msg->update.nodes.push_back(tree2_1);
+ msg->update.nodes.push_back(tree2_4);
+ msg->update.nodes.push_back(tree2_5);
+ msg->update.nodes.push_back(tree2_6);
msg->id = tree2_1.id;
manager->OnAccessibilityEvents(params);
diff --git a/content/browser/accessibility/browser_accessibility_win_unittest.cc b/content/browser/accessibility/browser_accessibility_win_unittest.cc
index 83edae7..42b211b 100644
--- a/content/browser/accessibility/browser_accessibility_win_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_win_unittest.cc
@@ -235,7 +235,7 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
text2.SetName("old text");
AccessibilityHostMsg_EventParams param;
param.event_type = ui::AX_EVENT_CHILDREN_CHANGED;
- param.nodes.push_back(text2);
+ param.update.nodes.push_back(text2);
param.id = text2.id;
std::vector<AccessibilityHostMsg_EventParams> events;
events.push_back(param);
@@ -307,7 +307,7 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) {
root.child_ids.clear();
AccessibilityHostMsg_EventParams param;
param.event_type = ui::AX_EVENT_CHILDREN_CHANGED;
- param.nodes.push_back(root);
+ param.update.nodes.push_back(root);
param.id = root.id;
std::vector<AccessibilityHostMsg_EventParams> events;
events.push_back(param);
@@ -637,8 +637,8 @@ TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) {
params.push_back(AccessibilityHostMsg_EventParams());
AccessibilityHostMsg_EventParams* msg = &params[0];
msg->event_type = ui::AX_EVENT_LOAD_COMPLETE;
- msg->nodes.push_back(tree1_1);
- msg->nodes.push_back(tree1_2);
+ msg->update.nodes.push_back(tree1_1);
+ msg->update.nodes.push_back(tree1_2);
msg->id = tree1_1.id;
manager->OnAccessibilityEvents(params);
@@ -662,9 +662,9 @@ TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) {
tree2_2.id = 3;
tree2_2.role = ui::AX_ROLE_BUTTON;
- msg->nodes.clear();
- msg->nodes.push_back(tree2_1);
- msg->nodes.push_back(tree2_2);
+ msg->update.nodes.clear();
+ msg->update.nodes.push_back(tree2_1);
+ msg->update.nodes.push_back(tree2_2);
msg->id = tree2_1.id;
// Fire another load complete.
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 43bfeed9..c5ed9d5 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -1728,7 +1728,7 @@ void RenderViewHostImpl::OnAccessibilityEvents(
for (unsigned int i = 0; i < params.size(); ++i) {
const AccessibilityHostMsg_EventParams& param = params[i];
AXEventNotificationDetails detail(
- param.nodes, param.event_type, param.id, GetRoutingID());
+ param.update.nodes, param.event_type, param.id, GetRoutingID());
details.push_back(detail);
}
@@ -1747,12 +1747,10 @@ void RenderViewHostImpl::OnAccessibilityEvents(
const AccessibilityHostMsg_EventParams& param = params[i];
if (static_cast<int>(param.event_type) < 0)
continue;
- ui::AXTreeUpdate update;
- update.nodes = param.nodes;
if (!ax_tree_)
- ax_tree_.reset(new ui::AXTree(update));
+ ax_tree_.reset(new ui::AXTree(param.update));
else
- CHECK(ax_tree_->Unserialize(update)) << ax_tree_->error();
+ CHECK(ax_tree_->Unserialize(param.update)) << ax_tree_->error();
accessibility_testing_callback_.Run(param.event_type);
}
}
diff --git a/content/common/accessibility_messages.h b/content/common/accessibility_messages.h
index c86720d..1830a1b 100644
--- a/content/common/accessibility_messages.h
+++ b/content/common/accessibility_messages.h
@@ -15,6 +15,7 @@
#include "ipc/param_traits_macros.h"
#include "third_party/WebKit/public/web/WebAXEnums.h"
#include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_tree_update.h"
#undef IPC_MESSAGE_EXPORT
#define IPC_MESSAGE_EXPORT CONTENT_EXPORT
@@ -45,10 +46,14 @@ IPC_STRUCT_TRAITS_BEGIN(ui::AXNodeData)
IPC_STRUCT_TRAITS_MEMBER(child_ids)
IPC_STRUCT_TRAITS_END()
+IPC_STRUCT_TRAITS_BEGIN(ui::AXTreeUpdate)
+ IPC_STRUCT_TRAITS_MEMBER(node_id_to_clear)
+ IPC_STRUCT_TRAITS_MEMBER(nodes)
+IPC_STRUCT_TRAITS_END()
+
IPC_STRUCT_BEGIN(AccessibilityHostMsg_EventParams)
- // Vector of nodes in the tree that need to be updated before
- // sending the event.
- IPC_STRUCT_MEMBER(std::vector<ui::AXNodeData>, nodes)
+ // The tree update.
+ IPC_STRUCT_MEMBER(ui::AXTreeUpdate, update)
// Type of event.
IPC_STRUCT_MEMBER(ui::AXEvent, event_type)
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index e993306..159321d 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -65,10 +65,10 @@
'public/renderer/video_encode_accelerator.cc',
'public/renderer/video_encode_accelerator.h',
'public/renderer/web_preferences.h',
- 'renderer/accessibility/accessibility_node_serializer.cc',
- 'renderer/accessibility/accessibility_node_serializer.h',
'renderer/accessibility/blink_ax_enum_conversion.cc',
'renderer/accessibility/blink_ax_enum_conversion.h',
+ 'renderer/accessibility/blink_ax_tree_source.cc',
+ 'renderer/accessibility/blink_ax_tree_source.h',
'renderer/accessibility/renderer_accessibility.cc',
'renderer/accessibility/renderer_accessibility.h',
'renderer/accessibility/renderer_accessibility_complete.cc',
diff --git a/content/renderer/accessibility/accessibility_node_serializer.h b/content/renderer/accessibility/accessibility_node_serializer.h
deleted file mode 100644
index a4021e7..0000000
--- a/content/renderer/accessibility/accessibility_node_serializer.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_ACCESSIBILITY_ACCESSIBILITY_NODE_SERIALIZER_H_
-#define CONTENT_RENDERER_ACCESSIBILITY_ACCESSIBILITY_NODE_SERIALIZER_H_
-
-#include "third_party/WebKit/public/web/WebAXObject.h"
-#include "ui/accessibility/ax_node_data.h"
-
-namespace content {
-
-void SerializeAccessibilityNode(
- const blink::WebAXObject& src,
- ui::AXNodeData* dst);
-
-bool ShouldIncludeChildNode(
- const blink::WebAXObject& parent,
- const blink::WebAXObject& child);
-
-} // namespace content
-
-#endif // CONTENT_RENDERER_ACCESSIBILITY_ACCESSIBILITY_NODE_SERIALIZER_H_
diff --git a/content/renderer/accessibility/accessibility_node_serializer.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index 56f977f..e7d6b08 100644
--- a/content/renderer/accessibility/accessibility_node_serializer.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/renderer/accessibility/accessibility_node_serializer.h"
+#include "content/renderer/accessibility/blink_ax_tree_source.h"
#include <set>
@@ -10,6 +10,7 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "content/renderer/accessibility/blink_ax_enum_conversion.h"
+#include "content/renderer/render_view_impl.h"
#include "third_party/WebKit/public/platform/WebRect.h"
#include "third_party/WebKit/public/platform/WebSize.h"
#include "third_party/WebKit/public/platform/WebString.h"
@@ -23,31 +24,36 @@
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebInputElement.h"
#include "third_party/WebKit/public/web/WebNode.h"
+#include "third_party/WebKit/public/web/WebView.h"
+using base::ASCIIToUTF16;
using base::UTF16ToUTF8;
using blink::WebAXObject;
using blink::WebDocument;
using blink::WebDocumentType;
using blink::WebElement;
+using blink::WebFrame;
using blink::WebNode;
using blink::WebVector;
+using blink::WebView;
namespace content {
+
namespace {
// Returns true if |ancestor| is the first unignored parent of |child|,
// which means that when walking up the parent chain from |child|,
// |ancestor| is the *first* ancestor that isn't marked as
// accessibilityIsIgnored().
-bool IsParentUnignoredOf(const WebAXObject& ancestor,
- const WebAXObject& child) {
+bool IsParentUnignoredOf(WebAXObject ancestor,
+ WebAXObject child) {
WebAXObject parent = child.parentObject();
while (!parent.isDetached() && parent.accessibilityIsIgnored())
parent = parent.parentObject();
return parent.equals(ancestor);
}
- bool IsTrue(std::string html_value) {
+bool IsTrue(std::string html_value) {
return LowerCaseEqualsASCII(html_value, "true");
}
@@ -85,17 +91,88 @@ void AddIntListAttributeFromWebObjects(ui::AXIntListAttribute attr,
dst->AddIntListAttribute(attr, ids);
}
-
} // Anonymous namespace
-void SerializeAccessibilityNode(
- const WebAXObject& src,
- ui::AXNodeData* dst) {
+BlinkAXTreeSource::BlinkAXTreeSource(RenderViewImpl* render_view)
+ : render_view_(render_view) {
+}
+
+BlinkAXTreeSource::~BlinkAXTreeSource() {
+}
+
+blink::WebAXObject BlinkAXTreeSource::GetRoot() const {
+ return GetMainDocument().accessibilityObject();
+}
+
+blink::WebAXObject BlinkAXTreeSource::GetFromId(int32 id) const {
+ return GetMainDocument().accessibilityObjectFromID(id);
+}
+
+int32 BlinkAXTreeSource::GetId(blink::WebAXObject node) const {
+ return node.axID();
+}
+
+void BlinkAXTreeSource::GetChildren(
+ blink::WebAXObject parent,
+ std::vector<blink::WebAXObject>* out_children) const {
+ bool is_iframe = false;
+ WebNode node = parent.node();
+ if (!node.isNull() && node.isElementNode()) {
+ WebElement element = node.to<WebElement>();
+ is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME"));
+ }
+
+ for (unsigned i = 0; i < parent.childCount(); i++) {
+ blink::WebAXObject child = parent.childAt(i);
+
+ // The child may be invalid due to issues in blink accessibility code.
+ if (child.isDetached())
+ continue;
+
+ // Skip children whose parent isn't |parent|.
+ // As an exception, include children of an iframe element.
+ if (!is_iframe && !IsParentUnignoredOf(parent, child))
+ continue;
+
+ out_children->push_back(child);
+ }
+}
+
+blink::WebAXObject BlinkAXTreeSource::GetParent(
+ blink::WebAXObject node) const {
+ // Blink returns ignored objects when walking up the parent chain,
+ // we have to skip those here. Also, stop when we get to the root
+ // element.
+ blink::WebAXObject root = GetRoot();
+ do {
+ if (node.equals(root))
+ return blink::WebAXObject();
+ node = node.parentObject();
+ } while (!node.isDetached() && node.accessibilityIsIgnored());
+
+ return node;
+}
+
+bool BlinkAXTreeSource::IsValid(blink::WebAXObject node) const {
+ return !node.isDetached(); // This also checks if it's null.
+}
+
+bool BlinkAXTreeSource::IsEqual(blink::WebAXObject node1,
+ blink::WebAXObject node2) const {
+ return node1.equals(node2);
+}
+
+blink::WebAXObject BlinkAXTreeSource::GetNull() const {
+ return blink::WebAXObject();
+}
+
+void BlinkAXTreeSource::SerializeNode(blink::WebAXObject src,
+ ui::AXNodeData* dst) const {
dst->role = AXRoleFromBlink(src.role());
dst->state = AXStateFromBlink(src);
dst->location = src.boundingBoxRect();
dst->id = src.axID();
- std::string name = base::UTF16ToUTF8(src.title());
+ std::string name = UTF16ToUTF8(src.title());
std::string value;
if (src.valueDescription().length()) {
@@ -140,9 +217,10 @@ void SerializeAccessibilityNode(
dst->AddIntListAttribute(ui::AX_ATTR_WORD_ENDS, word_ends);
}
- if (src.accessKey().length())
+ if (src.accessKey().length()) {
dst->AddStringAttribute(ui::AX_ATTR_ACCESS_KEY,
- UTF16ToUTF8(src.accessKey()));
+ UTF16ToUTF8(src.accessKey()));
+ }
if (src.actionVerb().length())
dst->AddStringAttribute(ui::AX_ATTR_ACTION, UTF16ToUTF8(src.actionVerb()));
if (src.isAriaReadOnly())
@@ -183,7 +261,7 @@ void SerializeAccessibilityNode(
dst->role == ui::AX_ROLE_ROW) &&
src.hierarchicalLevel() > 0) {
dst->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL,
- src.hierarchicalLevel());
+ src.hierarchicalLevel());
}
// Treat the active list box item as focused.
@@ -204,7 +282,7 @@ void SerializeAccessibilityNode(
if (!node.isNull() && node.isElementNode()) {
WebElement element = node.to<WebElement>();
- is_iframe = (element.tagName() == base::ASCIIToUTF16("IFRAME"));
+ is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME"));
if (LowerCaseEqualsASCII(element.getAttribute("aria-expanded"), "true"))
dst->state |= (1 << ui::AX_STATE_EXPANDED);
@@ -216,9 +294,9 @@ void SerializeAccessibilityNode(
ui::AX_ATTR_HTML_TAG,
StringToLowerASCII(UTF16ToUTF8(element.tagName())));
for (unsigned i = 0; i < element.attributeCount(); ++i) {
- std::string name = StringToLowerASCII(base::UTF16ToUTF8(
+ std::string name = StringToLowerASCII(UTF16ToUTF8(
element.attributeLocalName(i)));
- std::string value = base::UTF16ToUTF8(element.attributeValue(i));
+ std::string value = UTF16ToUTF8(element.attributeValue(i));
dst->html_attributes.push_back(std::make_pair(name, value));
}
@@ -250,10 +328,10 @@ void SerializeAccessibilityNode(
}
// Live region attributes
- live_atomic = base::UTF16ToUTF8(element.getAttribute("aria-atomic"));
- live_busy = base::UTF16ToUTF8(element.getAttribute("aria-busy"));
- live_status = base::UTF16ToUTF8(element.getAttribute("aria-live"));
- live_relevant = base::UTF16ToUTF8(element.getAttribute("aria-relevant"));
+ live_atomic = UTF16ToUTF8(element.getAttribute("aria-atomic"));
+ live_busy = UTF16ToUTF8(element.getAttribute("aria-busy"));
+ live_status = UTF16ToUTF8(element.getAttribute("aria-live"));
+ live_relevant = UTF16ToUTF8(element.getAttribute("aria-relevant"));
}
// Walk up the parent chain to set live region attributes of containers
@@ -269,22 +347,22 @@ void SerializeAccessibilityNode(
if (container_elem.hasAttribute("aria-atomic") &&
container_live_atomic.empty()) {
container_live_atomic =
- base::UTF16ToUTF8(container_elem.getAttribute("aria-atomic"));
+ UTF16ToUTF8(container_elem.getAttribute("aria-atomic"));
}
if (container_elem.hasAttribute("aria-busy") &&
container_live_busy.empty()) {
container_live_busy =
- base::UTF16ToUTF8(container_elem.getAttribute("aria-busy"));
+ UTF16ToUTF8(container_elem.getAttribute("aria-busy"));
}
if (container_elem.hasAttribute("aria-live") &&
container_live_status.empty()) {
container_live_status =
- base::UTF16ToUTF8(container_elem.getAttribute("aria-live"));
+ UTF16ToUTF8(container_elem.getAttribute("aria-live"));
}
if (container_elem.hasAttribute("aria-relevant") &&
container_live_relevant.empty()) {
container_live_relevant =
- base::UTF16ToUTF8(container_elem.getAttribute("aria-relevant"));
+ UTF16ToUTF8(container_elem.getAttribute("aria-relevant"));
}
}
container_accessible = container_accessible.parentObject();
@@ -334,7 +412,7 @@ void SerializeAccessibilityNode(
if (name.empty())
name = UTF16ToUTF8(document.title());
dst->AddStringAttribute(ui::AX_ATTR_DOC_TITLE,
- UTF16ToUTF8(document.title()));
+ UTF16ToUTF8(document.title()));
dst->AddStringAttribute(ui::AX_ATTR_DOC_URL, document.url().spec());
dst->AddStringAttribute(
ui::AX_ATTR_DOC_MIMETYPE,
@@ -425,15 +503,15 @@ void SerializeAccessibilityNode(
// parent is the row, the row adds it as a child, and the column adds it
// as an indirect child.
int child_count = src.childCount();
- std::vector<int32> indirect_child_ids;
for (int i = 0; i < child_count; ++i) {
WebAXObject child = src.childAt(i);
+ std::vector<int32> indirect_child_ids;
if (!is_iframe && !child.isDetached() && !IsParentUnignoredOf(src, child))
indirect_child_ids.push_back(child.axID());
- }
- if (indirect_child_ids.size() > 0) {
- dst->AddIntListAttribute(ui::AX_ATTR_INDIRECT_CHILD_IDS,
- indirect_child_ids);
+ if (indirect_child_ids.size() > 0) {
+ dst->AddIntListAttribute(
+ ui::AX_ATTR_INDIRECT_CHILD_IDS, indirect_child_ids);
+ }
}
WebVector<WebAXObject> controls;
@@ -461,26 +539,14 @@ void SerializeAccessibilityNode(
AddIntListAttributeFromWebObjects(ui::AX_ATTR_OWNS_IDS, owns, dst);
}
-bool ShouldIncludeChildNode(
- const WebAXObject& parent,
- const WebAXObject& child) {
- // The child may be invalid due to issues in webkit accessibility code.
- // Don't add children that are invalid thus preventing a crash.
- // https://bugs.webkit.org/show_bug.cgi?id=44149
- // TODO(ctguil): We may want to remove this check as webkit stabilizes.
- if (child.isDetached())
- return false;
-
- // Skip children whose parent isn't this - see indirect_child_ids, above.
- // As an exception, include children of an iframe element.
- bool is_iframe = false;
- WebNode node = parent.node();
- if (!node.isNull() && node.isElementNode()) {
- WebElement element = node.to<WebElement>();
- is_iframe = (element.tagName() == base::ASCIIToUTF16("IFRAME"));
- }
+blink::WebDocument BlinkAXTreeSource::GetMainDocument() const {
+ WebView* view = render_view_->GetWebView();
+ WebFrame* main_frame = view ? view->mainFrame() : NULL;
+
+ if (main_frame)
+ return main_frame->document();
- return (is_iframe || IsParentUnignoredOf(parent, child));
+ return WebDocument();
}
} // namespace content
diff --git a/content/renderer/accessibility/blink_ax_tree_source.h b/content/renderer/accessibility/blink_ax_tree_source.h
new file mode 100644
index 0000000..1417305
--- /dev/null
+++ b/content/renderer/accessibility/blink_ax_tree_source.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_ACCESSIBILITY_BLINK_AX_TREE_SOURCE_H_
+#define CONTENT_RENDERER_ACCESSIBILITY_BLINK_AX_TREE_SOURCE_H_
+
+#include "third_party/WebKit/public/web/WebAXObject.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_tree_source.h"
+
+namespace content {
+
+class RenderViewImpl;
+
+class BlinkAXTreeSource
+ : public ui::AXTreeSource<blink::WebAXObject> {
+ public:
+ BlinkAXTreeSource(RenderViewImpl* render_view);
+ virtual ~BlinkAXTreeSource();
+
+ // AXTreeSource implementation.
+ virtual blink::WebAXObject GetRoot() const OVERRIDE;
+ virtual blink::WebAXObject GetFromId(int32 id) const OVERRIDE;
+ virtual int32 GetId(blink::WebAXObject node) const OVERRIDE;
+ virtual void GetChildren(
+ blink::WebAXObject node,
+ std::vector<blink::WebAXObject>* out_children) const OVERRIDE;
+ virtual blink::WebAXObject GetParent(blink::WebAXObject node) const
+ OVERRIDE;
+ virtual void SerializeNode(blink::WebAXObject node,
+ ui::AXNodeData* out_data) const OVERRIDE;
+ virtual bool IsValid(blink::WebAXObject node) const OVERRIDE;
+ virtual bool IsEqual(blink::WebAXObject node1,
+ blink::WebAXObject node2) const OVERRIDE;
+ virtual blink::WebAXObject GetNull() const OVERRIDE;
+
+ blink::WebDocument GetMainDocument() const;
+
+ private:
+ RenderViewImpl* render_view_;
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_ACCESSIBILITY_BLINK_AX_TREE_SOURCE_H_
diff --git a/content/renderer/accessibility/renderer_accessibility_browsertest.cc b/content/renderer/accessibility/renderer_accessibility_browsertest.cc
index 95f2c42..f1b559b 100644
--- a/content/renderer/accessibility/renderer_accessibility_browsertest.cc
+++ b/content/renderer/accessibility/renderer_accessibility_browsertest.cc
@@ -23,36 +23,12 @@ namespace content {
class TestRendererAccessibilityComplete : public RendererAccessibilityComplete {
public:
explicit TestRendererAccessibilityComplete(RenderViewImpl* render_view)
- : RendererAccessibilityComplete(render_view),
- browser_tree_node_count_(0) {
- }
-
- int browser_tree_node_count() { return browser_tree_node_count_; }
-
- struct TestBrowserTreeNode : public BrowserTreeNode {
- TestBrowserTreeNode(TestRendererAccessibilityComplete* owner)
- : owner_(owner) {
- owner_->browser_tree_node_count_++;
- }
-
- virtual ~TestBrowserTreeNode() {
- owner_->browser_tree_node_count_--;
- }
-
- private:
- TestRendererAccessibilityComplete* owner_;
- };
-
- virtual BrowserTreeNode* CreateBrowserTreeNode() OVERRIDE {
- return new TestBrowserTreeNode(this);
+ : RendererAccessibilityComplete(render_view) {
}
void SendPendingAccessibilityEvents() {
RendererAccessibilityComplete::SendPendingAccessibilityEvents();
}
-
-private:
- int browser_tree_node_count_;
};
class RendererAccessibilityTest : public RenderViewTest {
@@ -90,7 +66,7 @@ class RendererAccessibilityTest : public RenderViewTest {
int CountAccessibilityNodesSentToBrowser() {
AccessibilityHostMsg_EventParams event;
GetLastAccEvent(&event);
- return event.nodes.size();
+ return event.update.nodes.size();
}
protected:
@@ -133,15 +109,15 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusEvents) {
EXPECT_EQ(event.event_type,
ui::AX_EVENT_LAYOUT_COMPLETE);
EXPECT_EQ(event.id, 1);
- EXPECT_EQ(event.nodes.size(), 2U);
- EXPECT_EQ(event.nodes[0].id, 1);
- EXPECT_EQ(event.nodes[0].role,
+ EXPECT_EQ(event.update.nodes.size(), 2U);
+ EXPECT_EQ(event.update.nodes[0].id, 1);
+ EXPECT_EQ(event.update.nodes[0].role,
ui::AX_ROLE_ROOT_WEB_AREA);
- EXPECT_EQ(event.nodes[0].state,
+ EXPECT_EQ(event.update.nodes[0].state,
(1U << ui::AX_STATE_READ_ONLY) |
(1U << ui::AX_STATE_FOCUSABLE) |
(1U << ui::AX_STATE_FOCUSED));
- EXPECT_EQ(event.nodes[0].child_ids.size(), 1U);
+ EXPECT_EQ(event.update.nodes[0].child_ids.size(), 1U);
}
// Now focus the input element, and check everything again.
@@ -154,17 +130,17 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusEvents) {
EXPECT_EQ(event.event_type,
ui::AX_EVENT_FOCUS);
EXPECT_EQ(event.id, 3);
- EXPECT_EQ(event.nodes[0].id, 1);
- EXPECT_EQ(event.nodes[0].role,
+ EXPECT_EQ(event.update.nodes[0].id, 1);
+ EXPECT_EQ(event.update.nodes[0].role,
ui::AX_ROLE_ROOT_WEB_AREA);
- EXPECT_EQ(event.nodes[0].state,
+ EXPECT_EQ(event.update.nodes[0].state,
(1U << ui::AX_STATE_READ_ONLY) |
(1U << ui::AX_STATE_FOCUSABLE));
- EXPECT_EQ(event.nodes[0].child_ids.size(), 1U);
- EXPECT_EQ(event.nodes[1].id, 3);
- EXPECT_EQ(event.nodes[1].role,
+ EXPECT_EQ(event.update.nodes[0].child_ids.size(), 1U);
+ EXPECT_EQ(event.update.nodes[1].id, 3);
+ EXPECT_EQ(event.update.nodes[1].role,
ui::AX_ROLE_GROUP);
- EXPECT_EQ(event.nodes[1].state,
+ EXPECT_EQ(event.update.nodes[1].state,
(1U << ui::AX_STATE_FOCUSABLE) |
(1U << ui::AX_STATE_FOCUSED));
}
@@ -177,7 +153,7 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusEvents) {
AccessibilityHostMsg_EventParams event;
GetLastAccEvent(&event);
EXPECT_EQ(event.id, 4);
- EXPECT_EQ(event.nodes[1].state,
+ EXPECT_EQ(event.update.nodes[1].state,
(1U << ui::AX_STATE_FOCUSABLE) |
(1U << ui::AX_STATE_FOCUSED));
}
@@ -189,7 +165,7 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusEvents) {
AccessibilityHostMsg_EventParams event;
GetLastAccEvent(&event);
EXPECT_EQ(event.id, 5);
- EXPECT_EQ(event.nodes[1].state,
+ EXPECT_EQ(event.update.nodes[1].state,
(1U << ui::AX_STATE_FOCUSABLE) |
(1U << ui::AX_STATE_FOCUSED));
}
@@ -201,7 +177,7 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusEvents) {
AccessibilityHostMsg_EventParams event;
GetLastAccEvent(&event);
EXPECT_EQ(event.id, 6);
- EXPECT_EQ(event.nodes[1].state,
+ EXPECT_EQ(event.update.nodes[1].state,
(1U << ui::AX_STATE_FOCUSABLE) |
(1U << ui::AX_STATE_FOCUSED));
}
@@ -214,7 +190,7 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusEvents) {
AccessibilityHostMsg_EventParams event;
GetLastAccEvent(&event);
EXPECT_EQ(event.id, 7);
- EXPECT_EQ(event.nodes[1].state,
+ EXPECT_EQ(event.update.nodes[1].state,
(1U << ui::AX_STATE_FOCUSABLE) |
(1U << ui::AX_STATE_FOCUSED) |
(1U << ui::AX_STATE_READ_ONLY));
@@ -227,7 +203,7 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusEvents) {
AccessibilityHostMsg_EventParams event;
GetLastAccEvent(&event);
EXPECT_EQ(event.id, 8);
- EXPECT_EQ(event.nodes[1].state,
+ EXPECT_EQ(event.update.nodes[1].state,
(1U << ui::AX_STATE_FOCUSABLE) |
(1U << ui::AX_STATE_FOCUSED) |
(1U << ui::AX_STATE_READ_ONLY));
@@ -265,7 +241,6 @@ TEST_F(RendererAccessibilityTest, SendFullAccessibilityTreeOnReload) {
scoped_ptr<TestRendererAccessibilityComplete> accessibility(
new TestRendererAccessibilityComplete(view()));
accessibility->SendPendingAccessibilityEvents();
- EXPECT_EQ(4, accessibility->browser_tree_node_count());
EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
// If we post another event but the tree doesn't change,
@@ -277,13 +252,12 @@ TEST_F(RendererAccessibilityTest, SendFullAccessibilityTreeOnReload) {
root_obj,
ui::AX_EVENT_LAYOUT_COMPLETE);
accessibility->SendPendingAccessibilityEvents();
- EXPECT_EQ(4, accessibility->browser_tree_node_count());
EXPECT_EQ(1, CountAccessibilityNodesSentToBrowser());
{
// Make sure it's the root object that was updated.
AccessibilityHostMsg_EventParams event;
GetLastAccEvent(&event);
- EXPECT_EQ(root_obj.axID(), event.nodes[0].id);
+ EXPECT_EQ(root_obj.axID(), event.update.nodes[0].id);
}
// If we reload the page and send a event, we should send
@@ -297,7 +271,6 @@ TEST_F(RendererAccessibilityTest, SendFullAccessibilityTreeOnReload) {
root_obj,
ui::AX_EVENT_LAYOUT_COMPLETE);
accessibility->SendPendingAccessibilityEvents();
- EXPECT_EQ(4, accessibility->browser_tree_node_count());
EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
// Even if the first event is sent on an element other than
@@ -312,7 +285,6 @@ TEST_F(RendererAccessibilityTest, SendFullAccessibilityTreeOnReload) {
first_child,
ui::AX_EVENT_LIVE_REGION_CHANGED);
accessibility->SendPendingAccessibilityEvents();
- EXPECT_EQ(4, accessibility->browser_tree_node_count());
EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
}
@@ -338,7 +310,6 @@ TEST_F(RendererAccessibilityTest,
scoped_ptr<TestRendererAccessibilityComplete> accessibility(
new TestRendererAccessibilityComplete(view()));
accessibility->SendPendingAccessibilityEvents();
- EXPECT_EQ(5, accessibility->browser_tree_node_count());
EXPECT_EQ(5, CountAccessibilityNodesSentToBrowser());
// Post a "value changed" event, but then swap out
@@ -392,7 +363,6 @@ TEST_F(RendererAccessibilityTest, HideAccessibilityObject) {
scoped_ptr<TestRendererAccessibilityComplete> accessibility(
new TestRendererAccessibilityComplete(view()));
accessibility->SendPendingAccessibilityEvents();
- EXPECT_EQ(4, accessibility->browser_tree_node_count());
EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
WebDocument document = view()->GetWebView()->mainFrame()->document();
@@ -414,18 +384,16 @@ TEST_F(RendererAccessibilityTest, HideAccessibilityObject) {
ui::AX_EVENT_CHILDREN_CHANGED);
accessibility->SendPendingAccessibilityEvents();
- EXPECT_EQ(3, accessibility->browser_tree_node_count());
AccessibilityHostMsg_EventParams event;
GetLastAccEvent(&event);
- ASSERT_EQ(3U, event.nodes.size());
+ ASSERT_EQ(2U, event.update.nodes.size());
// RendererAccessibilityComplete notices that 'C' is being reparented,
- // so it updates 'B' first to remove 'C' as a child, then 'A' to add it,
- // and finally it updates 'C'.
- EXPECT_EQ(node_b.axID(), event.nodes[0].id);
- EXPECT_EQ(node_a.axID(), event.nodes[1].id);
- EXPECT_EQ(node_c.axID(), event.nodes[2].id);
- EXPECT_EQ(3, CountAccessibilityNodesSentToBrowser());
+ // so it clears the subtree rooted at 'A', then updates 'A' and then 'C'.
+ EXPECT_EQ(node_a.axID(), event.update.node_id_to_clear);
+ EXPECT_EQ(node_a.axID(), event.update.nodes[0].id);
+ EXPECT_EQ(node_c.axID(), event.update.nodes[1].id);
+ EXPECT_EQ(2, CountAccessibilityNodesSentToBrowser());
}
TEST_F(RendererAccessibilityTest, ShowAccessibilityObject) {
@@ -447,7 +415,6 @@ TEST_F(RendererAccessibilityTest, ShowAccessibilityObject) {
scoped_ptr<TestRendererAccessibilityComplete> accessibility(
new TestRendererAccessibilityComplete(view()));
accessibility->SendPendingAccessibilityEvents();
- EXPECT_EQ(3, accessibility->browser_tree_node_count());
EXPECT_EQ(3, CountAccessibilityNodesSentToBrowser());
// Show node 'B', then send a childrenChanged on 'A'.
@@ -459,15 +426,22 @@ TEST_F(RendererAccessibilityTest, ShowAccessibilityObject) {
WebDocument document = view()->GetWebView()->mainFrame()->document();
WebAXObject root_obj = document.accessibilityObject();
WebAXObject node_a = root_obj.childAt(0);
+ WebAXObject node_b = node_a.childAt(0);
+ WebAXObject node_c = node_b.childAt(0);
+
accessibility->HandleAXEvent(
node_a,
ui::AX_EVENT_CHILDREN_CHANGED);
accessibility->SendPendingAccessibilityEvents();
- EXPECT_EQ(4, accessibility->browser_tree_node_count());
AccessibilityHostMsg_EventParams event;
GetLastAccEvent(&event);
- ASSERT_EQ(3U, event.nodes.size());
+
+ ASSERT_EQ(3U, event.update.nodes.size());
+ EXPECT_EQ(node_a.axID(), event.update.node_id_to_clear);
+ EXPECT_EQ(node_a.axID(), event.update.nodes[0].id);
+ EXPECT_EQ(node_b.axID(), event.update.nodes[1].id);
+ EXPECT_EQ(node_c.axID(), event.update.nodes[2].id);
EXPECT_EQ(3, CountAccessibilityNodesSentToBrowser());
}
@@ -485,7 +459,6 @@ TEST_F(RendererAccessibilityTest, DetachAccessibilityObject) {
scoped_ptr<TestRendererAccessibilityComplete> accessibility(
new TestRendererAccessibilityComplete(view()));
accessibility->SendPendingAccessibilityEvents();
- EXPECT_EQ(7, accessibility->browser_tree_node_count());
EXPECT_EQ(7, CountAccessibilityNodesSentToBrowser());
// Initially, the accessibility tree looks like this:
@@ -532,14 +505,13 @@ TEST_F(RendererAccessibilityTest, DetachAccessibilityObject) {
// accessibility tree and that only three nodes needed
// to be updated (the body, the static text 1, and
// the static text 2).
- EXPECT_EQ(6, accessibility->browser_tree_node_count());
AccessibilityHostMsg_EventParams event;
GetLastAccEvent(&event);
- ASSERT_EQ(5U, event.nodes.size());
+ ASSERT_EQ(5U, event.update.nodes.size());
- EXPECT_EQ(body.axID(), event.nodes[0].id);
- EXPECT_EQ(text_1.axID(), event.nodes[1].id);
+ EXPECT_EQ(body.axID(), event.update.nodes[0].id);
+ EXPECT_EQ(text_1.axID(), event.update.nodes[1].id);
// The third event is to update text_2, but its id changes
// so we don't have a test expectation for it.
}
diff --git a/content/renderer/accessibility/renderer_accessibility_complete.cc b/content/renderer/accessibility/renderer_accessibility_complete.cc
index b4ea81e..cd17e24 100644
--- a/content/renderer/accessibility/renderer_accessibility_complete.cc
+++ b/content/renderer/accessibility/renderer_accessibility_complete.cc
@@ -8,7 +8,6 @@
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
-#include "content/renderer/accessibility/accessibility_node_serializer.h"
#include "content/renderer/accessibility/blink_ax_enum_conversion.h"
#include "content/renderer/render_view_impl.h"
#include "third_party/WebKit/public/web/WebAXObject.h"
@@ -34,7 +33,8 @@ RendererAccessibilityComplete::RendererAccessibilityComplete(
RenderViewImpl* render_view)
: RendererAccessibility(render_view),
weak_factory_(this),
- browser_root_(NULL),
+ tree_source_(render_view),
+ serializer_(&tree_source_),
last_scroll_offset_(gfx::Size()),
ack_pending_(false) {
WebAXObject::enableAccessibility();
@@ -57,12 +57,6 @@ RendererAccessibilityComplete::RendererAccessibilityComplete(
}
RendererAccessibilityComplete::~RendererAccessibilityComplete() {
- if (browser_root_) {
- ClearBrowserTreeNode(browser_root_);
- browser_id_map_.erase(browser_root_->id);
- delete browser_root_;
- }
- DCHECK(browser_id_map_.empty());
}
bool RendererAccessibilityComplete::OnMessageReceived(
@@ -102,14 +96,6 @@ void RendererAccessibilityComplete::DidFinishLoad(blink::WebFrame* frame) {
const WebDocument& document = GetMainDocument();
if (document.isNull())
return;
-
- // Check to see if the root accessibility object has changed, to work
- // around Blink bugs that cause AXObjectCache to be cleared
- // unnecessarily.
- // TODO(dmazzoni): remove this once rdar://5794454 is fixed.
- WebAXObject new_root = document.accessibilityObject();
- if (!browser_root_ || new_root.axID() != browser_root_->id)
- HandleAXEvent(new_root, ui::AX_EVENT_LAYOUT_COMPLETE);
}
@@ -169,10 +155,6 @@ RendererAccessibilityType RendererAccessibilityComplete::GetType() {
return RendererAccessibilityTypeComplete;
}
-RendererAccessibilityComplete::BrowserTreeNode::BrowserTreeNode() : id(0) {}
-
-RendererAccessibilityComplete::BrowserTreeNode::~BrowserTreeNode() {}
-
void RendererAccessibilityComplete::SendPendingAccessibilityEvents() {
const WebDocument& document = GetMainDocument();
if (document.isNull())
@@ -211,83 +193,22 @@ void RendererAccessibilityComplete::SendPendingAccessibilityEvents() {
// selection state, so make sure we re-send that whole subtree.
if (event.event_type ==
ui::AX_EVENT_SELECTED_CHILDREN_CHANGED) {
- base::hash_map<int32, BrowserTreeNode*>::iterator iter =
- browser_id_map_.find(obj.axID());
- if (iter != browser_id_map_.end())
- ClearBrowserTreeNode(iter->second);
- }
-
- // The browser may not have this object yet, for example if we get a
- // event on an object that was recently added, or if we get a
- // event on a node before the page has loaded. Work our way
- // up the parent chain until we find a node the browser has, or until
- // we reach the root.
- WebAXObject root_object = document.accessibilityObject();
- int root_id = root_object.axID();
- while (browser_id_map_.find(obj.axID()) == browser_id_map_.end() &&
- !obj.isDetached() &&
- obj.axID() != root_id) {
- obj = obj.parentObject();
- if (event.event_type ==
- ui::AX_EVENT_CHILDREN_CHANGED) {
- event.id = obj.axID();
- }
- }
-
- if (obj.isDetached()) {
-#ifndef NDEBUG
- LOG(WARNING) << "Got event on object that is invalid or has"
- << " invalid ancestor. Id: " << obj.axID();
-#endif
- continue;
- }
-
- // Another potential problem is that this event may be on an
- // object that is detached from the tree. Determine if this node is not a
- // child of its parent, and if so move the event to the parent.
- // TODO(dmazzoni): see if this can be removed after
- // https://bugs.webkit.org/show_bug.cgi?id=68466 is fixed.
- if (obj.axID() != root_id) {
- WebAXObject parent = obj.parentObject();
- while (!parent.isDetached() &&
- parent.accessibilityIsIgnored()) {
- parent = parent.parentObject();
- }
-
- if (parent.isDetached()) {
- NOTREACHED();
- continue;
- }
- bool is_child_of_parent = false;
- for (unsigned int i = 0; i < parent.childCount(); ++i) {
- if (parent.childAt(i).equals(obj)) {
- is_child_of_parent = true;
- break;
- }
- }
-
- if (!is_child_of_parent) {
- obj = parent;
- event.id = obj.axID();
- }
+ serializer_.DeleteClientSubtree(obj);
}
// Allow Blink to cache intermediate results since we're doing a bunch
// of read-only queries at once.
- root_object.startCachingComputedObjectAttributesUntilTreeMutates();
+ obj.startCachingComputedObjectAttributesUntilTreeMutates();
AccessibilityHostMsg_EventParams event_msg;
event_msg.event_type = event.event_type;
event_msg.id = event.id;
- std::set<int> ids_serialized;
- SerializeChangedNodes(obj, &event_msg.nodes, &ids_serialized);
+ serializer_.SerializeChanges(obj, &event_msg.update);
event_msgs.push_back(event_msg);
#ifndef NDEBUG
ui::AXTree tree;
- ui::AXTreeUpdate update;
- update.nodes = event_msg.nodes;
- tree.Unserialize(update);
+ tree.Unserialize(event_msg.update);
VLOG(0) << "Accessibility update: \n"
<< "routing id=" << routing_id()
<< " event="
@@ -302,200 +223,38 @@ void RendererAccessibilityComplete::SendPendingAccessibilityEvents() {
}
void RendererAccessibilityComplete::SendLocationChanges() {
- std::queue<WebAXObject> objs_to_explore;
- std::vector<BrowserTreeNode*> location_changes;
- WebAXObject root_object = GetMainDocument().accessibilityObject();
- objs_to_explore.push(root_object);
+ std::vector<AccessibilityHostMsg_LocationChangeParams> messages;
+ // Do a breadth-first explore of the whole blink AX tree.
+ base::hash_map<int, gfx::Rect> new_locations;
+ std::queue<WebAXObject> objs_to_explore;
+ objs_to_explore.push(tree_source_.GetRoot());
while (objs_to_explore.size()) {
WebAXObject obj = objs_to_explore.front();
objs_to_explore.pop();
- int id = obj.axID();
- if (browser_id_map_.find(id) != browser_id_map_.end()) {
- BrowserTreeNode* browser_node = browser_id_map_[id];
- gfx::Rect new_location = obj.boundingBoxRect();
- if (browser_node->location != new_location) {
- browser_node->location = new_location;
- location_changes.push_back(browser_node);
- }
- }
-
- for (unsigned i = 0; i < obj.childCount(); ++i)
- objs_to_explore.push(obj.childAt(i));
- }
-
- if (location_changes.size() == 0)
- return;
-
- std::vector<AccessibilityHostMsg_LocationChangeParams> messages;
- messages.resize(location_changes.size());
- for (size_t i = 0; i < location_changes.size(); i++) {
- messages[i].id = location_changes[i]->id;
- messages[i].new_location = location_changes[i]->location;
- }
- Send(new AccessibilityHostMsg_LocationChanges(routing_id(), messages));
-}
-
-RendererAccessibilityComplete::BrowserTreeNode*
-RendererAccessibilityComplete::CreateBrowserTreeNode() {
- return new RendererAccessibilityComplete::BrowserTreeNode();
-}
-
-void RendererAccessibilityComplete::SerializeChangedNodes(
- const blink::WebAXObject& obj,
- std::vector<ui::AXNodeData>* dst,
- std::set<int>* ids_serialized) {
- if (ids_serialized->find(obj.axID()) != ids_serialized->end())
- return;
- ids_serialized->insert(obj.axID());
-
- // This method has three responsibilities:
- // 1. Serialize |obj| into an ui::AXNodeData, and append it to
- // the end of the |dst| vector to be send to the browser process.
- // 2. Determine if |obj| has any new children that the browser doesn't
- // know about yet, and call SerializeChangedNodes recursively on those.
- // 3. Update our internal data structure that keeps track of what nodes
- // the browser knows about.
-
- // First, find the BrowserTreeNode for this id in our data structure where
- // we keep track of what accessibility objects the browser already knows
- // about. If we don't find it, then this must be the new root of the
- // accessibility tree.
- BrowserTreeNode* browser_node = NULL;
- base::hash_map<int32, BrowserTreeNode*>::iterator iter =
- browser_id_map_.find(obj.axID());
- if (iter != browser_id_map_.end()) {
- browser_node = iter->second;
- } else {
- if (browser_root_) {
- ClearBrowserTreeNode(browser_root_);
- browser_id_map_.erase(browser_root_->id);
- delete browser_root_;
- }
- browser_root_ = CreateBrowserTreeNode();
- browser_node = browser_root_;
- browser_node->id = obj.axID();
- browser_node->location = obj.boundingBoxRect();
- browser_node->parent = NULL;
- browser_id_map_[browser_node->id] = browser_node;
- }
-
- // Iterate over the ids of the children of |obj|.
- // Create a set of the child ids so we can quickly look
- // up which children are new and which ones were there before.
- // Also catch the case where a child is already in the browser tree
- // data structure with a different parent, and make sure the old parent
- // clears this node first.
- base::hash_set<int32> new_child_ids;
- const WebDocument& document = GetMainDocument();
- for (unsigned i = 0; i < obj.childCount(); i++) {
- WebAXObject child = obj.childAt(i);
- if (ShouldIncludeChildNode(obj, child)) {
- int new_child_id = child.axID();
- new_child_ids.insert(new_child_id);
-
- BrowserTreeNode* child = browser_id_map_[new_child_id];
- if (child && child->parent != browser_node) {
- // The child is being reparented. Find the Blink accessibility
- // object corresponding to the old parent, or the closest ancestor
- // still in the tree.
- BrowserTreeNode* parent = child->parent;
- WebAXObject parent_obj;
- while (parent) {
- parent_obj = document.accessibilityObjectFromID(parent->id);
-
- if (!parent_obj.isDetached())
- break;
- parent = parent->parent;
- }
- CHECK(parent);
- // Call SerializeChangedNodes recursively on the old parent,
- // so that the update that clears |child| from its old parent
- // occurs stricly before the update that adds |child| to its
- // new parent.
- SerializeChangedNodes(parent_obj, dst, ids_serialized);
- }
- }
- }
-
- // Go through the old children and delete subtrees for child
- // ids that are no longer present, and create a map from
- // id to BrowserTreeNode for the rest. It's important to delete
- // first in a separate pass so that nodes that are reparented
- // don't end up children of two different parents in the middle
- // of an update, which can lead to a double-free.
- base::hash_map<int32, BrowserTreeNode*> browser_child_id_map;
- std::vector<BrowserTreeNode*> old_children;
- old_children.swap(browser_node->children);
- for (size_t i = 0; i < old_children.size(); i++) {
- BrowserTreeNode* old_child = old_children[i];
- int old_child_id = old_child->id;
- if (new_child_ids.find(old_child_id) == new_child_ids.end()) {
- browser_id_map_.erase(old_child_id);
- ClearBrowserTreeNode(old_child);
- delete old_child;
- } else {
- browser_child_id_map[old_child_id] = old_child;
- }
- }
-
- // Serialize this node. This fills in all of the fields in
- // ui::AXNodeData except child_ids, which we handle below.
- dst->push_back(ui::AXNodeData());
- ui::AXNodeData* serialized_node = &dst->back();
- SerializeAccessibilityNode(obj, serialized_node);
- if (serialized_node->id == browser_root_->id)
- serialized_node->role = ui::AX_ROLE_ROOT_WEB_AREA;
-
- // Iterate over the children, make note of the ones that are new
- // and need to be serialized, and update the BrowserTreeNode
- // data structure to reflect the new tree.
- std::vector<WebAXObject> children_to_serialize;
- int child_count = obj.childCount();
- browser_node->children.reserve(child_count);
- for (int i = 0; i < child_count; i++) {
- WebAXObject child = obj.childAt(i);
- int child_id = child.axID();
-
- // Checks to make sure the child is valid, attached to this node,
- // and one we want to include in the tree.
- if (!ShouldIncludeChildNode(obj, child))
- continue;
- // No need to do anything more with children that aren't new;
- // the browser will reuse its existing object.
- if (new_child_ids.find(child_id) == new_child_ids.end())
+ // See if we had a previous location. If not, this whole subtree must
+ // be new, so don't continue to explore this branch.
+ int id = obj.axID();
+ base::hash_map<int, gfx::Rect>::iterator iter = locations_.find(id);
+ if (iter == locations_.end())
continue;
- new_child_ids.erase(child_id);
- serialized_node->child_ids.push_back(child_id);
- if (browser_child_id_map.find(child_id) != browser_child_id_map.end()) {
- BrowserTreeNode* reused_child = browser_child_id_map[child_id];
- browser_node->children.push_back(reused_child);
- } else {
- BrowserTreeNode* new_child = CreateBrowserTreeNode();
- new_child->id = child_id;
- new_child->location = obj.boundingBoxRect();
- new_child->parent = browser_node;
- browser_node->children.push_back(new_child);
- browser_id_map_[child_id] = new_child;
- children_to_serialize.push_back(child);
+ // If the location has changed, append it to the IPC message.
+ gfx::Rect new_location = obj.boundingBoxRect();
+ if (iter != locations_.end() && iter->second != new_location) {
+ AccessibilityHostMsg_LocationChangeParams message;
+ message.id = id;
+ message.new_location = new_location;
+ messages.push_back(message);
}
- }
-
- // Serialize all of the new children, recursively.
- for (size_t i = 0; i < children_to_serialize.size(); ++i)
- SerializeChangedNodes(children_to_serialize[i], dst, ids_serialized);
-}
-void RendererAccessibilityComplete::ClearBrowserTreeNode(
- BrowserTreeNode* browser_node) {
- for (size_t i = 0; i < browser_node->children.size(); ++i) {
- browser_id_map_.erase(browser_node->children[i]->id);
- ClearBrowserTreeNode(browser_node->children[i]);
- delete browser_node->children[i];
+ // Save the new location.
+ new_locations[id] = new_location;
}
- browser_node->children.clear();
+ locations_.swap(new_locations);
+
+ Send(new AccessibilityHostMsg_LocationChanges(routing_id(), messages));
}
void RendererAccessibilityComplete::OnDoDefaultAction(int acc_obj_id) {
diff --git a/content/renderer/accessibility/renderer_accessibility_complete.h b/content/renderer/accessibility/renderer_accessibility_complete.h
index 2511e3e..16eeeae 100644
--- a/content/renderer/accessibility/renderer_accessibility_complete.h
+++ b/content/renderer/accessibility/renderer_accessibility_complete.h
@@ -11,10 +11,12 @@
#include "base/containers/hash_tables.h"
#include "base/memory/weak_ptr.h"
#include "content/public/renderer/render_view_observer.h"
+#include "content/renderer/accessibility/blink_ax_tree_source.h"
#include "content/renderer/accessibility/renderer_accessibility.h"
#include "third_party/WebKit/public/web/WebAXEnums.h"
#include "third_party/WebKit/public/web/WebAXObject.h"
#include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_tree_serializer.h"
namespace blink {
class WebDocument;
@@ -50,20 +52,6 @@ class CONTENT_EXPORT RendererAccessibilityComplete
void HandleAXEvent(const blink::WebAXObject& obj, ui::AXEvent event);
- // In order to keep track of what nodes the browser knows about, we keep a
- // representation of the browser tree - just IDs and parent/child
- // relationships.
- struct CONTENT_EXPORT BrowserTreeNode {
- BrowserTreeNode();
- virtual ~BrowserTreeNode();
- int32 id;
- gfx::Rect location;
- BrowserTreeNode* parent;
- std::vector<BrowserTreeNode*> children;
- };
-
- virtual BrowserTreeNode* CreateBrowserTreeNode();
-
protected:
// Send queued events from the renderer to the browser.
void SendPendingAccessibilityEvents();
@@ -74,19 +62,6 @@ class CONTENT_EXPORT RendererAccessibilityComplete
void SendLocationChanges();
private:
- // Serialize the given accessibility object |obj| and append it to
- // |dst|, and then recursively also serialize any *new* children of
- // |obj|, based on what object ids we know the browser already has.
- // The set of ids serialized is added to |ids_serialized|, and any
- // ids previously in that set are not serialized again.
- void SerializeChangedNodes(const blink::WebAXObject& obj,
- std::vector<ui::AXNodeData>* dst,
- std::set<int>* ids_serialized);
-
- // Clear the given node and recursively delete all of its descendants
- // from the browser tree. (Does not delete |browser_node|).
- void ClearBrowserTreeNode(BrowserTreeNode* browser_node);
-
// Handlers for messages from the browser to the renderer.
void OnDoDefaultAction(int acc_obj_id);
void OnEventsAck();
@@ -107,13 +82,6 @@ class CONTENT_EXPORT RendererAccessibilityComplete
const blink::WebAXObject& src,
ui::AXNodeData* dst);
- // Build a tree of serializable ui::AXNodeData nodes to send to the
- // browser process, given a WebAXObject node from Blink.
- // Modifies |dst| in-place, it's assumed to be empty.
- void BuildAccessibilityTree(const blink::WebAXObject& src,
- bool include_children,
- ui::AXNodeData* dst);
-
// So we can queue up tasks to be executed later.
base::WeakPtrFactory<RendererAccessibilityComplete> weak_factory_;
@@ -121,11 +89,14 @@ class CONTENT_EXPORT RendererAccessibilityComplete
// sent to the browser.
std::vector<AccessibilityHostMsg_EventParams> pending_events_;
- // Our representation of the browser tree.
- BrowserTreeNode* browser_root_;
+ // The adapter that exposes Blink's accessibility tree to AXTreeSerializer.
+ BlinkAXTreeSource tree_source_;
+
+ // The serializer that sends accessibility messages to the browser process.
+ ui::AXTreeSerializer<blink::WebAXObject> serializer_;
- // A map from IDs to nodes in the browser tree.
- base::hash_map<int32, BrowserTreeNode*> browser_id_map_;
+ // Current location of every object, so we can detect when it moves.
+ base::hash_map<int, gfx::Rect> locations_;
// The most recently observed scroll offset of the root document element.
// TODO(dmazzoni): remove once https://bugs.webkit.org/show_bug.cgi?id=73460
diff --git a/content/renderer/accessibility/renderer_accessibility_focus_only.cc b/content/renderer/accessibility/renderer_accessibility_focus_only.cc
index 651b30b..cebba6f 100644
--- a/content/renderer/accessibility/renderer_accessibility_focus_only.cc
+++ b/content/renderer/accessibility/renderer_accessibility_focus_only.cc
@@ -97,9 +97,9 @@ void RendererAccessibilityFocusOnly::HandleFocusedNodeChanged(
// has focus, otherwise the focused node.
event.id = node_has_focus ? next_id_ : 1;
- event.nodes.resize(2);
- ui::AXNodeData& root = event.nodes[0];
- ui::AXNodeData& child = event.nodes[1];
+ event.update.nodes.resize(2);
+ ui::AXNodeData& root = event.update.nodes[0];
+ ui::AXNodeData& child = event.update.nodes[1];
// Always include the root of the tree, the document. It always has id 1.
root.id = 1;
diff --git a/ui/accessibility/ax_tree_serializer.h b/ui/accessibility/ax_tree_serializer.h
index c5f2c0d..9c735e0 100644
--- a/ui/accessibility/ax_tree_serializer.h
+++ b/ui/accessibility/ax_tree_serializer.h
@@ -172,10 +172,13 @@ AXTreeSerializer<AXSourceNode>::~AXTreeSerializer() {
template<typename AXSourceNode>
void AXTreeSerializer<AXSourceNode>::Reset() {
- if (client_root_) {
- DeleteClientSubtree(client_root_);
- client_root_ = NULL;
- }
+ if (!client_root_)
+ return;
+
+ DeleteClientSubtree(client_root_);
+ client_id_map_.erase(client_root_->id);
+ delete client_root_;
+ client_root_ = NULL;
}
template<typename AXSourceNode>
@@ -305,9 +308,7 @@ void AXTreeSerializer<AXSourceNode>::SerializeChanges(
// If there's no LCA, just tell the client to destroy the whole
// tree and then we'll serialize everything from the new root.
out_update->node_id_to_clear = client_root_->id;
- DeleteClientSubtree(client_root_);
- client_id_map_.erase(client_root_->id);
- client_root_ = NULL;
+ Reset();
} else if (need_delete) {
// Otherwise, if we need to reserialize a subtree, first we need
// to delete those nodes in our client tree so that
@@ -318,6 +319,7 @@ void AXTreeSerializer<AXSourceNode>::SerializeChanges(
for (size_t i = 0; i < client_lca->children.size(); ++i) {
client_id_map_.erase(client_lca->children[i]->id);
DeleteClientSubtree(client_lca->children[i]);
+ delete client_lca->children[i];
}
client_lca->children.clear();
}
@@ -342,6 +344,7 @@ void AXTreeSerializer<AXSourceNode>::DeleteClientSubtree(
for (size_t i = 0; i < client_node->children.size(); ++i) {
client_id_map_.erase(client_node->children[i]->id);
DeleteClientSubtree(client_node->children[i]);
+ delete client_node->children[i];
}
client_node->children.clear();
}
@@ -365,10 +368,7 @@ void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
int id = tree_->GetId(node);
ClientTreeNode* client_node = ClientTreeNodeById(id);
if (!client_node) {
- if (client_root_) {
- client_id_map_.erase(client_root_->id);
- DeleteClientSubtree(client_root_);
- }
+ Reset();
client_root_ = new ClientTreeNode();
client_node = client_root_;
client_node->id = id;
@@ -408,6 +408,7 @@ void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
if (new_child_ids.find(old_child_id) == new_child_ids.end()) {
client_id_map_.erase(old_child_id);
DeleteClientSubtree(old_child);
+ delete old_child;
} else {
client_child_id_map[old_child_id] = old_child;
}
diff --git a/ui/accessibility/ax_tree_source.h b/ui/accessibility/ax_tree_source.h
index 0c99c83..1f13d8f 100644
--- a/ui/accessibility/ax_tree_source.h
+++ b/ui/accessibility/ax_tree_source.h
@@ -20,7 +20,7 @@ namespace ui {
// an accessibility tree will be sent over an IPC before being
// consumed.
template<typename AXNodeSource>
-class AX_EXPORT AXTreeSource {
+class AXTreeSource {
public:
virtual ~AXTreeSource() {}