Index: chrome/renderer/extensions/automation_internal_custom_bindings.cc |
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.cc b/chrome/renderer/extensions/automation_internal_custom_bindings.cc |
index 44bfb908fa6448b1a7ffd871c9766aa22398b51d..8f1d31e6af52bf3002ea63a8b4e8ead4830b1b24 100644 |
--- a/chrome/renderer/extensions/automation_internal_custom_bindings.cc |
+++ b/chrome/renderer/extensions/automation_internal_custom_bindings.cc |
@@ -136,12 +136,26 @@ static gfx::Rect ComputeGlobalNodeBounds(TreeCache* cache, ui::AXNode* node) { |
} |
need_to_offset_web_area = true; |
} |
- parent = parent->parent(); |
+ parent = cache->owner->GetParent(parent, &cache); |
} |
return bounds; |
} |
+ui::AXNode* FindNodeWithChildTreeId(ui::AXNode* node, int child_tree_id) { |
+ if (child_tree_id == node->data().GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) |
+ return node; |
+ |
+ for (int i = 0; i < node->child_count(); ++i) { |
+ ui::AXNode* result = |
+ FindNodeWithChildTreeId(node->ChildAtIndex(i), child_tree_id); |
+ if (result) |
+ return result; |
+ } |
+ |
+ return nullptr; |
+} |
+ |
// |
// Helper class that helps implement bindings for a JavaScript function |
// that takes a single input argument consisting of a Tree ID. Looks up |
@@ -771,6 +785,8 @@ bool AutomationInternalCustomBindings::GetFocusInternal(TreeCache* cache, |
if (!focus) |
return false; |
+ // If the focused node is the owner of a child tree, that indicates |
+ // a node within the child tree is the one that actually has focus. |
while (focus->data().HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) { |
// Try to keep following focus recursively, by letting |tree_id| be the |
// new subtree to search in, while keeping |focus_tree_id| set to the tree |
@@ -782,6 +798,15 @@ bool AutomationInternalCustomBindings::GetFocusInternal(TreeCache* cache, |
if (!child_cache) |
break; |
+ // If the child cache is a frame tree that indicates a focused frame, |
+ // jump to that frame if possible. |
+ if (child_cache->tree.data().focused_tree_id > 0) { |
+ TreeCache* focused_cache = |
+ GetTreeCacheFromTreeID(child_cache->tree.data().focused_tree_id); |
+ if (focused_cache) |
+ child_cache = focused_cache; |
+ } |
+ |
int child_focus_id = child_cache->tree.data().focus_id; |
ui::AXNode* child_focus = child_cache->tree.GetFromId(child_focus_id); |
if (!child_focus) |
@@ -878,6 +903,47 @@ void AutomationInternalCustomBindings::UpdateOverallTreeChangeObserverFilter() { |
} |
} |
+ui::AXNode* AutomationInternalCustomBindings::GetParent( |
+ ui::AXNode* node, |
+ TreeCache** in_out_cache) { |
+ if (node->parent()) |
+ return node->parent(); |
+ |
+ int parent_tree_id = (*in_out_cache)->tree.data().parent_tree_id; |
+ if (parent_tree_id < 0) |
+ return nullptr; |
+ |
+ TreeCache* parent_cache = GetTreeCacheFromTreeID(parent_tree_id); |
+ if (!parent_cache) |
+ return nullptr; |
+ |
+ // Try to use the cached parent node from the most recent time this |
+ // was called. |
+ if (parent_cache->parent_node_id_from_parent_tree > 0) { |
+ ui::AXNode* parent = parent_cache->tree.GetFromId( |
+ parent_cache->parent_node_id_from_parent_tree); |
+ if (parent) { |
+ int parent_child_tree_id = |
+ parent->data().GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID); |
+ if (parent_child_tree_id == (*in_out_cache)->tree_id) { |
+ *in_out_cache = parent_cache; |
+ return parent; |
+ } |
+ } |
+ } |
+ |
+ // If that fails, search for it and cache it for next time. |
+ ui::AXNode* parent = FindNodeWithChildTreeId(parent_cache->tree.root(), |
+ (*in_out_cache)->tree_id); |
+ if (parent) { |
+ (*in_out_cache)->parent_node_id_from_parent_tree = parent->id(); |
+ *in_out_cache = parent_cache; |
+ return parent; |
+ } |
+ |
+ return nullptr; |
+} |
+ |
void AutomationInternalCustomBindings::RouteTreeIDFunction( |
const std::string& name, |
TreeIDFunction callback) { |
@@ -953,7 +1019,9 @@ void AutomationInternalCustomBindings::OnAccessibilityEvent( |
cache = new TreeCache(); |
cache->tab_id = -1; |
cache->tree_id = params.tree_id; |
+ cache->parent_node_id_from_parent_tree = -1; |
cache->tree.SetDelegate(this); |
+ cache->owner = this; |
tree_id_to_tree_cache_map_.insert(std::make_pair(tree_id, cache)); |
axtree_to_tree_cache_map_.insert(std::make_pair(&cache->tree, cache)); |
} else { |