Commit 156c8830 authored by Randy Rossi's avatar Randy Rossi Committed by Commit Bot

Update chromecast automation code

Updates automation code to keep up to date with recent
refactoring in chrome tree.

Bug: None
Test: Manual
Change-Id: I56d77ce1a891b155d9374aed9b9690c2782159df
Reviewed-on: https://chromium-review.googlesource.com/c/1315702Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Reviewed-by: default avatarLuke Halliwell <halliwell@chromium.org>
Commit-Queue: Randy Rossi <rmrossi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606106}
parent 26f90256
...@@ -374,8 +374,7 @@ class NodeIDPlusStringBoolWrapper ...@@ -374,8 +374,7 @@ class NodeIDPlusStringBoolWrapper
class AutomationMessageFilter : public IPC::MessageFilter { class AutomationMessageFilter : public IPC::MessageFilter {
public: public:
explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner) explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner)
: owner_(owner), : owner_(owner), removed_(false) {
removed_(false) {
DCHECK(owner); DCHECK(owner);
content::RenderThread::Get()->AddFilter(this); content::RenderThread::Get()->AddFilter(this);
task_runner_ = base::ThreadTaskRunnerHandle::Get(); task_runner_ = base::ThreadTaskRunnerHandle::Get();
...@@ -390,28 +389,23 @@ class AutomationMessageFilter : public IPC::MessageFilter { ...@@ -390,28 +389,23 @@ class AutomationMessageFilter : public IPC::MessageFilter {
bool OnMessageReceived(const IPC::Message& message) override { bool OnMessageReceived(const IPC::Message& message) override {
task_runner_->PostTask( task_runner_->PostTask(
FROM_HERE, FROM_HERE,
base::Bind( base::Bind(&AutomationMessageFilter::OnMessageReceivedOnRenderThread,
&AutomationMessageFilter::OnMessageReceivedOnRenderThread, this, message));
this, message));
// Always return false in case there are multiple // Always return false in case there are multiple
// AutomationInternalCustomBindings instances attached to the same thread. // AutomationInternalCustomBindings instances attached to the same thread.
return false; return false;
} }
void OnFilterRemoved() override { void OnFilterRemoved() override { removed_ = true; }
removed_ = true;
}
private: private:
void OnMessageReceivedOnRenderThread(const IPC::Message& message) { void OnMessageReceivedOnRenderThread(const IPC::Message& message) {
if (owner_) if (owner_)
owner_->OnMessageReceived(message); owner_->OnMessageReceived(message);
} }
~AutomationMessageFilter() override { ~AutomationMessageFilter() override { Remove(); }
Remove();
}
void Remove() { void Remove() {
if (!removed_) { if (!removed_) {
...@@ -441,8 +435,8 @@ AutomationInternalCustomBindings::AutomationInternalCustomBindings( ...@@ -441,8 +435,8 @@ AutomationInternalCustomBindings::AutomationInternalCustomBindings(
if (context && context->extension()) { if (context && context->extension()) {
const GURL background_page_url = const GURL background_page_url =
extensions::BackgroundInfo::GetBackgroundURL(context->extension()); extensions::BackgroundInfo::GetBackgroundURL(context->extension());
should_ignore_context_ = background_page_url != "" && should_ignore_context_ =
background_page_url != context->url(); background_page_url != "" && background_page_url != context->url();
} }
} }
...@@ -466,7 +460,7 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -466,7 +460,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
ROUTE_FUNCTION(GetFocus); ROUTE_FUNCTION(GetFocus);
ROUTE_FUNCTION(GetHtmlAttributes); ROUTE_FUNCTION(GetHtmlAttributes);
ROUTE_FUNCTION(GetState); ROUTE_FUNCTION(GetState);
#undef ROUTE_FUNCTION #undef ROUTE_FUNCTION
// Bindings that take a Tree ID and return a property of the tree. // Bindings that take a Tree ID and return a property of the tree.
...@@ -1637,8 +1631,8 @@ bool AutomationInternalCustomBindings::SendTreeChangeEvent( ...@@ -1637,8 +1631,8 @@ bool AutomationInternalCustomBindings::SendTreeChangeEvent(
bool has_filter = false; bool has_filter = false;
if (tree_change_observer_overall_filter_ & if (tree_change_observer_overall_filter_ &
(1 << (1
api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES)) { << api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES)) {
if (node->data().HasStringAttribute( if (node->data().HasStringAttribute(
ax::mojom::StringAttribute::kContainerLiveStatus) || ax::mojom::StringAttribute::kContainerLiveStatus) ||
node->data().role == ax::mojom::Role::kAlert || node->data().role == ax::mojom::Role::kAlert ||
......
...@@ -178,6 +178,7 @@ ...@@ -178,6 +178,7 @@
imageMap, imageMap,
inlineTextBox, inlineTextBox,
inputTime, inputTime,
keyboard,
labelText, labelText,
layoutTable, layoutTable,
layoutTableCell, layoutTableCell,
...@@ -191,7 +192,6 @@ ...@@ -191,7 +192,6 @@
listBoxOption, listBoxOption,
listItem, listItem,
listMarker, listMarker,
locationBar,
log, log,
main, main,
mark, mark,
...@@ -356,9 +356,11 @@ ...@@ -356,9 +356,11 @@
uninitialized, uninitialized,
attribute, attribute,
attributeExplicitlyEmpty, attributeExplicitlyEmpty,
caption,
contents, contents,
placeholder, placeholder,
relatedElement, relatedElement,
title,
value value
}; };
...@@ -576,6 +578,9 @@ ...@@ -576,6 +578,9 @@
// applicable // applicable
AutomationNode? activeDescendant; AutomationNode? activeDescendant;
// Reverse relationship for active descendant.
AutomationNode[]? activeDescendantFor;
// The target of an in-page link. // The target of an in-page link.
AutomationNode? inPageLinkTarget; AutomationNode? inPageLinkTarget;
...@@ -604,10 +609,10 @@ ...@@ -604,10 +609,10 @@
AutomationNode[]? labelFor; AutomationNode[]? labelFor;
// The column header nodes for a table cell. // The column header nodes for a table cell.
AutomationNode? tableCellColumnHeaders; AutomationNode[]? tableCellColumnHeaders;
// The row header nodes for a table cell. // The row header nodes for a table cell.
AutomationNode? tableCellRowHeaders; AutomationNode[]? tableCellRowHeaders;
// An array of standard actions available on this node. // An array of standard actions available on this node.
ActionType[]? standardActions; ActionType[]? standardActions;
...@@ -652,6 +657,9 @@ ...@@ -652,6 +657,9 @@
long? scrollYMin; long? scrollYMin;
long? scrollYMax; long? scrollYMax;
// Indicates whether this node is scrollable.
boolean? scrollable;
// //
// Editable text field attributes. // Editable text field attributes.
// //
...@@ -667,9 +675,6 @@ ...@@ -667,9 +675,6 @@
// The input type, like email or number. // The input type, like email or number.
DOMString? textInputType; DOMString? textInputType;
// An array of indexes of the break between lines in editable text.
long[] lineBreaks;
// An array of indexes of the start position of each text marker. // An array of indexes of the start position of each text marker.
long[] markerStarts; long[] markerStarts;
...@@ -855,6 +860,9 @@ ...@@ -855,6 +860,9 @@
// 'false' | 'true' | 'mixed' // 'false' | 'true' | 'mixed'
DOMString? checked; DOMString? checked;
// The inner html of this element. Only populated for math content.
DOMString? innerHtml;
// The RGBA foreground color of this subtree, as an integer. // The RGBA foreground color of this subtree, as an integer.
long? color; long? color;
......
...@@ -103,7 +103,7 @@ namespace automationInternal { ...@@ -103,7 +103,7 @@ namespace automationInternal {
// Returns the accessibility tree id of the web contents who's accessibility // Returns the accessibility tree id of the web contents who's accessibility
// was enabled using enableTab(). // was enabled using enableTab().
callback EnableTabCallback = void(long tree_id); callback EnableTabCallback = void(DOMString tree_id);
// Callback called when enableDesktop() returns. // Callback called when enableDesktop() returns.
callback EnableDesktopCallback = void(); callback EnableDesktopCallback = void();
...@@ -119,7 +119,7 @@ namespace automationInternal { ...@@ -119,7 +119,7 @@ namespace automationInternal {
EnableTabCallback callback); EnableTabCallback callback);
// Enable automation of the frame with the given tree id. // Enable automation of the frame with the given tree id.
static void enableFrame(DOMString treeId); static void enableFrame(DOMString tree_id);
// Enables desktop automation. // Enables desktop automation.
static void enableDesktop(EnableDesktopCallback callback); static void enableDesktop(EnableDesktopCallback callback);
...@@ -144,7 +144,7 @@ namespace automationInternal { ...@@ -144,7 +144,7 @@ namespace automationInternal {
long nodeID, long nodeID,
DOMString changeType); DOMString changeType);
static void onChildTreeID(DOMString treeID, long nodeID); static void onChildTreeID(DOMString treeID);
static void onNodesRemoved(DOMString treeID, long[] nodeIDs); static void onNodesRemoved(DOMString treeID, long[] nodeIDs);
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "base/no_destructor.h"
#include "chromecast/common/extensions_api/cast_extension_messages.h" #include "chromecast/common/extensions_api/cast_extension_messages.h"
#include "chromecast/renderer/extensions/automation_internal_custom_bindings.h" #include "chromecast/renderer/extensions/automation_internal_custom_bindings.h"
#include "ui/accessibility/ax_node.h" #include "ui/accessibility/ax_node.h"
...@@ -11,6 +12,12 @@ namespace cast { ...@@ -11,6 +12,12 @@ namespace cast {
namespace { namespace {
std::map<ui::AXTreeID, AutomationAXTreeWrapper*>& GetChildTreeIDReverseMap() {
static base::NoDestructor<std::map<ui::AXTreeID, AutomationAXTreeWrapper*>>
child_tree_id_reverse_map;
return *child_tree_id_reverse_map;
}
// Convert from ax::mojom::Event to api::automation::EventType. // Convert from ax::mojom::Event to api::automation::EventType.
api::automation::EventType ToAutomationEvent(ax::mojom::Event event_type) { api::automation::EventType ToAutomationEvent(ax::mojom::Event event_type) {
switch (event_type) { switch (event_type) {
...@@ -193,7 +200,7 @@ api::automation::EventType ToAutomationEvent( ...@@ -193,7 +200,7 @@ api::automation::EventType ToAutomationEvent(
AutomationAXTreeWrapper::AutomationAXTreeWrapper( AutomationAXTreeWrapper::AutomationAXTreeWrapper(
ui::AXTreeID tree_id, ui::AXTreeID tree_id,
AutomationInternalCustomBindings* owner) AutomationInternalCustomBindings* owner)
: tree_id_(tree_id), host_node_id_(-1), owner_(owner) { : tree_id_(tree_id), owner_(owner) {
// We have to initialize AXEventGenerator here - we can't do it in the // We have to initialize AXEventGenerator here - we can't do it in the
// initializer list because AXTree hasn't been initialized yet at that point. // initializer list because AXTree hasn't been initialized yet at that point.
SetTree(&tree_); SetTree(&tree_);
...@@ -205,12 +212,32 @@ AutomationAXTreeWrapper::~AutomationAXTreeWrapper() { ...@@ -205,12 +212,32 @@ AutomationAXTreeWrapper::~AutomationAXTreeWrapper() {
tree_.SetDelegate(nullptr); tree_.SetDelegate(nullptr);
} }
// static
AutomationAXTreeWrapper* AutomationAXTreeWrapper::GetParentOfTreeId(
ui::AXTreeID tree_id) {
std::map<ui::AXTreeID, AutomationAXTreeWrapper*>& child_tree_id_reverse_map =
GetChildTreeIDReverseMap();
const auto& iter = child_tree_id_reverse_map.find(tree_id);
if (iter != child_tree_id_reverse_map.end())
return iter->second;
return nullptr;
}
bool AutomationAXTreeWrapper::OnAccessibilityEvents( bool AutomationAXTreeWrapper::OnAccessibilityEvents(
const ExtensionMsg_AccessibilityEventBundleParams& event_bundle, const ExtensionMsg_AccessibilityEventBundleParams& event_bundle,
bool is_active_profile) { bool is_active_profile) {
std::map<ui::AXTreeID, AutomationAXTreeWrapper*>& child_tree_id_reverse_map =
GetChildTreeIDReverseMap();
for (const ui::AXTreeID& tree_id : tree_.GetAllChildTreeIds()) {
DCHECK_EQ(child_tree_id_reverse_map[tree_id], this);
child_tree_id_reverse_map.erase(tree_id);
}
for (const auto& update : event_bundle.updates) { for (const auto& update : event_bundle.updates) {
set_event_from(update.event_from); set_event_from(update.event_from);
deleted_node_ids_.clear(); deleted_node_ids_.clear();
did_send_tree_change_during_unserialization_ = false;
if (!tree_.Unserialize(update)) if (!tree_.Unserialize(update))
return false; return false;
...@@ -218,7 +245,7 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents( ...@@ -218,7 +245,7 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents(
if (is_active_profile) { if (is_active_profile) {
owner_->SendNodesRemovedEvent(&tree_, deleted_node_ids_); owner_->SendNodesRemovedEvent(&tree_, deleted_node_ids_);
if (update.nodes.size()) { if (update.nodes.size() && did_send_tree_change_during_unserialization_) {
ui::AXNode* target = tree_.GetFromId(update.nodes[0].id); ui::AXNode* target = tree_.GetFromId(update.nodes[0].id);
if (target) { if (target) {
owner_->SendTreeChangeEvent( owner_->SendTreeChangeEvent(
...@@ -229,12 +256,32 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents( ...@@ -229,12 +256,32 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents(
} }
} }
for (const ui::AXTreeID& tree_id : tree_.GetAllChildTreeIds()) {
DCHECK(!base::ContainsKey(child_tree_id_reverse_map, tree_id));
child_tree_id_reverse_map.insert(std::make_pair(tree_id, this));
}
// Exit early if this isn't the active profile. // Exit early if this isn't the active profile.
if (!is_active_profile) if (!is_active_profile)
return true; return true;
// Send all blur and focus events first. This ensures we correctly dispatch
// these events, which gets re-targetted in the js bindings and ensures it
// receives the correct value for |event_from|.
for (const auto& event : event_bundle.events) {
if (event.event_type != ax::mojom::Event::kFocus &&
event.event_type != ax::mojom::Event::kBlur)
continue;
api::automation::EventType automation_event_type =
ToAutomationEvent(event.event_type);
owner_->SendAutomationEvent(event_bundle.tree_id,
event_bundle.mouse_location, event,
automation_event_type);
}
// Send auto-generated AXEventGenerator events. // Send auto-generated AXEventGenerator events.
for (auto targeted_event : *this) { for (const auto& targeted_event : *this) {
api::automation::EventType event_type = api::automation::EventType event_type =
ToAutomationEvent(targeted_event.event_params.event); ToAutomationEvent(targeted_event.event_params.event);
if (IsEventTypeHandledByAXEventGenerator(event_type)) { if (IsEventTypeHandledByAXEventGenerator(event_type)) {
...@@ -248,7 +295,11 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents( ...@@ -248,7 +295,11 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents(
} }
ClearEvents(); ClearEvents();
for (auto event : event_bundle.events) { for (const auto& event : event_bundle.events) {
if (event.event_type == ax::mojom::Event::kFocus ||
event.event_type == ax::mojom::Event::kBlur)
continue;
api::automation::EventType automation_event_type = api::automation::EventType automation_event_type =
ToAutomationEvent(event.event_type); ToAutomationEvent(event.event_type);
...@@ -277,8 +328,8 @@ void AutomationAXTreeWrapper::OnNodeDataWillChange( ...@@ -277,8 +328,8 @@ void AutomationAXTreeWrapper::OnNodeDataWillChange(
void AutomationAXTreeWrapper::OnNodeWillBeDeleted(ui::AXTree* tree, void AutomationAXTreeWrapper::OnNodeWillBeDeleted(ui::AXTree* tree,
ui::AXNode* node) { ui::AXNode* node) {
AXEventGenerator::OnNodeWillBeDeleted(tree, node); AXEventGenerator::OnNodeWillBeDeleted(tree, node);
owner_->SendTreeChangeEvent(api::automation::TREE_CHANGE_TYPE_NODEREMOVED, did_send_tree_change_during_unserialization_ |= owner_->SendTreeChangeEvent(
tree, node); api::automation::TREE_CHANGE_TYPE_NODEREMOVED, tree, node);
deleted_node_ids_.push_back(node->id()); deleted_node_ids_.push_back(node->id());
} }
...@@ -292,16 +343,19 @@ void AutomationAXTreeWrapper::OnAtomicUpdateFinished( ...@@ -292,16 +343,19 @@ void AutomationAXTreeWrapper::OnAtomicUpdateFinished(
ui::AXNode* node = change.node; ui::AXNode* node = change.node;
switch (change.type) { switch (change.type) {
case NODE_CREATED: case NODE_CREATED:
owner_->SendTreeChangeEvent( did_send_tree_change_during_unserialization_ |=
api::automation::TREE_CHANGE_TYPE_NODECREATED, tree, node); owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_NODECREATED, tree, node);
break; break;
case SUBTREE_CREATED: case SUBTREE_CREATED:
owner_->SendTreeChangeEvent( did_send_tree_change_during_unserialization_ |=
api::automation::TREE_CHANGE_TYPE_SUBTREECREATED, tree, node); owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_SUBTREECREATED, tree, node);
break; break;
case NODE_CHANGED: case NODE_CHANGED:
owner_->SendTreeChangeEvent( did_send_tree_change_during_unserialization_ |=
api::automation::TREE_CHANGE_TYPE_NODECHANGED, tree, node); owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_NODECHANGED, tree, node);
break; break;
// Unhandled. // Unhandled.
case NODE_REPARENTED: case NODE_REPARENTED:
...@@ -311,8 +365,9 @@ void AutomationAXTreeWrapper::OnAtomicUpdateFinished( ...@@ -311,8 +365,9 @@ void AutomationAXTreeWrapper::OnAtomicUpdateFinished(
} }
for (int id : text_changed_node_ids_) { for (int id : text_changed_node_ids_) {
owner_->SendTreeChangeEvent(api::automation::TREE_CHANGE_TYPE_TEXTCHANGED, did_send_tree_change_during_unserialization_ |= owner_->SendTreeChangeEvent(
tree, tree->GetFromId(id)); api::automation::TREE_CHANGE_TYPE_TEXTCHANGED, tree,
tree->GetFromId(id));
} }
text_changed_node_ids_.clear(); text_changed_node_ids_.clear();
} }
...@@ -324,6 +379,7 @@ bool AutomationAXTreeWrapper::IsEventTypeHandledByAXEventGenerator( ...@@ -324,6 +379,7 @@ bool AutomationAXTreeWrapper::IsEventTypeHandledByAXEventGenerator(
case api::automation::EVENT_TYPE_ACTIVEDESCENDANTCHANGED: case api::automation::EVENT_TYPE_ACTIVEDESCENDANTCHANGED:
case api::automation::EVENT_TYPE_ARIAATTRIBUTECHANGED: case api::automation::EVENT_TYPE_ARIAATTRIBUTECHANGED:
case api::automation::EVENT_TYPE_CHECKEDSTATECHANGED: case api::automation::EVENT_TYPE_CHECKEDSTATECHANGED:
case api::automation::EVENT_TYPE_CHILDRENCHANGED:
case api::automation::EVENT_TYPE_DOCUMENTSELECTIONCHANGED: case api::automation::EVENT_TYPE_DOCUMENTSELECTIONCHANGED:
case api::automation::EVENT_TYPE_DOCUMENTTITLECHANGED: case api::automation::EVENT_TYPE_DOCUMENTTITLECHANGED:
case api::automation::EVENT_TYPE_EXPANDEDCHANGED: case api::automation::EVENT_TYPE_EXPANDEDCHANGED:
...@@ -332,6 +388,9 @@ bool AutomationAXTreeWrapper::IsEventTypeHandledByAXEventGenerator( ...@@ -332,6 +388,9 @@ bool AutomationAXTreeWrapper::IsEventTypeHandledByAXEventGenerator(
case api::automation::EVENT_TYPE_LIVEREGIONCREATED: case api::automation::EVENT_TYPE_LIVEREGIONCREATED:
case api::automation::EVENT_TYPE_LOADCOMPLETE: case api::automation::EVENT_TYPE_LOADCOMPLETE:
case api::automation::EVENT_TYPE_LOADSTART: case api::automation::EVENT_TYPE_LOADSTART:
case api::automation::EVENT_TYPE_ROWCOLLAPSED:
case api::automation::EVENT_TYPE_ROWCOUNTCHANGED:
case api::automation::EVENT_TYPE_ROWEXPANDED:
case api::automation::EVENT_TYPE_SCROLLPOSITIONCHANGED: case api::automation::EVENT_TYPE_SCROLLPOSITIONCHANGED:
case api::automation::EVENT_TYPE_SELECTEDCHILDRENCHANGED: case api::automation::EVENT_TYPE_SELECTEDCHILDRENCHANGED:
return true; return true;
...@@ -365,24 +424,20 @@ bool AutomationAXTreeWrapper::IsEventTypeHandledByAXEventGenerator( ...@@ -365,24 +424,20 @@ bool AutomationAXTreeWrapper::IsEventTypeHandledByAXEventGenerator(
case api::automation::EVENT_TYPE_MOUSEMOVED: case api::automation::EVENT_TYPE_MOUSEMOVED:
case api::automation::EVENT_TYPE_MOUSEPRESSED: case api::automation::EVENT_TYPE_MOUSEPRESSED:
case api::automation::EVENT_TYPE_MOUSERELEASED: case api::automation::EVENT_TYPE_MOUSERELEASED:
case api::automation::EVENT_TYPE_SCROLLEDTOANCHOR:
case api::automation::EVENT_TYPE_WINDOWACTIVATED: case api::automation::EVENT_TYPE_WINDOWACTIVATED:
case api::automation::EVENT_TYPE_WINDOWDEACTIVATED: case api::automation::EVENT_TYPE_WINDOWDEACTIVATED:
case api::automation::EVENT_TYPE_SCROLLEDTOANCHOR:
return false; return false;
// These events might need to be migrated to AXEventGenerator. // These events might need to be migrated to AXEventGenerator.
case api::automation::EVENT_TYPE_ALERT: case api::automation::EVENT_TYPE_ALERT:
case api::automation::EVENT_TYPE_BLUR: case api::automation::EVENT_TYPE_BLUR:
case api::automation::EVENT_TYPE_CHILDRENCHANGED:
case api::automation::EVENT_TYPE_FOCUS: case api::automation::EVENT_TYPE_FOCUS:
case api::automation::EVENT_TYPE_IMAGEFRAMEUPDATED: case api::automation::EVENT_TYPE_IMAGEFRAMEUPDATED:
case api::automation::EVENT_TYPE_LOCATIONCHANGED: case api::automation::EVENT_TYPE_LOCATIONCHANGED:
case api::automation::EVENT_TYPE_MENUEND: case api::automation::EVENT_TYPE_MENUEND:
case api::automation::EVENT_TYPE_MENULISTITEMSELECTED: case api::automation::EVENT_TYPE_MENULISTITEMSELECTED:
case api::automation::EVENT_TYPE_MENUSTART: case api::automation::EVENT_TYPE_MENUSTART:
case api::automation::EVENT_TYPE_ROWCOLLAPSED:
case api::automation::EVENT_TYPE_ROWCOUNTCHANGED:
case api::automation::EVENT_TYPE_ROWEXPANDED:
case api::automation::EVENT_TYPE_SELECTION: case api::automation::EVENT_TYPE_SELECTION:
case api::automation::EVENT_TYPE_TEXTCHANGED: case api::automation::EVENT_TYPE_TEXTCHANGED:
case api::automation::EVENT_TYPE_TEXTSELECTIONCHANGED: case api::automation::EVENT_TYPE_TEXTSELECTIONCHANGED:
......
...@@ -23,16 +23,14 @@ class AutomationAXTreeWrapper : public ui::AXEventGenerator { ...@@ -23,16 +23,14 @@ class AutomationAXTreeWrapper : public ui::AXEventGenerator {
AutomationInternalCustomBindings* owner); AutomationInternalCustomBindings* owner);
~AutomationAXTreeWrapper() override; ~AutomationAXTreeWrapper() override;
// Returns the AutomationAXTreeWrapper that lists |tree_id| as one of its
// child trees, if any.
static AutomationAXTreeWrapper* GetParentOfTreeId(ui::AXTreeID tree_id);
ui::AXTreeID tree_id() const { return tree_id_; } ui::AXTreeID tree_id() const { return tree_id_; }
ui::AXTree* tree() { return &tree_; } ui::AXTree* tree() { return &tree_; }
AutomationInternalCustomBindings* owner() { return owner_; } AutomationInternalCustomBindings* owner() { return owner_; }
// The host node ID is the node ID of the parent node in the parent tree.
// For example, the host node ID of a web area of a child frame is the
// ID of the <iframe> element in its parent frame.
int32_t host_node_id() const { return host_node_id_; }
void set_host_node_id(int32_t id) { host_node_id_ = id; }
// Called by AutomationInternalCustomBindings::OnAccessibilityEvents on // Called by AutomationInternalCustomBindings::OnAccessibilityEvents on
// the AutomationAXTreeWrapper instance for the correct tree corresponding // the AutomationAXTreeWrapper instance for the correct tree corresponding
// to this event. Unserializes the tree update and calls back to // to this event. Unserializes the tree update and calls back to
...@@ -57,12 +55,15 @@ class AutomationAXTreeWrapper : public ui::AXEventGenerator { ...@@ -57,12 +55,15 @@ class AutomationAXTreeWrapper : public ui::AXEventGenerator {
bool IsEventTypeHandledByAXEventGenerator(api::automation::EventType) const; bool IsEventTypeHandledByAXEventGenerator(api::automation::EventType) const;
ui::AXTreeID tree_id_; ui::AXTreeID tree_id_;
int32_t host_node_id_;
ui::AXTree tree_; ui::AXTree tree_;
AutomationInternalCustomBindings* owner_; AutomationInternalCustomBindings* owner_;
std::vector<int> deleted_node_ids_; std::vector<int> deleted_node_ids_;
std::vector<int> text_changed_node_ids_; std::vector<int> text_changed_node_ids_;
// Tracks whether a tree change event was sent during unserialization. Tree
// changes outside of unserialization do not get reflected here. The value is
// reset after unserialization.
bool did_send_tree_change_during_unserialization_ = false;
DISALLOW_COPY_AND_ASSIGN(AutomationAXTreeWrapper); DISALLOW_COPY_AND_ASSIGN(AutomationAXTreeWrapper);
}; };
......
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include <memory> #include <memory>
#include "base/bind.h" #include "base/bind.h"
#include "base/i18n/string_search.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "base/values.h" #include "base/values.h"
#include "chromecast/common/extensions_api/cast_extension_messages.h" #include "chromecast/common/extensions_api/cast_extension_messages.h"
...@@ -30,6 +32,7 @@ ...@@ -30,6 +32,7 @@
#include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_event.h" #include "ui/accessibility/ax_event.h"
#include "ui/accessibility/ax_node.h" #include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/rect_conversions.h"
namespace extensions { namespace extensions {
...@@ -105,22 +108,6 @@ static gfx::Rect ComputeGlobalNodeBounds(AutomationAXTreeWrapper* tree_wrapper, ...@@ -105,22 +108,6 @@ static gfx::Rect ComputeGlobalNodeBounds(AutomationAXTreeWrapper* tree_wrapper,
return gfx::ToEnclosingRect(bounds); return gfx::ToEnclosingRect(bounds);
} }
ui::AXNode* FindNodeWithChildTreeId(ui::AXNode* node,
ui::AXTreeID child_tree_id) {
if (child_tree_id == ui::AXTreeID::FromString(node->data().GetStringAttribute(
ax::mojom::StringAttribute::kChildTreeId)))
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 // Helper class that helps implement bindings for a JavaScript function
// that takes a single input argument consisting of a Tree ID. Looks up // that takes a single input argument consisting of a Tree ID. Looks up
...@@ -172,11 +159,11 @@ class TreeIDWrapper : public base::RefCountedThreadSafe<TreeIDWrapper> { ...@@ -172,11 +159,11 @@ class TreeIDWrapper : public base::RefCountedThreadSafe<TreeIDWrapper> {
// AutomationAXTreeWrapper and the AXNode and passes them to the function passed // AutomationAXTreeWrapper and the AXNode and passes them to the function passed
// to the constructor. // to the constructor.
// //
typedef std::function<void(v8::Isolate* isolate,
typedef void (*NodeIDFunction)(v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
v8::ReturnValue<v8::Value> result, AutomationAXTreeWrapper* tree_wrapper,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node)>
ui::AXNode* node); NodeIDFunction;
class NodeIDWrapper : public base::RefCountedThreadSafe<NodeIDWrapper> { class NodeIDWrapper : public base::RefCountedThreadSafe<NodeIDWrapper> {
public: public:
...@@ -330,6 +317,59 @@ class NodeIDPlusRangeWrapper ...@@ -330,6 +317,59 @@ class NodeIDPlusRangeWrapper
NodeIDPlusRangeFunction function_; NodeIDPlusRangeFunction function_;
}; };
typedef std::function<void(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
ui::AXNode* node,
const std::string& strVal,
bool boolVal)>
NodeIDPlusStringBoolFunction;
class NodeIDPlusStringBoolWrapper
: public base::RefCountedThreadSafe<NodeIDPlusStringBoolWrapper> {
public:
NodeIDPlusStringBoolWrapper(
AutomationInternalCustomBindings* automation_bindings,
NodeIDPlusStringBoolFunction function)
: automation_bindings_(automation_bindings), function_(function) {}
void Run(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = automation_bindings_->GetIsolate();
if (args.Length() < 4 || !args[0]->IsString() || !args[1]->IsNumber() ||
!args[2]->IsString() || !args[3]->IsBoolean()) {
ThrowInvalidArgumentsException(automation_bindings_);
}
v8::Local<v8::Context> context =
automation_bindings_->context()->v8_context();
ui::AXTreeID tree_id =
ui::AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0]));
int node_id = args[1]->Int32Value(context).FromMaybe(0);
std::string str_val = *v8::String::Utf8Value(isolate, args[2]);
bool bool_val = args[3].As<v8::Boolean>()->Value();
AutomationAXTreeWrapper* tree_wrapper =
automation_bindings_->GetAutomationAXTreeWrapperFromTreeID(tree_id);
if (!tree_wrapper)
return;
ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id);
if (!node)
return;
function_(isolate, args.GetReturnValue(), tree_wrapper, node, str_val,
bool_val);
}
private:
virtual ~NodeIDPlusStringBoolWrapper() {}
friend class base::RefCountedThreadSafe<NodeIDPlusStringBoolWrapper>;
AutomationInternalCustomBindings* automation_bindings_;
NodeIDPlusStringBoolFunction function_;
};
} // namespace } // namespace
class AutomationMessageFilter : public IPC::MessageFilter { class AutomationMessageFilter : public IPC::MessageFilter {
...@@ -430,17 +470,21 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -430,17 +470,21 @@ void AutomationInternalCustomBindings::AddRoutes() {
AutomationAXTreeWrapper* tree_wrapper) { AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::Integer::New(isolate, tree_wrapper->tree()->root()->id())); result.Set(v8::Integer::New(isolate, tree_wrapper->tree()->root()->id()));
}); });
RouteTreeIDFunction( RouteTreeIDFunction("GetDocURL", [](v8::Isolate* isolate,
"GetDocURL", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) { AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::String::NewFromUtf8( result.Set(v8::String::NewFromUtf8(isolate,
isolate, tree_wrapper->tree()->data().url.c_str())); tree_wrapper->tree()->data().url.c_str(),
}); v8::NewStringType::kNormal)
.ToLocalChecked());
});
RouteTreeIDFunction( RouteTreeIDFunction(
"GetDocTitle", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, "GetDocTitle", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) { AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::String::NewFromUtf8( result.Set(v8::String::NewFromUtf8(
isolate, tree_wrapper->tree()->data().title.c_str())); isolate, tree_wrapper->tree()->data().title.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
}); });
RouteTreeIDFunction( RouteTreeIDFunction(
"GetDocLoaded", "GetDocLoaded",
...@@ -505,16 +549,27 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -505,16 +549,27 @@ void AutomationInternalCustomBindings::AddRoutes() {
RouteNodeIDFunction( RouteNodeIDFunction(
"GetParentID", "GetParentID",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, [this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
if (node->parent()) ui::AXNode* parent = GetParent(node, &tree_wrapper);
result.Set(v8::Integer::New(isolate, node->parent()->id())); if (parent) {
gin::DataObjectBuilder response(isolate);
response.Set("treeId", tree_wrapper->tree_id().ToString());
response.Set("nodeId", parent->id());
result.Set(response.Build());
}
}); });
RouteNodeIDFunction( RouteNodeIDFunction(
"GetChildCount", "GetChildCount",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, [this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
result.Set(v8::Integer::New(isolate, node->child_count())); int child_count;
if (GetRootOfChildTree(&node, &tree_wrapper))
child_count = 1;
else
child_count = node->child_count();
result.Set(v8::Integer::New(isolate, child_count));
}); });
RouteNodeIDFunction( RouteNodeIDFunction(
"GetIndexInParent", "GetIndexInParent",
...@@ -525,29 +580,10 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -525,29 +580,10 @@ void AutomationInternalCustomBindings::AddRoutes() {
RouteNodeIDFunction( RouteNodeIDFunction(
"GetRole", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, "GetRole", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
auto role = node->data().role; std::string role_name = ui::ToString(node->data().role);
auto mapped_role = role; result.Set(v8::String::NewFromUtf8(isolate, role_name.c_str(),
v8::NewStringType::kNormal)
// These roles are remapped for simplicity in handling them in AT. .ToLocalChecked());
switch (role) {
case ax::mojom::Role::kLayoutTable:
mapped_role = ax::mojom::Role::kTable;
break;
case ax::mojom::Role::kLayoutTableCell:
mapped_role = ax::mojom::Role::kCell;
break;
case ax::mojom::Role::kLayoutTableColumn:
mapped_role = ax::mojom::Role::kColumn;
break;
case ax::mojom::Role::kLayoutTableRow:
mapped_role = ax::mojom::Role::kRow;
break;
default:
break;
}
std::string role_name = ui::ToString(mapped_role);
result.Set(v8::String::NewFromUtf8(isolate, role_name.c_str()));
}); });
RouteNodeIDFunction( RouteNodeIDFunction(
"GetLocation", "GetLocation",
...@@ -581,18 +617,24 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -581,18 +617,24 @@ void AutomationInternalCustomBindings::AddRoutes() {
} }
result.Set(array_result); result.Set(array_result);
}); });
RouteNodeIDFunction("GetChildIDs", [](v8::Isolate* isolate, RouteNodeIDFunction(
v8::ReturnValue<v8::Value> result, "GetChildIDs",
AutomationAXTreeWrapper* tree_wrapper, [this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
ui::AXNode* node) { AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
const std::vector<ui::AXNode*>& children = node->children(); std::vector<int> child_ids;
v8::Local<v8::Array> array_result(v8::Array::New(isolate, children.size())); if (GetRootOfChildTree(&node, &tree_wrapper)) {
for (size_t i = 0; i < children.size(); ++i) { child_ids.push_back(node->id());
array_result->Set(static_cast<uint32_t>(i), } else {
v8::Integer::New(isolate, children[i]->id())); const std::vector<ui::AXNode*>& children = node->children();
} for (size_t i = 0; i < children.size(); ++i)
result.Set(array_result); child_ids.push_back(children[i]->id());
}); }
gin::DataObjectBuilder response(isolate);
response.Set("treeId", tree_wrapper->tree_id().ToString());
response.Set("nodeIds", child_ids);
result.Set(response.Build());
});
// Bindings that take a Tree ID and Node ID and string attribute name // Bindings that take a Tree ID and Node ID and string attribute name
// and return a property of the node. // and return a property of the node.
...@@ -646,10 +688,8 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -646,10 +688,8 @@ void AutomationInternalCustomBindings::AddRoutes() {
// Convert from local to global coordinates second, after subsetting, // Convert from local to global coordinates second, after subsetting,
// because the local to global conversion might involve matrix // because the local to global conversion might involve matrix
// transformations. // transformations.
// TODO(katie): Instead of removing clipping we could trim local_bounds
// to fit in the clipped space?
gfx::Rect global_bounds = ComputeGlobalNodeBounds( gfx::Rect global_bounds = ComputeGlobalNodeBounds(
tree_wrapper, node, local_bounds, nullptr, false /* clip_bounds */); tree_wrapper, node, local_bounds, nullptr, true /* clip_bounds */);
result.Set(RectToV8Object(isolate, global_bounds)); result.Set(RectToV8Object(isolate, global_bounds));
}); });
...@@ -671,7 +711,9 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -671,7 +711,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
return; return;
} }
result.Set(v8::String::NewFromUtf8(isolate, attr_value.c_str())); result.Set(v8::String::NewFromUtf8(isolate, attr_value.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
}); });
RouteNodeIDPlusAttributeFunction( RouteNodeIDPlusAttributeFunction(
"GetBoolAttribute", "GetBoolAttribute",
...@@ -728,7 +770,11 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -728,7 +770,11 @@ void AutomationInternalCustomBindings::AddRoutes() {
if (!node->data().GetFloatAttribute(attribute, &attr_value)) if (!node->data().GetFloatAttribute(attribute, &attr_value))
return; return;
result.Set(v8::Number::New(isolate, attr_value)); double intpart, fracpart;
fracpart = modf(attr_value, &intpart);
double value_precision_2 =
intpart + std::round(fracpart * 100) / 100.0f;
result.Set(v8::Number::New(isolate, value_precision_2));
}); });
RouteNodeIDPlusAttributeFunction( RouteNodeIDPlusAttributeFunction(
"GetIntListAttribute", "GetIntListAttribute",
...@@ -775,7 +821,9 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -775,7 +821,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
if (!node->data().GetHtmlAttribute(attribute_name.c_str(), &attr_value)) if (!node->data().GetHtmlAttribute(attribute_name.c_str(), &attr_value))
return; return;
result.Set(v8::String::NewFromUtf8(isolate, attr_value.c_str())); result.Set(v8::String::NewFromUtf8(isolate, attr_value.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
}); });
RouteNodeIDFunction( RouteNodeIDFunction(
"GetNameFrom", "GetNameFrom",
...@@ -784,7 +832,9 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -784,7 +832,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
ax::mojom::NameFrom name_from = static_cast<ax::mojom::NameFrom>( ax::mojom::NameFrom name_from = static_cast<ax::mojom::NameFrom>(
node->data().GetIntAttribute(ax::mojom::IntAttribute::kNameFrom)); node->data().GetIntAttribute(ax::mojom::IntAttribute::kNameFrom));
std::string name_from_str = ui::ToString(name_from); std::string name_from_str = ui::ToString(name_from);
result.Set(v8::String::NewFromUtf8(isolate, name_from_str.c_str())); result.Set(v8::String::NewFromUtf8(isolate, name_from_str.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
}); });
RouteNodeIDFunction("GetSubscript", [](v8::Isolate* isolate, RouteNodeIDFunction("GetSubscript", [](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result, v8::ReturnValue<v8::Value> result,
...@@ -892,7 +942,9 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -892,7 +942,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
for (size_t i = 0; i < standard_actions.size(); i++) { for (size_t i = 0; i < standard_actions.size(); i++) {
const v8::Maybe<bool>& did_set_value = actions_result->Set( const v8::Maybe<bool>& did_set_value = actions_result->Set(
isolate->GetCurrentContext(), i, isolate->GetCurrentContext(), i,
v8::String::NewFromUtf8(isolate, standard_actions[i].c_str())); v8::String::NewFromUtf8(isolate, standard_actions[i].c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
bool did_set_value_result = false; bool did_set_value_result = false;
if (!did_set_value.To(&did_set_value_result) || !did_set_value_result) if (!did_set_value.To(&did_set_value_result) || !did_set_value_result)
...@@ -909,7 +961,9 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -909,7 +961,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
ax::mojom::IntAttribute::kCheckedState)); ax::mojom::IntAttribute::kCheckedState));
if (checked_state != ax::mojom::CheckedState::kNone) { if (checked_state != ax::mojom::CheckedState::kNone) {
const std::string checked_str = ui::ToString(checked_state); const std::string checked_str = ui::ToString(checked_state);
result.Set(v8::String::NewFromUtf8(isolate, checked_str.c_str())); result.Set(v8::String::NewFromUtf8(isolate, checked_str.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
} }
}); });
RouteNodeIDFunction( RouteNodeIDFunction(
...@@ -920,7 +974,9 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -920,7 +974,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
node->data().GetRestriction(); node->data().GetRestriction();
if (restriction != ax::mojom::Restriction::kNone) { if (restriction != ax::mojom::Restriction::kNone) {
const std::string restriction_str = ui::ToString(restriction); const std::string restriction_str = ui::ToString(restriction);
result.Set(v8::String::NewFromUtf8(isolate, restriction_str.c_str())); result.Set(v8::String::NewFromUtf8(isolate, restriction_str.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
} }
}); });
RouteNodeIDFunction( RouteNodeIDFunction(
...@@ -932,8 +988,76 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -932,8 +988,76 @@ void AutomationInternalCustomBindings::AddRoutes() {
node->data().GetIntAttribute( node->data().GetIntAttribute(
ax::mojom::IntAttribute::kDefaultActionVerb)); ax::mojom::IntAttribute::kDefaultActionVerb));
std::string default_action_verb_str = ui::ToString(default_action_verb); std::string default_action_verb_str = ui::ToString(default_action_verb);
result.Set( result.Set(v8::String::NewFromUtf8(isolate,
v8::String::NewFromUtf8(isolate, default_action_verb_str.c_str())); default_action_verb_str.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
});
RouteNodeIDPlusStringBoolFunction(
"GetNextTextMatch",
[this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node,
const std::string& search_str, bool backward) {
base::string16 search_str_16 = base::UTF8ToUTF16(search_str);
auto func =
backward ? &AutomationInternalCustomBindings::GetPreviousInTreeOrder
: &AutomationInternalCustomBindings::GetNextInTreeOrder;
std::function<ui::AXNode*(ui::AXNode*, AutomationAXTreeWrapper**)> next(
std::bind(func, this, std::placeholders::_1,
std::placeholders::_2));
AutomationAXTreeWrapper** target_tree_wrapper = &tree_wrapper;
while (true) {
node = next(node, target_tree_wrapper);
// We explicitly disallow searches in the desktop tree.
if ((*target_tree_wrapper)->tree_id() == ui::DesktopAXTreeID())
return;
if (!node)
return;
base::string16 name;
if (!node->GetString16Attribute(ax::mojom::StringAttribute::kName,
&name))
continue;
if (base::i18n::StringSearchIgnoringCaseAndAccents(
search_str_16, name, nullptr, nullptr)) {
gin::DataObjectBuilder response(isolate);
response.Set("treeId",
(*target_tree_wrapper)->tree_id().ToString());
response.Set("nodeId", node->id());
result.Set(response.Build());
return;
}
}
});
RouteNodeIDFunction(
"GetTableCellColumnHeaders",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
std::vector<int32_t> col_headers;
node->GetTableCellColHeaderNodeIds(&col_headers);
v8::Local<v8::Array> array_result(
v8::Array::New(isolate, col_headers.size()));
for (size_t i = 0; i < col_headers.size(); ++i)
array_result->Set(static_cast<uint32_t>(i),
v8::Integer::New(isolate, col_headers[i]));
result.Set(array_result);
});
RouteNodeIDFunction(
"GetTableCellRowHeaders",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
std::vector<int32_t> row_headers;
node->GetTableCellRowHeaderNodeIds(&row_headers);
v8::Local<v8::Array> array_result(
v8::Array::New(isolate, row_headers.size()));
for (size_t i = 0; i < row_headers.size(); ++i)
array_result->Set(static_cast<uint32_t>(i),
v8::Integer::New(isolate, row_headers[i]));
result.Set(array_result);
}); });
} }
...@@ -959,7 +1083,7 @@ void AutomationInternalCustomBindings::OnMessageReceived( ...@@ -959,7 +1083,7 @@ void AutomationInternalCustomBindings::OnMessageReceived(
} // clang-format on } // clang-format on
AutomationAXTreeWrapper* AutomationInternalCustomBindings:: AutomationAXTreeWrapper* AutomationInternalCustomBindings::
GetAutomationAXTreeWrapperFromTreeID(ui::AXTreeID tree_id) { GetAutomationAXTreeWrapperFromTreeID(ui::AXTreeID tree_id) const {
const auto iter = tree_id_to_tree_wrapper_map_.find(tree_id); const auto iter = tree_id_to_tree_wrapper_map_.find(tree_id);
if (iter == tree_id_to_tree_wrapper_map_.end()) if (iter == tree_id_to_tree_wrapper_map_.end())
return nullptr; return nullptr;
...@@ -1241,52 +1365,124 @@ void AutomationInternalCustomBindings::UpdateOverallTreeChangeObserverFilter() { ...@@ -1241,52 +1365,124 @@ void AutomationInternalCustomBindings::UpdateOverallTreeChangeObserverFilter() {
ui::AXNode* AutomationInternalCustomBindings::GetParent( ui::AXNode* AutomationInternalCustomBindings::GetParent(
ui::AXNode* node, ui::AXNode* node,
AutomationAXTreeWrapper** in_out_tree_wrapper) { AutomationAXTreeWrapper** in_out_tree_wrapper) const {
if (node->parent()) if (node->parent())
return node->parent(); return node->parent();
AutomationAXTreeWrapper* parent_tree_wrapper = nullptr;
ui::AXTreeID parent_tree_id = ui::AXTreeID parent_tree_id =
(*in_out_tree_wrapper)->tree()->data().parent_tree_id; (*in_out_tree_wrapper)->tree()->data().parent_tree_id;
if (parent_tree_id != ui::AXTreeIDUnknown()) {
// If the tree specifies its parent tree ID, use that. That provides
// some additional security guarantees, so a tree can't be "claimed"
// by something else.
parent_tree_wrapper = GetAutomationAXTreeWrapperFromTreeID(parent_tree_id);
} else {
// Otherwise if it was unspecified, check to see if another tree listed
// this one as its child, and then we know the parent.
parent_tree_wrapper = AutomationAXTreeWrapper::GetParentOfTreeId(
(*in_out_tree_wrapper)->tree_id());
}
// Try the desktop tree if the parent is unknown. If this tree really is
// a child of the desktop tree, we'll find its parent, and if not, the
// search, below, will fail until the real parent tree loads.
if (parent_tree_id == ui::AXTreeIDUnknown())
parent_tree_id = ui::DesktopAXTreeID();
AutomationAXTreeWrapper* parent_tree_wrapper =
GetAutomationAXTreeWrapperFromTreeID(parent_tree_id);
if (!parent_tree_wrapper) if (!parent_tree_wrapper)
return nullptr; return nullptr;
// Try to use the cached host node from the most recent time this std::set<int32_t> host_node_ids =
// was called. parent_tree_wrapper->tree()->GetNodeIdsForChildTreeId(
if ((*in_out_tree_wrapper)->host_node_id() > 0) { (*in_out_tree_wrapper)->tree_id());
ui::AXNode* parent = parent_tree_wrapper->tree()->GetFromId(
(*in_out_tree_wrapper)->host_node_id()); #if !defined(NDEBUG)
if (parent) { if (host_node_ids.size() > 1)
ui::AXTreeID parent_child_tree_id = DLOG(WARNING) << "Multiple nodes claim the same child tree id.";
ui::AXTreeID::FromString(parent->data().GetStringAttribute( #endif
ax::mojom::StringAttribute::kChildTreeId));
if (parent_child_tree_id == (*in_out_tree_wrapper)->tree_id()) { for (int32_t host_node_id : host_node_ids) {
*in_out_tree_wrapper = parent_tree_wrapper; ui::AXNode* host_node =
return parent; parent_tree_wrapper->tree()->GetFromId(host_node_id);
} if (host_node) {
DCHECK_EQ((*in_out_tree_wrapper)->tree_id(),
ui::AXTreeID::FromString(host_node->GetStringAttribute(
ax::mojom::StringAttribute::kChildTreeId)));
*in_out_tree_wrapper = parent_tree_wrapper;
return host_node;
} }
} }
// If that fails, search for it and cache it for next time. return nullptr;
ui::AXNode* parent = FindNodeWithChildTreeId( }
parent_tree_wrapper->tree()->root(), (*in_out_tree_wrapper)->tree_id());
if (parent) { bool AutomationInternalCustomBindings::GetRootOfChildTree(
(*in_out_tree_wrapper)->set_host_node_id(parent->id()); ui::AXNode** in_out_node,
*in_out_tree_wrapper = parent_tree_wrapper; AutomationAXTreeWrapper** in_out_tree_wrapper) const {
return parent; std::string child_tree_id_str;
if (!(*in_out_node)
->GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
&child_tree_id_str))
return false;
AutomationAXTreeWrapper* child_tree_wrapper =
GetAutomationAXTreeWrapperFromTreeID(
ui::AXTreeID::FromString(child_tree_id_str));
if (!child_tree_wrapper || !child_tree_wrapper->tree()->root())
return false;
*in_out_tree_wrapper = child_tree_wrapper;
*in_out_node = child_tree_wrapper->tree()->root();
return true;
}
ui::AXNode* AutomationInternalCustomBindings::GetNextInTreeOrder(
ui::AXNode* start,
AutomationAXTreeWrapper** in_out_tree_wrapper) const {
ui::AXNode* walker = start;
if (walker->child_count() > 0)
return walker->ChildAtIndex(0);
// We also have to check child tree id.
if (GetRootOfChildTree(&walker, in_out_tree_wrapper))
return walker;
// Find the next branch forward.
ui::AXNode* parent;
while ((parent = GetParent(walker, in_out_tree_wrapper))) {
if ((walker->index_in_parent() + 1) < parent->child_count())
return parent->ChildAtIndex(walker->index_in_parent() + 1);
walker = parent;
} }
return nullptr; return nullptr;
} }
ui::AXNode* AutomationInternalCustomBindings::GetPreviousInTreeOrder(
ui::AXNode* start,
AutomationAXTreeWrapper** in_out_tree_wrapper) const {
ui::AXNode* walker = start;
ui::AXNode* parent = GetParent(start, in_out_tree_wrapper);
// No more nodes.
if (!parent)
return nullptr;
// No previous sibling; parent is previous.
if (walker->index_in_parent() == 0)
return parent;
walker = parent->ChildAtIndex(walker->index_in_parent() - 1);
// Walks to deepest last child.
while (true) {
if (walker->child_count() > 0) {
walker = walker->ChildAtIndex(walker->child_count() - 1);
} else if (!GetRootOfChildTree(&walker, in_out_tree_wrapper)) {
break;
}
}
return walker;
}
float AutomationInternalCustomBindings::GetDeviceScaleFactor() const { float AutomationInternalCustomBindings::GetDeviceScaleFactor() const {
return context()->GetRenderFrame()->GetRenderView()->GetDeviceScaleFactor(); return context()->GetRenderFrame()->GetRenderView()->GetDeviceScaleFactor();
} }
...@@ -1302,7 +1498,7 @@ void AutomationInternalCustomBindings::RouteNodeIDFunction( ...@@ -1302,7 +1498,7 @@ void AutomationInternalCustomBindings::RouteNodeIDFunction(
const std::string& name, const std::string& name,
NodeIDFunction callback) { NodeIDFunction callback) {
scoped_refptr<NodeIDWrapper> wrapper = new NodeIDWrapper(this, callback); scoped_refptr<NodeIDWrapper> wrapper = new NodeIDWrapper(this, callback);
RouteHandlerFunction(name, base::Bind(&NodeIDWrapper::Run, wrapper)); RouteHandlerFunction(name, base::BindRepeating(&NodeIDWrapper::Run, wrapper));
} }
void AutomationInternalCustomBindings::RouteNodeIDPlusAttributeFunction( void AutomationInternalCustomBindings::RouteNodeIDPlusAttributeFunction(
...@@ -1322,6 +1518,15 @@ void AutomationInternalCustomBindings::RouteNodeIDPlusRangeFunction( ...@@ -1322,6 +1518,15 @@ void AutomationInternalCustomBindings::RouteNodeIDPlusRangeFunction(
RouteHandlerFunction(name, base::Bind(&NodeIDPlusRangeWrapper::Run, wrapper)); RouteHandlerFunction(name, base::Bind(&NodeIDPlusRangeWrapper::Run, wrapper));
} }
void AutomationInternalCustomBindings::RouteNodeIDPlusStringBoolFunction(
const std::string& name,
NodeIDPlusStringBoolFunction callback) {
scoped_refptr<NodeIDPlusStringBoolWrapper> wrapper =
new NodeIDPlusStringBoolWrapper(this, callback);
RouteHandlerFunction(
name, base::BindRepeating(&NodeIDPlusStringBoolWrapper::Run, wrapper));
}
void AutomationInternalCustomBindings::GetChildIDAtIndex( void AutomationInternalCustomBindings::GetChildIDAtIndex(
const v8::FunctionCallbackInfo<v8::Value>& args) { const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() < 3 || !args[2]->IsNumber()) { if (args.Length() < 3 || !args[2]->IsNumber()) {
...@@ -1343,11 +1548,20 @@ void AutomationInternalCustomBindings::GetChildIDAtIndex( ...@@ -1343,11 +1548,20 @@ void AutomationInternalCustomBindings::GetChildIDAtIndex(
return; return;
int index = args[2]->Int32Value(context()->v8_context()).FromMaybe(0); int index = args[2]->Int32Value(context()->v8_context()).FromMaybe(0);
if (index < 0 || index >= node->child_count()) int child_id;
// Check for a child tree, which is guaranteed to always be the only child.
if (index == 0 && GetRootOfChildTree(&node, &tree_wrapper))
child_id = node->id();
else if (index < 0 || index >= node->child_count())
return; return;
else
child_id = node->children()[index]->id();
int child_id = node->children()[index]->id(); gin::DataObjectBuilder response(GetIsolate());
args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_id)); response.Set("treeId", tree_wrapper->tree_id().ToString());
response.Set("nodeId", child_id);
args.GetReturnValue().Set(response.Build());
} }
// //
...@@ -1397,18 +1611,24 @@ void AutomationInternalCustomBindings::OnAccessibilityLocationChange( ...@@ -1397,18 +1611,24 @@ void AutomationInternalCustomBindings::OnAccessibilityLocationChange(
params.new_location.transform.get()); params.new_location.transform.get());
} }
void AutomationInternalCustomBindings::SendTreeChangeEvent( bool AutomationInternalCustomBindings::SendTreeChangeEvent(
api::automation::TreeChangeType change_type, api::automation::TreeChangeType change_type,
ui::AXTree* tree, ui::AXTree* tree,
ui::AXNode* node) { ui::AXNode* node) {
// Don't send tree change events when it's not the active profile. // Don't send tree change events when it's not the active profile.
if (!is_active_profile_) if (!is_active_profile_)
return; return false;
// Always notify the custom bindings when there's a node with a child tree // Notify custom bindings when there's an unloaded tree; js will enable the
// ID that might need to be loaded. // renderer and wait for it to load.
if (node->data().HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId)) std::string child_tree_id_str;
SendChildTreeIDEvent(tree, node); if (node->data().GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
&child_tree_id_str)) {
ui::AXTreeID child_tree_id = ui::AXTreeID::FromString(child_tree_id_str);
auto* tree_wrapper = GetAutomationAXTreeWrapperFromTreeID(child_tree_id);
if (!tree_wrapper || !tree_wrapper->tree()->data().loaded)
SendChildTreeIDEvent(child_tree_id);
}
bool has_filter = false; bool has_filter = false;
if (tree_change_observer_overall_filter_ & if (tree_change_observer_overall_filter_ &
...@@ -1416,7 +1636,8 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent( ...@@ -1416,7 +1636,8 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent(
<< api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES)) { << api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES)) {
if (node->data().HasStringAttribute( if (node->data().HasStringAttribute(
ax::mojom::StringAttribute::kContainerLiveStatus) || ax::mojom::StringAttribute::kContainerLiveStatus) ||
node->data().role == ax::mojom::Role::kAlert) { node->data().role == ax::mojom::Role::kAlert ||
change_type == api::automation::TREE_CHANGE_TYPE_SUBTREEUPDATEEND) {
has_filter = true; has_filter = true;
} }
} }
...@@ -1433,14 +1654,14 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent( ...@@ -1433,14 +1654,14 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent(
has_filter = true; has_filter = true;
if (!has_filter) if (!has_filter)
return; return false;
auto iter = axtree_to_tree_wrapper_map_.find(tree); auto iter = axtree_to_tree_wrapper_map_.find(tree);
if (iter == axtree_to_tree_wrapper_map_.end()) if (iter == axtree_to_tree_wrapper_map_.end())
return; return false;
ui::AXTreeID tree_id = iter->second->tree_id(); ui::AXTreeID tree_id = iter->second->tree_id();
bool did_send_event = false;
for (const auto& observer : tree_change_observers_) { for (const auto& observer : tree_change_observers_) {
switch (observer.filter) { switch (observer.filter) {
case api::automation::TREE_CHANGE_OBSERVER_FILTER_NOTREECHANGES: case api::automation::TREE_CHANGE_OBSERVER_FILTER_NOTREECHANGES:
...@@ -1449,7 +1670,8 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent( ...@@ -1449,7 +1670,8 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent(
case api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES: case api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES:
if (!node->data().HasStringAttribute( if (!node->data().HasStringAttribute(
ax::mojom::StringAttribute::kContainerLiveStatus) && ax::mojom::StringAttribute::kContainerLiveStatus) &&
node->data().role != ax::mojom::Role::kAlert) { node->data().role != ax::mojom::Role::kAlert &&
change_type != api::automation::TREE_CHANGE_TYPE_SUBTREEUPDATEEND) {
continue; continue;
} }
break; break;
...@@ -1462,6 +1684,7 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent( ...@@ -1462,6 +1684,7 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent(
break; break;
} }
did_send_event = true;
base::ListValue args; base::ListValue args;
args.AppendInteger(observer.id); args.AppendInteger(observer.id);
args.AppendString(tree_id.ToString()); args.AppendString(tree_id.ToString());
...@@ -1470,12 +1693,14 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent( ...@@ -1470,12 +1693,14 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent(
bindings_system_->DispatchEventInContext("automationInternal.onTreeChange", bindings_system_->DispatchEventInContext("automationInternal.onTreeChange",
&args, nullptr, context()); &args, nullptr, context());
} }
return did_send_event;
} }
void AutomationInternalCustomBindings::SendAutomationEvent( void AutomationInternalCustomBindings::SendAutomationEvent(
ui::AXTreeID tree_id, ui::AXTreeID tree_id,
const gfx::Point& mouse_location, const gfx::Point& mouse_location,
ui::AXEvent& event, const ui::AXEvent& event,
api::automation::EventType event_type) { api::automation::EventType event_type) {
auto event_params = std::make_unique<base::DictionaryValue>(); auto event_params = std::make_unique<base::DictionaryValue>();
event_params->SetString("treeID", tree_id.ToString()); event_params->SetString("treeID", tree_id.ToString());
...@@ -1491,17 +1716,10 @@ void AutomationInternalCustomBindings::SendAutomationEvent( ...@@ -1491,17 +1716,10 @@ void AutomationInternalCustomBindings::SendAutomationEvent(
"automationInternal.onAccessibilityEvent", &args, nullptr, context()); "automationInternal.onAccessibilityEvent", &args, nullptr, context());
} }
void AutomationInternalCustomBindings::SendChildTreeIDEvent(ui::AXTree* tree, void AutomationInternalCustomBindings::SendChildTreeIDEvent(
ui::AXNode* node) { ui::AXTreeID child_tree_id) {
auto iter = axtree_to_tree_wrapper_map_.find(tree);
if (iter == axtree_to_tree_wrapper_map_.end())
return;
ui::AXTreeID tree_id = iter->second->tree_id();
base::ListValue args; base::ListValue args;
args.AppendString(tree_id.ToString()); args.AppendString(child_tree_id.ToString());
args.AppendInteger(node->id());
bindings_system_->DispatchEventInContext("automationInternal.onChildTreeID", bindings_system_->DispatchEventInContext("automationInternal.onChildTreeID",
&args, nullptr, context()); &args, nullptr, context());
} }
......
...@@ -53,14 +53,26 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler { ...@@ -53,14 +53,26 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
void OnMessageReceived(const IPC::Message& message); void OnMessageReceived(const IPC::Message& message);
AutomationAXTreeWrapper* GetAutomationAXTreeWrapperFromTreeID( AutomationAXTreeWrapper* GetAutomationAXTreeWrapperFromTreeID(
ui::AXTreeID tree_id); ui::AXTreeID tree_id) const;
// Given a tree (|in_out_tree_wrapper|) and a node, returns the parent. // Given a tree (|in_out_tree_wrapper|) and a node, returns the parent.
// If |node| is the root of its tree, the return value will be the host // If |node| is the root of its tree, the return value will be the host
// node of the parent tree and |in_out_tree_wrapper| will be updated to // node of the parent tree and |in_out_tree_wrapper| will be updated to
// point to that parent tree. // point to that parent tree.
ui::AXNode* GetParent(ui::AXNode* node, ui::AXNode* GetParent(ui::AXNode* node,
AutomationAXTreeWrapper** in_out_tree_wrapper); AutomationAXTreeWrapper** in_out_tree_wrapper) const;
// Gets the root of a node's child tree and adjusts incoming arguments
// accordingly. Returns false if no adjustments were made.
bool GetRootOfChildTree(ui::AXNode** in_out_node,
AutomationAXTreeWrapper** in_out_tree_wrapper) const;
ui::AXNode* GetNextInTreeOrder(
ui::AXNode* start,
AutomationAXTreeWrapper** in_out_tree_wrapper) const;
ui::AXNode* GetPreviousInTreeOrder(
ui::AXNode* start,
AutomationAXTreeWrapper** in_out_tree_wrapper) const;
ScriptContext* context() const { ScriptContext* context() const {
return ObjectBackedNativeHandler::context(); return ObjectBackedNativeHandler::context();
...@@ -69,12 +81,12 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler { ...@@ -69,12 +81,12 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
float GetDeviceScaleFactor() const; float GetDeviceScaleFactor() const;
void SendNodesRemovedEvent(ui::AXTree* tree, const std::vector<int>& ids); void SendNodesRemovedEvent(ui::AXTree* tree, const std::vector<int>& ids);
void SendTreeChangeEvent(api::automation::TreeChangeType change_type, bool SendTreeChangeEvent(api::automation::TreeChangeType change_type,
ui::AXTree* tree, ui::AXTree* tree,
ui::AXNode* node); ui::AXNode* node);
void SendAutomationEvent(ui::AXTreeID tree_id, void SendAutomationEvent(ui::AXTreeID tree_id,
const gfx::Point& mouse_location, const gfx::Point& mouse_location,
ui::AXEvent& event, const ui::AXEvent& event,
api::automation::EventType event_type); api::automation::EventType event_type);
private: private:
...@@ -98,7 +110,7 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler { ...@@ -98,7 +110,7 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
// Called when an accessibility tree is destroyed and needs to be // Called when an accessibility tree is destroyed and needs to be
// removed from our cache. // removed from our cache.
// Args: int ax_tree_id // Args: string ax_tree_id
void DestroyAccessibilityTree( void DestroyAccessibilityTree(
const v8::FunctionCallbackInfo<v8::Value>& args); const v8::FunctionCallbackInfo<v8::Value>& args);
...@@ -124,11 +136,10 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler { ...@@ -124,11 +136,10 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
void RouteNodeIDFunction( void RouteNodeIDFunction(
const std::string& name, const std::string& name,
void (*callback)(v8::Isolate* isolate, std::function<void(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, AutomationAXTreeWrapper* tree_wrapper,
ui::AXNode* node)); ui::AXNode* node)> callback);
void RouteNodeIDPlusAttributeFunction( void RouteNodeIDPlusAttributeFunction(
const std::string& name, const std::string& name,
void (*callback)(v8::Isolate* isolate, void (*callback)(v8::Isolate* isolate,
...@@ -144,19 +155,27 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler { ...@@ -144,19 +155,27 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
ui::AXNode* node, ui::AXNode* node,
int start, int start,
int end)); int end));
void RouteNodeIDPlusStringBoolFunction(
const std::string& name,
std::function<void(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
ui::AXNode* node,
const std::string& strVal,
bool boolVal)> callback);
// //
// Access the cached accessibility trees and properties of their nodes. // Access the cached accessibility trees and properties of their nodes.
// //
// Args: int ax_tree_id, int node_id, Returns: int child_id. // Args: string ax_tree_id, int node_id, Returns: int child_id.
void GetChildIDAtIndex(const v8::FunctionCallbackInfo<v8::Value>& args); void GetChildIDAtIndex(const v8::FunctionCallbackInfo<v8::Value>& args);
// Args: int ax_tree_id, int node_id // Args: string ax_tree_id, int node_id
// Returns: JS object with a map from html attribute key to value. // Returns: JS object with a map from html attribute key to value.
void GetHtmlAttributes(const v8::FunctionCallbackInfo<v8::Value>& args); void GetHtmlAttributes(const v8::FunctionCallbackInfo<v8::Value>& args);
// Args: int ax_tree_id, int node_id // Args: string ax_tree_id, int node_id
// Returns: JS object with a string key for each state flag that's set. // Returns: JS object with a string key for each state flag that's set.
void GetState(const v8::FunctionCallbackInfo<v8::Value>& args); void GetState(const v8::FunctionCallbackInfo<v8::Value>& args);
...@@ -173,7 +192,7 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler { ...@@ -173,7 +192,7 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
void UpdateOverallTreeChangeObserverFilter(); void UpdateOverallTreeChangeObserverFilter();
void SendChildTreeIDEvent(ui::AXTree* tree, ui::AXNode* node); void SendChildTreeIDEvent(ui::AXTreeID child_tree_id);
std::map<ui::AXTreeID, std::unique_ptr<AutomationAXTreeWrapper>> std::map<ui::AXTreeID, std::unique_ptr<AutomationAXTreeWrapper>>
tree_id_to_tree_wrapper_map_; tree_id_to_tree_wrapper_map_;
......
...@@ -14,74 +14,74 @@ var natives = requireNative('automationInternal'); ...@@ -14,74 +14,74 @@ var natives = requireNative('automationInternal');
var IsInteractPermitted = natives.IsInteractPermitted; var IsInteractPermitted = natives.IsInteractPermitted;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The id of the root node. * @return {?number} The id of the root node.
*/ */
var GetRootID = natives.GetRootID; var GetRootID = natives.GetRootID;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?string} The title of the document. * @return {?string} The title of the document.
*/ */
var GetDocTitle = natives.GetDocTitle; var GetDocTitle = natives.GetDocTitle;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?string} The url of the document. * @return {?string} The url of the document.
*/ */
var GetDocURL = natives.GetDocURL; var GetDocURL = natives.GetDocURL;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?boolean} True if the document has finished loading. * @return {?boolean} True if the document has finished loading.
*/ */
var GetDocLoaded = natives.GetDocLoaded; var GetDocLoaded = natives.GetDocLoaded;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The loading progress, from 0.0 to 1.0 (fully loaded). * @return {?number} The loading progress, from 0.0 to 1.0 (fully loaded).
*/ */
var GetDocLoadingProgress = var GetDocLoadingProgress =
natives.GetDocLoadingProgress; natives.GetDocLoadingProgress;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The ID of the selection anchor object. * @return {?number} The ID of the selection anchor object.
*/ */
var GetAnchorObjectID = natives.GetAnchorObjectID; var GetAnchorObjectID = natives.GetAnchorObjectID;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The selection anchor offset. * @return {?number} The selection anchor offset.
*/ */
var GetAnchorOffset = natives.GetAnchorOffset; var GetAnchorOffset = natives.GetAnchorOffset;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?string} The selection anchor affinity. * @return {?string} The selection anchor affinity.
*/ */
var GetAnchorAffinity = natives.GetAnchorAffinity; var GetAnchorAffinity = natives.GetAnchorAffinity;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The ID of the selection focus object. * @return {?number} The ID of the selection focus object.
*/ */
var GetFocusObjectID = natives.GetFocusObjectID; var GetFocusObjectID = natives.GetFocusObjectID;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The selection focus offset. * @return {?number} The selection focus offset.
*/ */
var GetFocusOffset = natives.GetFocusOffset; var GetFocusOffset = natives.GetFocusOffset;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @return {?string} The selection focus affinity. * @return {?string} The selection focus affinity.
*/ */
var GetFocusAffinity = natives.GetFocusAffinity; var GetFocusAffinity = natives.GetFocusAffinity;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {?number} The id of the node's parent, or undefined if it's the * @return {?number} The id of the node's parent, or undefined if it's the
* root of its tree or if the tree or node wasn't found. * root of its tree or if the tree or node wasn't found.
...@@ -89,7 +89,7 @@ var GetFocusAffinity = natives.GetFocusAffinity; ...@@ -89,7 +89,7 @@ var GetFocusAffinity = natives.GetFocusAffinity;
var GetParentID = natives.GetParentID; var GetParentID = natives.GetParentID;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {?number} The number of children of the node, or undefined if * @return {?number} The number of children of the node, or undefined if
* the tree or node wasn't found. * the tree or node wasn't found.
...@@ -97,7 +97,7 @@ var GetParentID = natives.GetParentID; ...@@ -97,7 +97,7 @@ var GetParentID = natives.GetParentID;
var GetChildCount = natives.GetChildCount; var GetChildCount = natives.GetChildCount;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @param {number} childIndex An index of a child of this node. * @param {number} childIndex An index of a child of this node.
* @return {?number} The id of the child at the given index, or undefined * @return {?number} The id of the child at the given index, or undefined
...@@ -106,7 +106,7 @@ var GetChildCount = natives.GetChildCount; ...@@ -106,7 +106,7 @@ var GetChildCount = natives.GetChildCount;
var GetChildIDAtIndex = natives.GetChildIDAtIndex; var GetChildIDAtIndex = natives.GetChildIDAtIndex;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {?number} The ids of the children of the node, or undefined * @return {?number} The ids of the children of the node, or undefined
* if the tree or node wasn't found. * if the tree or node wasn't found.
...@@ -114,14 +114,14 @@ var GetChildIDAtIndex = natives.GetChildIDAtIndex; ...@@ -114,14 +114,14 @@ var GetChildIDAtIndex = natives.GetChildIDAtIndex;
var GetChildIds = natives.GetChildIDs; var GetChildIds = natives.GetChildIDs;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {?Object} An object mapping html attributes to values. * @return {?Object} An object mapping html attributes to values.
*/ */
var GetHtmlAttributes = natives.GetHtmlAttributes; var GetHtmlAttributes = natives.GetHtmlAttributes;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {?number} The index of this node in its parent, or undefined if * @return {?number} The index of this node in its parent, or undefined if
* the tree or node or node parent wasn't found. * the tree or node or node parent wasn't found.
...@@ -129,7 +129,7 @@ var GetHtmlAttributes = natives.GetHtmlAttributes; ...@@ -129,7 +129,7 @@ var GetHtmlAttributes = natives.GetHtmlAttributes;
var GetIndexInParent = natives.GetIndexInParent; var GetIndexInParent = natives.GetIndexInParent;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {?Object} An object with a string key for every state flag set, * @return {?Object} An object with a string key for every state flag set,
* or undefined if the tree or node or node parent wasn't found. * or undefined if the tree or node or node parent wasn't found.
...@@ -137,7 +137,7 @@ var GetIndexInParent = natives.GetIndexInParent; ...@@ -137,7 +137,7 @@ var GetIndexInParent = natives.GetIndexInParent;
var GetState = natives.GetState; var GetState = natives.GetState;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {string} The restriction, one of * @return {string} The restriction, one of
* "disabled", "readOnly" or undefined if enabled or other object not disabled * "disabled", "readOnly" or undefined if enabled or other object not disabled
...@@ -145,14 +145,14 @@ var GetState = natives.GetState; ...@@ -145,14 +145,14 @@ var GetState = natives.GetState;
var GetRestriction = natives.GetRestriction; var GetRestriction = natives.GetRestriction;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {string} The checked state, as undefined, "true", "false" or "mixed". * @return {string} The checked state, as undefined, "true", "false" or "mixed".
*/ */
var GetChecked = natives.GetChecked; var GetChecked = natives.GetChecked;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {string} The role of the node, or undefined if the tree or * @return {string} The role of the node, or undefined if the tree or
* node wasn't found. * node wasn't found.
...@@ -160,7 +160,7 @@ var GetChecked = natives.GetChecked; ...@@ -160,7 +160,7 @@ var GetChecked = natives.GetChecked;
var GetRole = natives.GetRole; var GetRole = natives.GetRole;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {?automation.Rect} The location of the node, or undefined if * @return {?automation.Rect} The location of the node, or undefined if
* the tree or node wasn't found. * the tree or node wasn't found.
...@@ -168,7 +168,7 @@ var GetRole = natives.GetRole; ...@@ -168,7 +168,7 @@ var GetRole = natives.GetRole;
var GetLocation = natives.GetLocation; var GetLocation = natives.GetLocation;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @param {number} startIndex The start index of the range. * @param {number} startIndex The start index of the range.
* @param {number} endIndex The end index of the range. * @param {number} endIndex The end index of the range.
...@@ -179,7 +179,7 @@ var GetLocation = natives.GetLocation; ...@@ -179,7 +179,7 @@ var GetLocation = natives.GetLocation;
var GetBoundsForRange = natives.GetBoundsForRange; var GetBoundsForRange = natives.GetBoundsForRange;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {?automation.Rect} The unclipped location of the node, or * @return {?automation.Rect} The unclipped location of the node, or
* undefined if the tree or node wasn't found. * undefined if the tree or node wasn't found.
...@@ -187,7 +187,7 @@ var GetBoundsForRange = natives.GetBoundsForRange; ...@@ -187,7 +187,7 @@ var GetBoundsForRange = natives.GetBoundsForRange;
var GetUnclippedLocation = natives.GetUnclippedLocation; var GetUnclippedLocation = natives.GetUnclippedLocation;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {!Array<number>} The text offset where each line starts, or an empty * @return {!Array<number>} The text offset where each line starts, or an empty
* array if this node has no text content, or undefined if the tree or node * array if this node has no text content, or undefined if the tree or node
...@@ -197,7 +197,7 @@ var GetLineStartOffsets = requireNative( ...@@ -197,7 +197,7 @@ var GetLineStartOffsets = requireNative(
'automationInternal').GetLineStartOffsets; 'automationInternal').GetLineStartOffsets;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @param {string} attr The name of a string attribute. * @param {string} attr The name of a string attribute.
* @return {?string} The value of this attribute, or undefined if the tree, * @return {?string} The value of this attribute, or undefined if the tree,
...@@ -206,7 +206,7 @@ var GetLineStartOffsets = requireNative( ...@@ -206,7 +206,7 @@ var GetLineStartOffsets = requireNative(
var GetStringAttribute = natives.GetStringAttribute; var GetStringAttribute = natives.GetStringAttribute;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute. * @param {string} attr The name of an attribute.
* @return {?boolean} The value of this attribute, or undefined if the tree, * @return {?boolean} The value of this attribute, or undefined if the tree,
...@@ -215,7 +215,7 @@ var GetStringAttribute = natives.GetStringAttribute; ...@@ -215,7 +215,7 @@ var GetStringAttribute = natives.GetStringAttribute;
var GetBoolAttribute = natives.GetBoolAttribute; var GetBoolAttribute = natives.GetBoolAttribute;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute. * @param {string} attr The name of an attribute.
* @return {?number} The value of this attribute, or undefined if the tree, * @return {?number} The value of this attribute, or undefined if the tree,
...@@ -224,7 +224,7 @@ var GetBoolAttribute = natives.GetBoolAttribute; ...@@ -224,7 +224,7 @@ var GetBoolAttribute = natives.GetBoolAttribute;
var GetIntAttribute = natives.GetIntAttribute; var GetIntAttribute = natives.GetIntAttribute;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute. * @param {string} attr The name of an attribute.
* @return {?Array<number>} The ids of nodes who have a relationship pointing * @return {?Array<number>} The ids of nodes who have a relationship pointing
...@@ -234,7 +234,7 @@ var GetIntAttributeReverseRelations = ...@@ -234,7 +234,7 @@ var GetIntAttributeReverseRelations =
natives.GetIntAttributeReverseRelations; natives.GetIntAttributeReverseRelations;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute. * @param {string} attr The name of an attribute.
* @return {?number} The value of this attribute, or undefined if the tree, * @return {?number} The value of this attribute, or undefined if the tree,
...@@ -243,7 +243,7 @@ var GetIntAttributeReverseRelations = ...@@ -243,7 +243,7 @@ var GetIntAttributeReverseRelations =
var GetFloatAttribute = natives.GetFloatAttribute; var GetFloatAttribute = natives.GetFloatAttribute;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute. * @param {string} attr The name of an attribute.
* @return {?Array<number>} The value of this attribute, or undefined * @return {?Array<number>} The value of this attribute, or undefined
...@@ -253,7 +253,7 @@ var GetIntListAttribute = ...@@ -253,7 +253,7 @@ var GetIntListAttribute =
natives.GetIntListAttribute; natives.GetIntListAttribute;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute. * @param {string} attr The name of an attribute.
* @return {?Array<number>} The ids of nodes who have a relationship pointing * @return {?Array<number>} The ids of nodes who have a relationship pointing
...@@ -263,7 +263,7 @@ var GetIntListAttributeReverseRelations = ...@@ -263,7 +263,7 @@ var GetIntListAttributeReverseRelations =
natives.GetIntListAttributeReverseRelations; natives.GetIntListAttributeReverseRelations;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @param {string} attr The name of an HTML attribute. * @param {string} attr The name of an HTML attribute.
* @return {?string} The value of this attribute, or undefined if the tree, * @return {?string} The value of this attribute, or undefined if the tree,
...@@ -272,42 +272,42 @@ var GetIntListAttributeReverseRelations = ...@@ -272,42 +272,42 @@ var GetIntListAttributeReverseRelations =
var GetHtmlAttribute = natives.GetHtmlAttribute; var GetHtmlAttribute = natives.GetHtmlAttribute;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {automation.NameFromType} The source of the node's name. * @return {automation.NameFromType} The source of the node's name.
*/ */
var GetNameFrom = natives.GetNameFrom; var GetNameFrom = natives.GetNameFrom;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {boolean} * @return {boolean}
*/ */
var GetBold = natives.GetBold; var GetBold = natives.GetBold;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {boolean} * @return {boolean}
*/ */
var GetItalic = natives.GetItalic; var GetItalic = natives.GetItalic;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {boolean} * @return {boolean}
*/ */
var GetUnderline = natives.GetUnderline; var GetUnderline = natives.GetUnderline;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {boolean} * @return {boolean}
*/ */
var GetLineThrough = natives.GetLineThrough; var GetLineThrough = natives.GetLineThrough;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {?Array<automation.CustomAction>} List of custom actions of the * @return {?Array<automation.CustomAction>} List of custom actions of the
* node. * node.
...@@ -315,19 +315,45 @@ var GetLineThrough = natives.GetLineThrough; ...@@ -315,19 +315,45 @@ var GetLineThrough = natives.GetLineThrough;
var GetCustomActions = natives.GetCustomActions; var GetCustomActions = natives.GetCustomActions;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {?Array<string>} List of standard actions of the node. * @return {?Array<string>} List of standard actions of the node.
*/ */
var GetStandardActions = natives.GetStandardActions; var GetStandardActions = natives.GetStandardActions;
/** /**
* @param {number} axTreeID The id of the accessibility tree. * @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node. * @param {number} nodeID The id of a node.
* @return {automation.NameFromType} The source of the node's name. * @return {automation.NameFromType} The source of the node's name.
*/ */
var GetDefaultActionVerb = natives.GetDefaultActionVerb; var GetDefaultActionVerb = natives.GetDefaultActionVerb;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} searchStr
* @param {boolean} backward
* @return {{treeId: string, nodeId: number}}
*/
var GetNextTextMatch = natives.GetNextTextMatch;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?Array<number>} A list of column header ids.
* @return {?number} The id of the column header, if it exists.
*/
var GetTableCellColumnHeaders = natives.GetTableCellColumnHeaders;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?Array<number>} A list of row header ids.
*/
var GetTableCellRowHeaders = natives.GetTableCellRowHeaders;
var logging = requireNative('logging'); var logging = requireNative('logging');
var utils = require('utils'); var utils = require('utils');
...@@ -338,19 +364,17 @@ var utils = require('utils'); ...@@ -338,19 +364,17 @@ var utils = require('utils');
*/ */
function AutomationNodeImpl(root) { function AutomationNodeImpl(root) {
this.rootImpl = root; this.rootImpl = root;
this.hostNode_ = null;
this.listeners = {__proto__: null}; this.listeners = {__proto__: null};
} }
AutomationNodeImpl.prototype = { AutomationNodeImpl.prototype = {
__proto__: null, __proto__: null,
treeID: -1, treeID: "",
id: -1, id: -1,
isRootNode: false, isRootNode: false,
detach: function() { detach: function() {
this.rootImpl = null; this.rootImpl = null;
this.hostNode_ = null;
this.listeners = {__proto__: null}; this.listeners = {__proto__: null};
}, },
...@@ -359,12 +383,10 @@ AutomationNodeImpl.prototype = { ...@@ -359,12 +383,10 @@ AutomationNodeImpl.prototype = {
}, },
get parent() { get parent() {
if (!this.rootImpl) var info = GetParentID(this.treeID, this.id);
return undefined; if (!info)
if (this.hostNode_) return;
return this.hostNode_; return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
var parentID = GetParentID(this.treeID, this.id);
return this.rootImpl.get(parentID);
}, },
get htmlAttributes() { get htmlAttributes() {
...@@ -411,47 +433,40 @@ AutomationNodeImpl.prototype = { ...@@ -411,47 +433,40 @@ AutomationNodeImpl.prototype = {
}, },
get childTree() { get childTree() {
var childTreeID = GetIntAttribute(this.treeID, this.id, 'childTreeId'); var childTreeID = GetStringAttribute(this.treeID, this.id, 'childTreeId');
if (childTreeID) if (childTreeID)
return AutomationRootNodeImpl.get(childTreeID); return AutomationRootNodeImpl.get(childTreeID);
}, },
get firstChild() { get firstChild() {
if (!this.rootImpl) if (GetChildCount(this.treeID, this.id) == 0)
return undefined;
if (this.childTree)
return this.childTree;
if (!GetChildCount(this.treeID, this.id))
return undefined; return undefined;
var firstChildID = GetChildIDAtIndex(this.treeID, this.id, 0); var info = GetChildIDAtIndex(this.treeID, this.id, 0);
return this.rootImpl.get(firstChildID); if (info)
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
}, },
get lastChild() { get lastChild() {
if (!this.rootImpl)
return undefined;
if (this.childTree)
return this.childTree;
var count = GetChildCount(this.treeID, this.id); var count = GetChildCount(this.treeID, this.id);
if (!count) if (count == 0)
return undefined; return;
var lastChildID = GetChildIDAtIndex(this.treeID, this.id, count - 1);
return this.rootImpl.get(lastChildID); var info = GetChildIDAtIndex(this.treeID, this.id, count - 1);
if (info)
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
}, },
get children() { get children() {
if (!this.rootImpl) var info = GetChildIds(this.treeID, this.id);
if (!info)
return []; return [];
if (this.childTree)
return [this.childTree];
var children = []; var children = [];
var childIds = GetChildIds(this.treeID, this.id); for (var i = 0; i < info.nodeIds.length; ++i) {
for (var i = 0; i < childIds.length; ++i) { var childID = info.nodeIds[i];
var childID = childIds[i]; var child = AutomationRootNodeImpl.getNodeFromTree(info.treeId, childID);
var child = this.rootImpl.get(childID); if (child)
$Array.push(children, child); $Array.push(children, child);
} }
return children; return children;
}, },
...@@ -462,8 +477,9 @@ AutomationNodeImpl.prototype = { ...@@ -462,8 +477,9 @@ AutomationNodeImpl.prototype = {
return undefined; return undefined;
parent = privates(parent).impl; parent = privates(parent).impl;
var indexInParent = GetIndexInParent(this.treeID, this.id); var indexInParent = GetIndexInParent(this.treeID, this.id);
return this.rootImpl.get( var info = GetChildIDAtIndex(parent.treeID, parent.id, indexInParent - 1);
GetChildIDAtIndex(parent.treeID, parent.id, indexInParent - 1)); if (info)
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
}, },
get nextSibling() { get nextSibling() {
...@@ -472,8 +488,9 @@ AutomationNodeImpl.prototype = { ...@@ -472,8 +488,9 @@ AutomationNodeImpl.prototype = {
return undefined; return undefined;
parent = privates(parent).impl; parent = privates(parent).impl;
var indexInParent = GetIndexInParent(this.treeID, this.id); var indexInParent = GetIndexInParent(this.treeID, this.id);
return this.rootImpl.get( var info = GetChildIDAtIndex(parent.treeID, parent.id, indexInParent + 1);
GetChildIDAtIndex(parent.treeID, parent.id, indexInParent + 1)); if (info)
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
}, },
get nameFrom() { get nameFrom() {
...@@ -508,6 +525,26 @@ AutomationNodeImpl.prototype = { ...@@ -508,6 +525,26 @@ AutomationNodeImpl.prototype = {
return GetDefaultActionVerb(this.treeID, this.id); return GetDefaultActionVerb(this.treeID, this.id);
}, },
get tableCellColumnHeaders() {
var ids = GetTableCellColumnHeaders(this.treeID, this.id);
if (ids && this.rootImpl) {
var result = [];
for (var i = 0; i < ids.length; i++)
result.push(this.rootImpl.get(ids[i]));
return result;
}
},
get tableCellRowHeaders() {
var id = GetTableCellRowHeaders(this.treeID, this.id);
if (ids && this.rootImpl) {
var result = [];
for (var i = 0; i < ids.length; i++)
result.push(this.rootImpl.get(ids[i]));
return result;
}
},
doDefault: function() { doDefault: function() {
this.performAction_('doDefault'); this.performAction_('doDefault');
}, },
...@@ -648,6 +685,17 @@ AutomationNodeImpl.prototype = { ...@@ -648,6 +685,17 @@ AutomationNodeImpl.prototype = {
return this.matchInternal_(params); return this.matchInternal_(params);
}, },
getNextTextMatch: function(searchStr, backward) {
var info = GetNextTextMatch(this.treeID, this.id, searchStr, backward);
if (!info)
return;
var impl = privates(AutomationRootNodeImpl.get(info.treeId)).impl;
if (impl)
return impl.get(info.nodeId);
},
addEventListener: function(eventType, callback, capture) { addEventListener: function(eventType, callback, capture) {
this.removeEventListener(eventType, callback); this.removeEventListener(eventType, callback);
if (!this.listeners[eventType]) if (!this.listeners[eventType])
...@@ -702,26 +750,25 @@ AutomationNodeImpl.prototype = { ...@@ -702,26 +750,25 @@ AutomationNodeImpl.prototype = {
toString: function() { toString: function() {
var parentID = GetParentID(this.treeID, this.id); var parentID = GetParentID(this.treeID, this.id);
var childTreeID = GetIntAttribute(this.treeID, this.id, 'childTreeId'); parentID = parentID ? parentID.nodeId : null;
var childTreeID = GetStringAttribute(this.treeID, this.id, 'childTreeId');
var count = GetChildCount(this.treeID, this.id); var count = GetChildCount(this.treeID, this.id);
var childIDs = []; var childIDs = [];
for (var i = 0; i < count; ++i) { for (var i = 0; i < count; ++i) {
var childID = GetChildIDAtIndex(this.treeID, this.id, i); var childID = GetChildIDAtIndex(this.treeID, this.id, i).nodeId;
$Array.push(childIDs, childID); $Array.push(childIDs, childID);
} }
var name = GetStringAttribute(this.treeID, this.id, 'name');
var result = 'node id=' + this.id + var result = 'node id=' + this.id +
' role=' + this.role + ' role=' + this.role +
' state=' + $JSON.stringify(this.state) + ' state=' + $JSON.stringify(this.state) +
' parentID=' + parentID + ' parentID=' + parentID +
' childIds=' + $JSON.stringify(childIDs); ' childIds=' + $JSON.stringify(childIDs);
if (this.hostNode_) {
var hostNodeImpl = privates(this.hostNode_).impl;
result += ' host treeID=' + hostNodeImpl.treeID +
' host nodeID=' + hostNodeImpl.id;
}
if (childTreeID) if (childTreeID)
result += ' childTreeID=' + childTreeID; result += ' childTreeID=' + childTreeID;
if (name)
result += ' name=' + name;
return result; return result;
}, },
...@@ -899,6 +946,7 @@ var stringAttributes = [ ...@@ -899,6 +946,7 @@ var stringAttributes = [
'display', 'display',
'htmlTag', 'htmlTag',
'imageDataUrl', 'imageDataUrl',
'innerHtml',
'language', 'language',
'liveRelevant', 'liveRelevant',
'liveStatus', 'liveStatus',
...@@ -950,7 +998,7 @@ var intAttributes = [ ...@@ -950,7 +998,7 @@ var intAttributes = [
// Int attribute, relation property to expose, reverse relation to expose. // Int attribute, relation property to expose, reverse relation to expose.
var nodeRefAttributes = [ var nodeRefAttributes = [
['activedescendantId', 'activeDescendant', null], ['activedescendantId', 'activeDescendant', 'activeDescendantFor'],
['detailsId', 'details', 'detailsFor'], ['detailsId', 'details', 'detailsFor'],
['errorMessageId', 'errorMessage', 'errorMessageFor'], ['errorMessageId', 'errorMessage', 'errorMessageFor'],
['inPageLinkTargetId', 'inPageLinkTarget', null], ['inPageLinkTargetId', 'inPageLinkTarget', null],
...@@ -1149,7 +1197,6 @@ function AutomationRootNodeImpl(treeID) { ...@@ -1149,7 +1197,6 @@ function AutomationRootNodeImpl(treeID) {
$Function.call(AutomationNodeImpl, this, this); $Function.call(AutomationNodeImpl, this, this);
this.treeID = treeID; this.treeID = treeID;
this.axNodeDataCache_ = {__proto__: null}; this.axNodeDataCache_ = {__proto__: null};
this.actionRequestIDToCallback_ = {__proto__: null};
} }
utils.defineProperty(AutomationRootNodeImpl, 'idToAutomationRootNode_', utils.defineProperty(AutomationRootNodeImpl, 'idToAutomationRootNode_',
...@@ -1168,10 +1215,29 @@ utils.defineProperty(AutomationRootNodeImpl, 'getOrCreate', function(treeID) { ...@@ -1168,10 +1215,29 @@ utils.defineProperty(AutomationRootNodeImpl, 'getOrCreate', function(treeID) {
return result; return result;
}); });
utils.defineProperty(
AutomationRootNodeImpl, 'getNodeFromTree', function(treeId, nodeId) {
var impl = privates(AutomationRootNodeImpl.get(treeId)).impl;
if (impl)
return impl.get(nodeId);
});
utils.defineProperty(AutomationRootNodeImpl, 'destroy', function(treeID) { utils.defineProperty(AutomationRootNodeImpl, 'destroy', function(treeID) {
delete AutomationRootNodeImpl.idToAutomationRootNode_[treeID]; delete AutomationRootNodeImpl.idToAutomationRootNode_[treeID];
}); });
/**
* A counter keeping track of IDs to use for mapping action requests to
* their callback function.
*/
AutomationRootNodeImpl.actionRequestCounter = 0;
/**
* A map from a request ID to the corresponding callback function to call
* when the action response event is received.
*/
AutomationRootNodeImpl.actionRequestIDToCallback = {};
AutomationRootNodeImpl.prototype = { AutomationRootNodeImpl.prototype = {
__proto__: AutomationNodeImpl.prototype, __proto__: AutomationNodeImpl.prototype,
...@@ -1181,16 +1247,9 @@ AutomationRootNodeImpl.prototype = { ...@@ -1181,16 +1247,9 @@ AutomationRootNodeImpl.prototype = {
isRootNode: true, isRootNode: true,
/** /**
* @type {number} * @type {string}
*/ */
treeID: -1, treeID: "",
/**
* The parent of this node from a different tree.
* @type {?AutomationNode}
* @private
*/
hostNode_: null,
/** /**
* A map from id to AutomationNode. * A map from id to AutomationNode.
...@@ -1199,10 +1258,6 @@ AutomationRootNodeImpl.prototype = { ...@@ -1199,10 +1258,6 @@ AutomationRootNodeImpl.prototype = {
*/ */
axNodeDataCache_: null, axNodeDataCache_: null,
actionRequestCounter_: 0,
actionRequestIDToCallback_: null,
get id() { get id() {
var result = GetRootID(this.treeID); var result = GetRootID(this.treeID);
...@@ -1214,10 +1269,6 @@ AutomationRootNodeImpl.prototype = { ...@@ -1214,10 +1269,6 @@ AutomationRootNodeImpl.prototype = {
return result; return result;
}, },
get chromeChannel() {
return GetStringAttribute(this.treeID, this.id, 'chromeChannel');
},
get docUrl() { get docUrl() {
return GetDocURL(this.treeID); return GetDocURL(this.treeID);
}, },
...@@ -1281,7 +1332,7 @@ AutomationRootNodeImpl.prototype = { ...@@ -1281,7 +1332,7 @@ AutomationRootNodeImpl.prototype = {
if (id == this.id) if (id == this.id)
return this.wrapper; return this.wrapper;
var obj = this.axNodeDataCache_[id]; var obj = this.axNodeDataCache_[id];
if (obj) if (obj)
return obj; return obj;
...@@ -1310,10 +1361,6 @@ AutomationRootNodeImpl.prototype = { ...@@ -1310,10 +1361,6 @@ AutomationRootNodeImpl.prototype = {
this.detach(); this.detach();
}, },
setHostNode(hostNode) {
this.hostNode_ = hostNode;
},
onAccessibilityEvent: function(eventParams) { onAccessibilityEvent: function(eventParams) {
var targetNode = this.get(eventParams.targetID); var targetNode = this.get(eventParams.targetID);
if (targetNode) { if (targetNode) {
...@@ -1334,14 +1381,15 @@ AutomationRootNodeImpl.prototype = { ...@@ -1334,14 +1381,15 @@ AutomationRootNodeImpl.prototype = {
}, },
addActionResultCallback: function(callback) { addActionResultCallback: function(callback) {
this.actionRequestIDToCallback_[++this.actionRequestCounter_] = callback; AutomationRootNodeImpl.actionRequestIDToCallback[
return this.actionRequestCounter_; ++AutomationRootNodeImpl.actionRequestCounter] = callback;
return AutomationRootNodeImpl.actionRequestCounter;
}, },
onActionResult: function(requestID, result) { onActionResult: function(requestID, result) {
if (requestID in this.actionRequestIDToCallback_) { if (requestID in AutomationRootNodeImpl.actionRequestIDToCallback) {
this.actionRequestIDToCallback_[requestID](result); AutomationRootNodeImpl.actionRequestIDToCallback[requestID](result);
delete this.actionRequestIDToCallback_[requestID]; delete AutomationRootNodeImpl.actionRequestIDToCallback[requestID];
} }
}, },
...@@ -1374,6 +1422,7 @@ utils.expose(AutomationNode, AutomationNodeImpl, { ...@@ -1374,6 +1422,7 @@ utils.expose(AutomationNode, AutomationNodeImpl, {
'findAll', 'findAll',
'focus', 'focus',
'getImageData', 'getImageData',
'getNextTextMatch',
'hitTest', 'hitTest',
'hitTestWithReply', 'hitTestWithReply',
'makeVisible', 'makeVisible',
...@@ -1427,6 +1476,8 @@ utils.expose(AutomationNode, AutomationNodeImpl, { ...@@ -1427,6 +1476,8 @@ utils.expose(AutomationNode, AutomationNodeImpl, {
'customActions', 'customActions',
'standardActions', 'standardActions',
'unclippedLocation', 'unclippedLocation',
'tableCellColumnHeaders',
'tableCellRowHeaders',
]), ]),
}); });
...@@ -1436,7 +1487,6 @@ function AutomationRootNode() { ...@@ -1436,7 +1487,6 @@ function AutomationRootNode() {
utils.expose(AutomationRootNode, AutomationRootNodeImpl, { utils.expose(AutomationRootNode, AutomationRootNodeImpl, {
superclass: AutomationNode, superclass: AutomationNode,
readonly: [ readonly: [
'chromeChannel',
'docTitle', 'docTitle',
'docUrl', 'docUrl',
'docLoaded', 'docLoaded',
......
...@@ -37,7 +37,7 @@ window.automationUtil = function() {}; ...@@ -37,7 +37,7 @@ window.automationUtil = function() {};
// TODO(aboxhall): Look into using WeakMap // TODO(aboxhall): Look into using WeakMap
var idToCallback = {}; var idToCallback = {};
var DESKTOP_TREE_ID = 0; var DESKTOP_TREE_ID = "0";
automationUtil.storeTreeCallback = function(id, callback) { automationUtil.storeTreeCallback = function(id, callback) {
if (!callback) if (!callback)
...@@ -198,14 +198,12 @@ automation.registerCustomHook(function(bindingsAPI) { ...@@ -198,14 +198,12 @@ automation.registerCustomHook(function(bindingsAPI) {
}); });
automationInternal.onChildTreeID.addListener(function(treeID, automationInternal.onChildTreeID.addListener(function(childTreeId) {
nodeID) { var targetTree = AutomationRootNode.get(childTreeId);
var tree = AutomationRootNode.getOrCreate(treeID);
if (!tree)
return;
var node = privates(tree).impl.get(nodeID); // If the tree is already loded, or if we previously requested it be loaded
if (!node) // (i.e. have a callback for it), don't try to do so again.
if (targetTree || idToCallback[childTreeId])
return; return;
// A WebView in the desktop tree has a different AX tree as its child. // A WebView in the desktop tree has a different AX tree as its child.
...@@ -213,30 +211,11 @@ automationInternal.onChildTreeID.addListener(function(treeID, ...@@ -213,30 +211,11 @@ automationInternal.onChildTreeID.addListener(function(treeID,
// currently have cached, explicitly request that AX tree from the // currently have cached, explicitly request that AX tree from the
// browser process and set up a callback when it loads to attach that // browser process and set up a callback when it loads to attach that
// tree as a child of this node and fire appropriate events. // tree as a child of this node and fire appropriate events.
var childTreeID = GetIntAttribute(treeID, nodeID, 'childTreeId'); automationUtil.storeTreeCallback(childTreeId, function(root) {
if (!childTreeID) privates(root).impl.dispatchEvent('loadComplete', 'page');
return; }, true);
var subroot = AutomationRootNode.get(childTreeID);
if (!subroot || subroot.role == 'unknown') {
automationUtil.storeTreeCallback(childTreeID, function(root) {
// Return early if the root has already been attached.
if (root.parent)
return;
privates(root).impl.setHostNode(node); automationInternal.enableFrame(childTreeId);
if (root.docLoaded) {
privates(root).impl.dispatchEvent('loadComplete', 'page');
}
privates(node).impl.dispatchEvent('childrenChanged', 'none');
});
automationInternal.enableFrame(childTreeID);
} else {
privates(subroot).impl.setHostNode(node);
}
}); });
automationInternal.onTreeChange.addListener(function(observerID, automationInternal.onTreeChange.addListener(function(observerID,
...@@ -259,7 +238,7 @@ automationInternal.onTreeChange.addListener(function(observerID, ...@@ -259,7 +238,7 @@ automationInternal.onTreeChange.addListener(function(observerID,
observer({target: node, type: changeType}); observer({target: node, type: changeType});
} catch (e) { } catch (e) {
exceptionHandler.handle('Error in tree change observer for ' + exceptionHandler.handle('Error in tree change observer for ' +
treeChange.type, e); changeType, e);
} }
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment