diff options
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 = ¶ms[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 = ¶ms[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 = ¶ms[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 = ¶ms[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() {} |