diff options
Diffstat (limited to 'content/renderer/accessibility/renderer_accessibility_complete.cc')
-rw-r--r-- | content/renderer/accessibility/renderer_accessibility_complete.cc | 149 |
1 files changed, 113 insertions, 36 deletions
diff --git a/content/renderer/accessibility/renderer_accessibility_complete.cc b/content/renderer/accessibility/renderer_accessibility_complete.cc index 80923d1..6ece9dc 100644 --- a/content/renderer/accessibility/renderer_accessibility_complete.cc +++ b/content/renderer/accessibility/renderer_accessibility_complete.cc @@ -85,6 +85,9 @@ bool WebAccessibilityNotificationToAccessibilityNotification( case WebKit::WebAccessibilityNotificationSelectedTextChanged: *type = AccessibilityNotificationSelectedTextChanged; break; + case WebKit::WebAccessibilityNotificationTextChanged: + *type = AccessibilityNotificationTextChanged; + break; case WebKit::WebAccessibilityNotificationValueChanged: *type = AccessibilityNotificationValueChanged; break; @@ -134,6 +137,7 @@ bool RendererAccessibilityComplete::OnMessageReceived( OnScrollToPoint) IPC_MESSAGE_HANDLER(AccessibilityMsg_SetTextSelection, OnSetTextSelection) + IPC_MESSAGE_HANDLER(AccessibilityMsg_FatalError, OnFatalError) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -261,11 +265,6 @@ void RendererAccessibilityComplete::SendPendingAccessibilityNotifications() { AccessibilityHostMsg_NotificationParams& notification = src_notifications[i]; - // TODO(dtseng): Come up with a cleaner way of deciding to include children. - WebAccessibilityObject rootObject = document.accessibilityObject(); - int root_id = rootObject.axID(); - bool includes_children = ShouldIncludeChildren(notification) || - root_id == notification.id; WebAccessibilityObject obj = document.accessibilityObjectFromID( notification.id); if (!obj.updateBackingStoreAndCheckValidity()) @@ -276,11 +275,12 @@ void RendererAccessibilityComplete::SendPendingAccessibilityNotifications() { // notification 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. + WebAccessibilityObject 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(); - includes_children = true; if (notification.notification_type == AccessibilityNotificationChildrenChanged) { notification.id = obj.axID(); @@ -323,39 +323,28 @@ void RendererAccessibilityComplete::SendPendingAccessibilityNotifications() { if (!is_child_of_parent) { obj = parent; notification.id = obj.axID(); - includes_children = true; } } // Allow WebKit to cache intermediate results since we're doing a bunch // of read-only queries at once. - rootObject.startCachingComputedObjectAttributesUntilTreeMutates(); + root_object.startCachingComputedObjectAttributesUntilTreeMutates(); AccessibilityHostMsg_NotificationParams notification_msg; notification_msg.notification_type = notification.notification_type; notification_msg.id = notification.id; - notification_msg.includes_children = includes_children; - SerializeAccessibilityNode(obj, - ¬ification_msg.acc_tree, - includes_children); - if (obj.axID() == root_id) { - DCHECK_EQ(notification_msg.acc_tree.role, - AccessibilityNodeData::ROLE_WEB_AREA); - notification_msg.acc_tree.role = - AccessibilityNodeData::ROLE_ROOT_WEB_AREA; - } + SerializeChangedNodes(obj, ¬ification_msg.nodes); notification_msgs.push_back(notification_msg); - if (includes_children) - UpdateBrowserTree(notification_msg.acc_tree); - #ifndef NDEBUG if (logging_) { + AccessibilityNodeDataTreeNode tree; + MakeAccessibilityNodeDataTree(notification_msg.nodes, &tree); LOG(INFO) << "Accessibility update: \n" << "routing id=" << routing_id() << " notification=" << AccessibilityNotificationToString(notification.notification_type) - << "\n" << notification_msg.acc_tree.DebugString(true); + << "\n" << tree.DebugString(true); } #endif } @@ -363,34 +352,118 @@ void RendererAccessibilityComplete::SendPendingAccessibilityNotifications() { Send(new AccessibilityHostMsg_Notifications(routing_id(), notification_msgs)); } -void RendererAccessibilityComplete::UpdateBrowserTree( - const AccessibilityNodeData& renderer_node) { +RendererAccessibilityComplete::BrowserTreeNode* +RendererAccessibilityComplete::CreateBrowserTreeNode() { + return new RendererAccessibilityComplete::BrowserTreeNode(); +} + +void RendererAccessibilityComplete::SerializeChangedNodes( + const WebKit::WebAccessibilityObject& obj, + std::vector<AccessibilityNodeData>* dst) { + // This method has three responsibilities: + // 1. Serialize |obj| into an AccessibilityNodeData, 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(renderer_node.id); + browser_id_map_.find(obj.axID()); if (iter != browser_id_map_.end()) { browser_node = iter->second; - ClearBrowserTreeNode(browser_node); } else { - DCHECK_EQ(renderer_node.role, AccessibilityNodeData::ROLE_ROOT_WEB_AREA); if (browser_root_) { ClearBrowserTreeNode(browser_root_); browser_id_map_.erase(browser_root_->id); delete browser_root_; } - browser_root_ = new BrowserTreeNode; + browser_root_ = CreateBrowserTreeNode(); browser_node = browser_root_; - browser_node->id = renderer_node.id; + browser_node->id = obj.axID(); browser_id_map_[browser_node->id] = browser_node; } - browser_node->children.reserve(renderer_node.children.size()); - for (size_t i = 0; i < renderer_node.children.size(); ++i) { - BrowserTreeNode* browser_child_node = new BrowserTreeNode; - browser_child_node->id = renderer_node.children[i].id; - browser_id_map_[browser_child_node->id] = browser_child_node; - browser_node->children.push_back(browser_child_node); - UpdateBrowserTree(renderer_node.children[i]); + + // Serialize this node. This fills in all of the fields in + // AccessibilityNodeData except child_ids, which we handle below. + dst->push_back(AccessibilityNodeData()); + AccessibilityNodeData* serialized_node = &dst->back(); + SerializeAccessibilityNode(obj, serialized_node); + if (serialized_node->id == browser_root_->id) + serialized_node->role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + + // Create set of the ids of the children of |obj| so we can quickly look + // up which children are new and which ones were there before. + base::hash_set<int32> new_child_ids; + for (unsigned i = 0; i < obj.childCount(); i++) { + WebAccessibilityObject child = obj.childAt(i); + if (ShouldIncludeChildNode(obj, child)) { + new_child_ids.insert(child.axID()); + } + } + + // 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; + } } + + // 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<WebAccessibilityObject> children_to_serialize; + int child_count = obj.childCount(); + browser_node->children.reserve(child_count); + for (int i = 0; i < child_count; i++) { + WebAccessibilityObject 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()) + 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()) { + browser_node->children.push_back(browser_child_id_map[child_id]); + } else { + BrowserTreeNode* new_child = CreateBrowserTreeNode(); + new_child->id = child_id; + browser_node->children.push_back(new_child); + browser_id_map_[child_id] = new_child; + children_to_serialize.push_back(child); + } + } + + // Serialize all of the new children, recursively. + for (size_t i = 0; i < children_to_serialize.size(); ++i) + SerializeChangedNodes(children_to_serialize[i], dst); } void RendererAccessibilityComplete::ClearBrowserTreeNode( @@ -540,6 +613,10 @@ void RendererAccessibilityComplete::OnSetFocus(int acc_obj_id) { obj.setFocused(true); } +void RendererAccessibilityComplete::OnFatalError() { + CHECK(false); +} + bool RendererAccessibilityComplete::ShouldIncludeChildren( const AccessibilityHostMsg_NotificationParams& notification) { AccessibilityNotification type = notification.notification_type; |