Chromium Code Reviews| 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..69adbfd14e5b6450e249a325ccfc30a8ea47ec6b 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) { |
|
David Tseng
2016/03/04 16:50:20
Would it make sense to have the AXTree maintain a
dmazzoni
2016/03/07 21:35:31
I debated that but I'm not convinced it would actu
|
| + 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,46 @@ void AutomationInternalCustomBindings::UpdateOverallTreeChangeObserverFilter() { |
| } |
| } |
| +ui::AXNode* AutomationInternalCustomBindings::GetParent(ui::AXNode* node, |
| + TreeCache** cache) { |
|
David Tseng
2016/03/04 16:50:20
out_cache
dmazzoni
2016/03/07 21:35:31
Made it in_out_cache
|
| + if (node->parent()) |
| + return node->parent(); |
| + |
| + int parent_tree_id = (*cache)->tree.data().parent_tree_id; |
| + if (parent_tree_id <= 0) |
|
David Tseng
2016/03/04 16:50:20
Doesn't the desktop tree have id 0?
dmazzoni
2016/03/07 21:35:32
Done.
|
| + return nullptr; |
| + |
| + TreeCache* parent_cache = GetTreeCacheFromTreeID(parent_tree_id); |
| + if (!parent_cache) |
|
David Tseng
2016/03/04 16:50:20
Should |cache| be set to nullptr?
dmazzoni
2016/03/07 21:35:32
No, because it's an input and output parameter
|
| + 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 == (*cache)->tree_id) { |
| + *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(), (*cache)->tree_id); |
| + if (parent) { |
|
David Tseng
2016/03/04 16:50:20
Is it an error if |parent| is null?
dmazzoni
2016/03/07 21:35:32
No, it's possible the child frame is loaded before
|
| + (*cache)->parent_node_id_from_parent_tree = parent->id(); |
| + *cache = parent_cache; |
| + return parent; |
| + } |
| + |
| + return nullptr; |
| +} |
| + |
| void AutomationInternalCustomBindings::RouteTreeIDFunction( |
| const std::string& name, |
| TreeIDFunction callback) { |
| @@ -953,7 +1018,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 { |