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
class AutomationMessageFilter : public IPC::MessageFilter {
public:
explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner)
: owner_(owner),
removed_(false) {
: owner_(owner), removed_(false) {
DCHECK(owner);
content::RenderThread::Get()->AddFilter(this);
task_runner_ = base::ThreadTaskRunnerHandle::Get();
......@@ -390,28 +389,23 @@ class AutomationMessageFilter : public IPC::MessageFilter {
bool OnMessageReceived(const IPC::Message& message) override {
task_runner_->PostTask(
FROM_HERE,
base::Bind(
&AutomationMessageFilter::OnMessageReceivedOnRenderThread,
this, message));
base::Bind(&AutomationMessageFilter::OnMessageReceivedOnRenderThread,
this, message));
// Always return false in case there are multiple
// AutomationInternalCustomBindings instances attached to the same thread.
return false;
}
void OnFilterRemoved() override {
removed_ = true;
}
void OnFilterRemoved() override { removed_ = true; }
private:
private:
void OnMessageReceivedOnRenderThread(const IPC::Message& message) {
if (owner_)
owner_->OnMessageReceived(message);
}
~AutomationMessageFilter() override {
Remove();
}
~AutomationMessageFilter() override { Remove(); }
void Remove() {
if (!removed_) {
......@@ -441,8 +435,8 @@ AutomationInternalCustomBindings::AutomationInternalCustomBindings(
if (context && context->extension()) {
const GURL background_page_url =
extensions::BackgroundInfo::GetBackgroundURL(context->extension());
should_ignore_context_ = background_page_url != "" &&
background_page_url != context->url();
should_ignore_context_ =
background_page_url != "" && background_page_url != context->url();
}
}
......@@ -466,7 +460,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
ROUTE_FUNCTION(GetFocus);
ROUTE_FUNCTION(GetHtmlAttributes);
ROUTE_FUNCTION(GetState);
#undef ROUTE_FUNCTION
#undef ROUTE_FUNCTION
// Bindings that take a Tree ID and return a property of the tree.
......@@ -1637,8 +1631,8 @@ bool AutomationInternalCustomBindings::SendTreeChangeEvent(
bool has_filter = false;
if (tree_change_observer_overall_filter_ &
(1 <<
api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES)) {
(1
<< api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES)) {
if (node->data().HasStringAttribute(
ax::mojom::StringAttribute::kContainerLiveStatus) ||
node->data().role == ax::mojom::Role::kAlert ||
......
......@@ -178,6 +178,7 @@
imageMap,
inlineTextBox,
inputTime,
keyboard,
labelText,
layoutTable,
layoutTableCell,
......@@ -191,7 +192,6 @@
listBoxOption,
listItem,
listMarker,
locationBar,
log,
main,
mark,
......@@ -356,9 +356,11 @@
uninitialized,
attribute,
attributeExplicitlyEmpty,
caption,
contents,
placeholder,
relatedElement,
title,
value
};
......@@ -576,6 +578,9 @@
// applicable
AutomationNode? activeDescendant;
// Reverse relationship for active descendant.
AutomationNode[]? activeDescendantFor;
// The target of an in-page link.
AutomationNode? inPageLinkTarget;
......@@ -604,10 +609,10 @@
AutomationNode[]? labelFor;
// The column header nodes for a table cell.
AutomationNode? tableCellColumnHeaders;
AutomationNode[]? tableCellColumnHeaders;
// The row header nodes for a table cell.
AutomationNode? tableCellRowHeaders;
AutomationNode[]? tableCellRowHeaders;
// An array of standard actions available on this node.
ActionType[]? standardActions;
......@@ -652,6 +657,9 @@
long? scrollYMin;
long? scrollYMax;
// Indicates whether this node is scrollable.
boolean? scrollable;
//
// Editable text field attributes.
//
......@@ -667,9 +675,6 @@
// The input type, like email or number.
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.
long[] markerStarts;
......@@ -855,6 +860,9 @@
// 'false' | 'true' | 'mixed'
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.
long? color;
......
......@@ -103,7 +103,7 @@ namespace automationInternal {
// Returns the accessibility tree id of the web contents who's accessibility
// was enabled using enableTab().
callback EnableTabCallback = void(long tree_id);
callback EnableTabCallback = void(DOMString tree_id);
// Callback called when enableDesktop() returns.
callback EnableDesktopCallback = void();
......@@ -119,7 +119,7 @@ namespace automationInternal {
EnableTabCallback callback);
// Enable automation of the frame with the given tree id.
static void enableFrame(DOMString treeId);
static void enableFrame(DOMString tree_id);
// Enables desktop automation.
static void enableDesktop(EnableDesktopCallback callback);
......@@ -144,7 +144,7 @@ namespace automationInternal {
long nodeID,
DOMString changeType);
static void onChildTreeID(DOMString treeID, long nodeID);
static void onChildTreeID(DOMString treeID);
static void onNodesRemoved(DOMString treeID, long[] nodeIDs);
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/no_destructor.h"
#include "chromecast/common/extensions_api/cast_extension_messages.h"
#include "chromecast/renderer/extensions/automation_internal_custom_bindings.h"
#include "ui/accessibility/ax_node.h"
......@@ -11,6 +12,12 @@ namespace cast {
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.
api::automation::EventType ToAutomationEvent(ax::mojom::Event event_type) {
switch (event_type) {
......@@ -193,7 +200,7 @@ api::automation::EventType ToAutomationEvent(
AutomationAXTreeWrapper::AutomationAXTreeWrapper(
ui::AXTreeID tree_id,
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
// initializer list because AXTree hasn't been initialized yet at that point.
SetTree(&tree_);
......@@ -205,12 +212,32 @@ AutomationAXTreeWrapper::~AutomationAXTreeWrapper() {
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(
const ExtensionMsg_AccessibilityEventBundleParams& event_bundle,
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) {
set_event_from(update.event_from);
deleted_node_ids_.clear();
did_send_tree_change_during_unserialization_ = false;
if (!tree_.Unserialize(update))
return false;
......@@ -218,7 +245,7 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents(
if (is_active_profile) {
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);
if (target) {
owner_->SendTreeChangeEvent(
......@@ -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.
if (!is_active_profile)
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.
for (auto targeted_event : *this) {
for (const auto& targeted_event : *this) {
api::automation::EventType event_type =
ToAutomationEvent(targeted_event.event_params.event);
if (IsEventTypeHandledByAXEventGenerator(event_type)) {
......@@ -248,7 +295,11 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents(
}
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 =
ToAutomationEvent(event.event_type);
......@@ -277,8 +328,8 @@ void AutomationAXTreeWrapper::OnNodeDataWillChange(
void AutomationAXTreeWrapper::OnNodeWillBeDeleted(ui::AXTree* tree,
ui::AXNode* node) {
AXEventGenerator::OnNodeWillBeDeleted(tree, node);
owner_->SendTreeChangeEvent(api::automation::TREE_CHANGE_TYPE_NODEREMOVED,
tree, node);
did_send_tree_change_during_unserialization_ |= owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_NODEREMOVED, tree, node);
deleted_node_ids_.push_back(node->id());
}
......@@ -292,16 +343,19 @@ void AutomationAXTreeWrapper::OnAtomicUpdateFinished(
ui::AXNode* node = change.node;
switch (change.type) {
case NODE_CREATED:
owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_NODECREATED, tree, node);
did_send_tree_change_during_unserialization_ |=
owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_NODECREATED, tree, node);
break;
case SUBTREE_CREATED:
owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_SUBTREECREATED, tree, node);
did_send_tree_change_during_unserialization_ |=
owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_SUBTREECREATED, tree, node);
break;
case NODE_CHANGED:
owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_NODECHANGED, tree, node);
did_send_tree_change_during_unserialization_ |=
owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_NODECHANGED, tree, node);
break;
// Unhandled.
case NODE_REPARENTED:
......@@ -311,8 +365,9 @@ void AutomationAXTreeWrapper::OnAtomicUpdateFinished(
}
for (int id : text_changed_node_ids_) {
owner_->SendTreeChangeEvent(api::automation::TREE_CHANGE_TYPE_TEXTCHANGED,
tree, tree->GetFromId(id));
did_send_tree_change_during_unserialization_ |= owner_->SendTreeChangeEvent(
api::automation::TREE_CHANGE_TYPE_TEXTCHANGED, tree,
tree->GetFromId(id));
}
text_changed_node_ids_.clear();
}
......@@ -324,6 +379,7 @@ bool AutomationAXTreeWrapper::IsEventTypeHandledByAXEventGenerator(
case api::automation::EVENT_TYPE_ACTIVEDESCENDANTCHANGED:
case api::automation::EVENT_TYPE_ARIAATTRIBUTECHANGED:
case api::automation::EVENT_TYPE_CHECKEDSTATECHANGED:
case api::automation::EVENT_TYPE_CHILDRENCHANGED:
case api::automation::EVENT_TYPE_DOCUMENTSELECTIONCHANGED:
case api::automation::EVENT_TYPE_DOCUMENTTITLECHANGED:
case api::automation::EVENT_TYPE_EXPANDEDCHANGED:
......@@ -332,6 +388,9 @@ bool AutomationAXTreeWrapper::IsEventTypeHandledByAXEventGenerator(
case api::automation::EVENT_TYPE_LIVEREGIONCREATED:
case api::automation::EVENT_TYPE_LOADCOMPLETE:
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_SELECTEDCHILDRENCHANGED:
return true;
......@@ -365,24 +424,20 @@ bool AutomationAXTreeWrapper::IsEventTypeHandledByAXEventGenerator(
case api::automation::EVENT_TYPE_MOUSEMOVED:
case api::automation::EVENT_TYPE_MOUSEPRESSED:
case api::automation::EVENT_TYPE_MOUSERELEASED:
case api::automation::EVENT_TYPE_SCROLLEDTOANCHOR:
case api::automation::EVENT_TYPE_WINDOWACTIVATED:
case api::automation::EVENT_TYPE_WINDOWDEACTIVATED:
case api::automation::EVENT_TYPE_SCROLLEDTOANCHOR:
return false;
// These events might need to be migrated to AXEventGenerator.
case api::automation::EVENT_TYPE_ALERT:
case api::automation::EVENT_TYPE_BLUR:
case api::automation::EVENT_TYPE_CHILDRENCHANGED:
case api::automation::EVENT_TYPE_FOCUS:
case api::automation::EVENT_TYPE_IMAGEFRAMEUPDATED:
case api::automation::EVENT_TYPE_LOCATIONCHANGED:
case api::automation::EVENT_TYPE_MENUEND:
case api::automation::EVENT_TYPE_MENULISTITEMSELECTED:
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_TEXTCHANGED:
case api::automation::EVENT_TYPE_TEXTSELECTIONCHANGED:
......
......@@ -23,16 +23,14 @@ class AutomationAXTreeWrapper : public ui::AXEventGenerator {
AutomationInternalCustomBindings* owner);
~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::AXTree* tree() { return &tree_; }
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
// the AutomationAXTreeWrapper instance for the correct tree corresponding
// to this event. Unserializes the tree update and calls back to
......@@ -57,12 +55,15 @@ class AutomationAXTreeWrapper : public ui::AXEventGenerator {
bool IsEventTypeHandledByAXEventGenerator(api::automation::EventType) const;
ui::AXTreeID tree_id_;
int32_t host_node_id_;
ui::AXTree tree_;
AutomationInternalCustomBindings* owner_;
std::vector<int> deleted_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);
};
......
......@@ -10,7 +10,9 @@
#include <memory>
#include "base/bind.h"
#include "base/i18n/string_search.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "chromecast/common/extensions_api/cast_extension_messages.h"
......@@ -30,6 +32,7 @@
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_event.h"
#include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace extensions {
......@@ -105,22 +108,6 @@ static gfx::Rect ComputeGlobalNodeBounds(AutomationAXTreeWrapper* tree_wrapper,
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
// that takes a single input argument consisting of a Tree ID. Looks up
......@@ -172,11 +159,11 @@ class TreeIDWrapper : public base::RefCountedThreadSafe<TreeIDWrapper> {
// AutomationAXTreeWrapper and the AXNode and passes them to the function passed
// to the constructor.
//
typedef void (*NodeIDFunction)(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
ui::AXNode* node);
typedef std::function<void(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
ui::AXNode* node)>
NodeIDFunction;
class NodeIDWrapper : public base::RefCountedThreadSafe<NodeIDWrapper> {
public:
......@@ -330,6 +317,59 @@ class NodeIDPlusRangeWrapper
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
class AutomationMessageFilter : public IPC::MessageFilter {
......@@ -430,17 +470,21 @@ void AutomationInternalCustomBindings::AddRoutes() {
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::Integer::New(isolate, tree_wrapper->tree()->root()->id()));
});
RouteTreeIDFunction(
"GetDocURL", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::String::NewFromUtf8(
isolate, tree_wrapper->tree()->data().url.c_str()));
});
RouteTreeIDFunction("GetDocURL", [](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
result.Set(v8::String::NewFromUtf8(isolate,
tree_wrapper->tree()->data().url.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
});
RouteTreeIDFunction(
"GetDocTitle", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper) {
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(
"GetDocLoaded",
......@@ -505,16 +549,27 @@ void AutomationInternalCustomBindings::AddRoutes() {
RouteNodeIDFunction(
"GetParentID",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
if (node->parent())
result.Set(v8::Integer::New(isolate, node->parent()->id()));
[this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
ui::AXNode* parent = GetParent(node, &tree_wrapper);
if (parent) {
gin::DataObjectBuilder response(isolate);
response.Set("treeId", tree_wrapper->tree_id().ToString());
response.Set("nodeId", parent->id());
result.Set(response.Build());
}
});
RouteNodeIDFunction(
"GetChildCount",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
result.Set(v8::Integer::New(isolate, node->child_count()));
[this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
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(
"GetIndexInParent",
......@@ -525,29 +580,10 @@ void AutomationInternalCustomBindings::AddRoutes() {
RouteNodeIDFunction(
"GetRole", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
auto role = node->data().role;
auto mapped_role = role;
// These roles are remapped for simplicity in handling them in AT.
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()));
std::string role_name = ui::ToString(node->data().role);
result.Set(v8::String::NewFromUtf8(isolate, role_name.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
});
RouteNodeIDFunction(
"GetLocation",
......@@ -581,18 +617,24 @@ void AutomationInternalCustomBindings::AddRoutes() {
}
result.Set(array_result);
});
RouteNodeIDFunction("GetChildIDs", [](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
ui::AXNode* node) {
const std::vector<ui::AXNode*>& children = node->children();
v8::Local<v8::Array> array_result(v8::Array::New(isolate, children.size()));
for (size_t i = 0; i < children.size(); ++i) {
array_result->Set(static_cast<uint32_t>(i),
v8::Integer::New(isolate, children[i]->id()));
}
result.Set(array_result);
});
RouteNodeIDFunction(
"GetChildIDs",
[this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
std::vector<int> child_ids;
if (GetRootOfChildTree(&node, &tree_wrapper)) {
child_ids.push_back(node->id());
} else {
const std::vector<ui::AXNode*>& children = node->children();
for (size_t i = 0; i < children.size(); ++i)
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
// and return a property of the node.
......@@ -646,10 +688,8 @@ void AutomationInternalCustomBindings::AddRoutes() {
// Convert from local to global coordinates second, after subsetting,
// because the local to global conversion might involve matrix
// transformations.
// TODO(katie): Instead of removing clipping we could trim local_bounds
// to fit in the clipped space?
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));
});
......@@ -671,7 +711,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
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(
"GetBoolAttribute",
......@@ -728,7 +770,11 @@ void AutomationInternalCustomBindings::AddRoutes() {
if (!node->data().GetFloatAttribute(attribute, &attr_value))
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(
"GetIntListAttribute",
......@@ -775,7 +821,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
if (!node->data().GetHtmlAttribute(attribute_name.c_str(), &attr_value))
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(
"GetNameFrom",
......@@ -784,7 +832,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
ax::mojom::NameFrom name_from = static_cast<ax::mojom::NameFrom>(
node->data().GetIntAttribute(ax::mojom::IntAttribute::kNameFrom));
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,
v8::ReturnValue<v8::Value> result,
......@@ -892,7 +942,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
for (size_t i = 0; i < standard_actions.size(); i++) {
const v8::Maybe<bool>& did_set_value = actions_result->Set(
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;
if (!did_set_value.To(&did_set_value_result) || !did_set_value_result)
......@@ -909,7 +961,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
ax::mojom::IntAttribute::kCheckedState));
if (checked_state != ax::mojom::CheckedState::kNone) {
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(
......@@ -920,7 +974,9 @@ void AutomationInternalCustomBindings::AddRoutes() {
node->data().GetRestriction();
if (restriction != ax::mojom::Restriction::kNone) {
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(
......@@ -932,8 +988,76 @@ void AutomationInternalCustomBindings::AddRoutes() {
node->data().GetIntAttribute(
ax::mojom::IntAttribute::kDefaultActionVerb));
std::string default_action_verb_str = ui::ToString(default_action_verb);
result.Set(
v8::String::NewFromUtf8(isolate, default_action_verb_str.c_str()));
result.Set(v8::String::NewFromUtf8(isolate,
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(
} // clang-format on
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);
if (iter == tree_id_to_tree_wrapper_map_.end())
return nullptr;
......@@ -1241,52 +1365,124 @@ void AutomationInternalCustomBindings::UpdateOverallTreeChangeObserverFilter() {
ui::AXNode* AutomationInternalCustomBindings::GetParent(
ui::AXNode* node,
AutomationAXTreeWrapper** in_out_tree_wrapper) {
AutomationAXTreeWrapper** in_out_tree_wrapper) const {
if (node->parent())
return node->parent();
AutomationAXTreeWrapper* parent_tree_wrapper = nullptr;
ui::AXTreeID 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)
return nullptr;
// Try to use the cached host node from the most recent time this
// was called.
if ((*in_out_tree_wrapper)->host_node_id() > 0) {
ui::AXNode* parent = parent_tree_wrapper->tree()->GetFromId(
(*in_out_tree_wrapper)->host_node_id());
if (parent) {
ui::AXTreeID parent_child_tree_id =
ui::AXTreeID::FromString(parent->data().GetStringAttribute(
ax::mojom::StringAttribute::kChildTreeId));
if (parent_child_tree_id == (*in_out_tree_wrapper)->tree_id()) {
*in_out_tree_wrapper = parent_tree_wrapper;
return parent;
}
std::set<int32_t> host_node_ids =
parent_tree_wrapper->tree()->GetNodeIdsForChildTreeId(
(*in_out_tree_wrapper)->tree_id());
#if !defined(NDEBUG)
if (host_node_ids.size() > 1)
DLOG(WARNING) << "Multiple nodes claim the same child tree id.";
#endif
for (int32_t host_node_id : host_node_ids) {
ui::AXNode* host_node =
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.
ui::AXNode* parent = FindNodeWithChildTreeId(
parent_tree_wrapper->tree()->root(), (*in_out_tree_wrapper)->tree_id());
if (parent) {
(*in_out_tree_wrapper)->set_host_node_id(parent->id());
*in_out_tree_wrapper = parent_tree_wrapper;
return parent;
return nullptr;
}
bool AutomationInternalCustomBindings::GetRootOfChildTree(
ui::AXNode** in_out_node,
AutomationAXTreeWrapper** in_out_tree_wrapper) const {
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;
}
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 {
return context()->GetRenderFrame()->GetRenderView()->GetDeviceScaleFactor();
}
......@@ -1302,7 +1498,7 @@ void AutomationInternalCustomBindings::RouteNodeIDFunction(
const std::string& name,
NodeIDFunction 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(
......@@ -1322,6 +1518,15 @@ void AutomationInternalCustomBindings::RouteNodeIDPlusRangeFunction(
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(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() < 3 || !args[2]->IsNumber()) {
......@@ -1343,11 +1548,20 @@ void AutomationInternalCustomBindings::GetChildIDAtIndex(
return;
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;
else
child_id = node->children()[index]->id();
int child_id = node->children()[index]->id();
args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_id));
gin::DataObjectBuilder response(GetIsolate());
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(
params.new_location.transform.get());
}
void AutomationInternalCustomBindings::SendTreeChangeEvent(
bool AutomationInternalCustomBindings::SendTreeChangeEvent(
api::automation::TreeChangeType change_type,
ui::AXTree* tree,
ui::AXNode* node) {
// Don't send tree change events when it's not the active profile.
if (!is_active_profile_)
return;
return false;
// Always notify the custom bindings when there's a node with a child tree
// ID that might need to be loaded.
if (node->data().HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId))
SendChildTreeIDEvent(tree, node);
// Notify custom bindings when there's an unloaded tree; js will enable the
// renderer and wait for it to load.
std::string child_tree_id_str;
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;
if (tree_change_observer_overall_filter_ &
......@@ -1416,7 +1636,8 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent(
<< api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES)) {
if (node->data().HasStringAttribute(
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;
}
}
......@@ -1433,14 +1654,14 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent(
has_filter = true;
if (!has_filter)
return;
return false;
auto iter = axtree_to_tree_wrapper_map_.find(tree);
if (iter == axtree_to_tree_wrapper_map_.end())
return;
return false;
ui::AXTreeID tree_id = iter->second->tree_id();
bool did_send_event = false;
for (const auto& observer : tree_change_observers_) {
switch (observer.filter) {
case api::automation::TREE_CHANGE_OBSERVER_FILTER_NOTREECHANGES:
......@@ -1449,7 +1670,8 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent(
case api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES:
if (!node->data().HasStringAttribute(
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;
}
break;
......@@ -1462,6 +1684,7 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent(
break;
}
did_send_event = true;
base::ListValue args;
args.AppendInteger(observer.id);
args.AppendString(tree_id.ToString());
......@@ -1470,12 +1693,14 @@ void AutomationInternalCustomBindings::SendTreeChangeEvent(
bindings_system_->DispatchEventInContext("automationInternal.onTreeChange",
&args, nullptr, context());
}
return did_send_event;
}
void AutomationInternalCustomBindings::SendAutomationEvent(
ui::AXTreeID tree_id,
const gfx::Point& mouse_location,
ui::AXEvent& event,
const ui::AXEvent& event,
api::automation::EventType event_type) {
auto event_params = std::make_unique<base::DictionaryValue>();
event_params->SetString("treeID", tree_id.ToString());
......@@ -1491,17 +1716,10 @@ void AutomationInternalCustomBindings::SendAutomationEvent(
"automationInternal.onAccessibilityEvent", &args, nullptr, context());
}
void AutomationInternalCustomBindings::SendChildTreeIDEvent(ui::AXTree* tree,
ui::AXNode* node) {
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();
void AutomationInternalCustomBindings::SendChildTreeIDEvent(
ui::AXTreeID child_tree_id) {
base::ListValue args;
args.AppendString(tree_id.ToString());
args.AppendInteger(node->id());
args.AppendString(child_tree_id.ToString());
bindings_system_->DispatchEventInContext("automationInternal.onChildTreeID",
&args, nullptr, context());
}
......
......@@ -53,14 +53,26 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
void OnMessageReceived(const IPC::Message& message);
AutomationAXTreeWrapper* GetAutomationAXTreeWrapperFromTreeID(
ui::AXTreeID tree_id);
ui::AXTreeID tree_id) const;
// 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
// node of the parent tree and |in_out_tree_wrapper| will be updated to
// point to that parent tree.
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 {
return ObjectBackedNativeHandler::context();
......@@ -69,12 +81,12 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
float GetDeviceScaleFactor() const;
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::AXNode* node);
void SendAutomationEvent(ui::AXTreeID tree_id,
const gfx::Point& mouse_location,
ui::AXEvent& event,
const ui::AXEvent& event,
api::automation::EventType event_type);
private:
......@@ -98,7 +110,7 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
// Called when an accessibility tree is destroyed and needs to be
// removed from our cache.
// Args: int ax_tree_id
// Args: string ax_tree_id
void DestroyAccessibilityTree(
const v8::FunctionCallbackInfo<v8::Value>& args);
......@@ -124,11 +136,10 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
void RouteNodeIDFunction(
const std::string& name,
void (*callback)(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
ui::AXNode* node));
std::function<void(v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
ui::AXNode* node)> callback);
void RouteNodeIDPlusAttributeFunction(
const std::string& name,
void (*callback)(v8::Isolate* isolate,
......@@ -144,19 +155,27 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
ui::AXNode* node,
int start,
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.
//
// 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);
// 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.
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.
void GetState(const v8::FunctionCallbackInfo<v8::Value>& args);
......@@ -173,7 +192,7 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
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>>
tree_id_to_tree_wrapper_map_;
......
......@@ -14,74 +14,74 @@ var natives = requireNative('automationInternal');
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.
*/
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.
*/
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.
*/
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.
*/
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).
*/
var 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.
*/
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.
*/
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.
*/
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.
*/
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.
*/
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.
*/
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.
* @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.
......@@ -89,7 +89,7 @@ var GetFocusAffinity = natives.GetFocusAffinity;
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.
* @return {?number} The number of children of the node, or undefined if
* the tree or node wasn't found.
......@@ -97,7 +97,7 @@ var GetParentID = natives.GetParentID;
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} childIndex An index of a child of this node.
* @return {?number} The id of the child at the given index, or undefined
......@@ -106,7 +106,7 @@ var GetChildCount = natives.GetChildCount;
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.
* @return {?number} The ids of the children of the node, or undefined
* if the tree or node wasn't found.
......@@ -114,14 +114,14 @@ var GetChildIDAtIndex = natives.GetChildIDAtIndex;
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.
* @return {?Object} An object mapping html attributes to values.
*/
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.
* @return {?number} The index of this node in its parent, or undefined if
* the tree or node or node parent wasn't found.
......@@ -129,7 +129,7 @@ var GetHtmlAttributes = natives.GetHtmlAttributes;
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.
* @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.
......@@ -137,7 +137,7 @@ var GetIndexInParent = natives.GetIndexInParent;
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.
* @return {string} The restriction, one of
* "disabled", "readOnly" or undefined if enabled or other object not disabled
......@@ -145,14 +145,14 @@ var GetState = natives.GetState;
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.
* @return {string} The checked state, as undefined, "true", "false" or "mixed".
*/
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.
* @return {string} The role of the node, or undefined if the tree or
* node wasn't found.
......@@ -160,7 +160,7 @@ var GetChecked = natives.GetChecked;
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.
* @return {?automation.Rect} The location of the node, or undefined if
* the tree or node wasn't found.
......@@ -168,7 +168,7 @@ var GetRole = natives.GetRole;
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} startIndex The start index of the range.
* @param {number} endIndex The end index of the range.
......@@ -179,7 +179,7 @@ var GetLocation = natives.GetLocation;
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.
* @return {?automation.Rect} The unclipped location of the node, or
* undefined if the tree or node wasn't found.
......@@ -187,7 +187,7 @@ var GetBoundsForRange = natives.GetBoundsForRange;
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.
* @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
......@@ -197,7 +197,7 @@ var GetLineStartOffsets = requireNative(
'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 {string} attr The name of a string attribute.
* @return {?string} The value of this attribute, or undefined if the tree,
......@@ -206,7 +206,7 @@ var GetLineStartOffsets = requireNative(
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 {string} attr The name of an attribute.
* @return {?boolean} The value of this attribute, or undefined if the tree,
......@@ -215,7 +215,7 @@ var GetStringAttribute = natives.GetStringAttribute;
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 {string} attr The name of an attribute.
* @return {?number} The value of this attribute, or undefined if the tree,
......@@ -224,7 +224,7 @@ var GetBoolAttribute = natives.GetBoolAttribute;
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 {string} attr The name of an attribute.
* @return {?Array<number>} The ids of nodes who have a relationship pointing
......@@ -234,7 +234,7 @@ var 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 {string} attr The name of an attribute.
* @return {?number} The value of this attribute, or undefined if the tree,
......@@ -243,7 +243,7 @@ var GetIntAttributeReverseRelations =
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 {string} attr The name of an attribute.
* @return {?Array<number>} The value of this attribute, or undefined
......@@ -253,7 +253,7 @@ var 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 {string} attr The name of an attribute.
* @return {?Array<number>} The ids of nodes who have a relationship pointing
......@@ -263,7 +263,7 @@ var 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 {string} attr The name of an HTML attribute.
* @return {?string} The value of this attribute, or undefined if the tree,
......@@ -272,42 +272,42 @@ var GetIntListAttributeReverseRelations =
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.
* @return {automation.NameFromType} The source of the node's name.
*/
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.
* @return {boolean}
*/
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.
* @return {boolean}
*/
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.
* @return {boolean}
*/
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.
* @return {boolean}
*/
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.
* @return {?Array<automation.CustomAction>} List of custom actions of the
* node.
......@@ -315,19 +315,45 @@ var GetLineThrough = natives.GetLineThrough;
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.
* @return {?Array<string>} List of standard actions of the node.
*/
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.
* @return {automation.NameFromType} The source of the node's name.
*/
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 utils = require('utils');
......@@ -338,19 +364,17 @@ var utils = require('utils');
*/
function AutomationNodeImpl(root) {
this.rootImpl = root;
this.hostNode_ = null;
this.listeners = {__proto__: null};
}
AutomationNodeImpl.prototype = {
__proto__: null,
treeID: -1,
treeID: "",
id: -1,
isRootNode: false,
detach: function() {
this.rootImpl = null;
this.hostNode_ = null;
this.listeners = {__proto__: null};
},
......@@ -359,12 +383,10 @@ AutomationNodeImpl.prototype = {
},
get parent() {
if (!this.rootImpl)
return undefined;
if (this.hostNode_)
return this.hostNode_;
var parentID = GetParentID(this.treeID, this.id);
return this.rootImpl.get(parentID);
var info = GetParentID(this.treeID, this.id);
if (!info)
return;
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
},
get htmlAttributes() {
......@@ -411,47 +433,40 @@ AutomationNodeImpl.prototype = {
},
get childTree() {
var childTreeID = GetIntAttribute(this.treeID, this.id, 'childTreeId');
var childTreeID = GetStringAttribute(this.treeID, this.id, 'childTreeId');
if (childTreeID)
return AutomationRootNodeImpl.get(childTreeID);
},
get firstChild() {
if (!this.rootImpl)
return undefined;
if (this.childTree)
return this.childTree;
if (!GetChildCount(this.treeID, this.id))
if (GetChildCount(this.treeID, this.id) == 0)
return undefined;
var firstChildID = GetChildIDAtIndex(this.treeID, this.id, 0);
return this.rootImpl.get(firstChildID);
var info = GetChildIDAtIndex(this.treeID, this.id, 0);
if (info)
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
},
get lastChild() {
if (!this.rootImpl)
return undefined;
if (this.childTree)
return this.childTree;
var count = GetChildCount(this.treeID, this.id);
if (!count)
return undefined;
var lastChildID = GetChildIDAtIndex(this.treeID, this.id, count - 1);
return this.rootImpl.get(lastChildID);
if (count == 0)
return;
var info = GetChildIDAtIndex(this.treeID, this.id, count - 1);
if (info)
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
},
get children() {
if (!this.rootImpl)
var info = GetChildIds(this.treeID, this.id);
if (!info)
return [];
if (this.childTree)
return [this.childTree];
var children = [];
var childIds = GetChildIds(this.treeID, this.id);
for (var i = 0; i < childIds.length; ++i) {
var childID = childIds[i];
var child = this.rootImpl.get(childID);
$Array.push(children, child);
for (var i = 0; i < info.nodeIds.length; ++i) {
var childID = info.nodeIds[i];
var child = AutomationRootNodeImpl.getNodeFromTree(info.treeId, childID);
if (child)
$Array.push(children, child);
}
return children;
},
......@@ -462,8 +477,9 @@ AutomationNodeImpl.prototype = {
return undefined;
parent = privates(parent).impl;
var indexInParent = GetIndexInParent(this.treeID, this.id);
return this.rootImpl.get(
GetChildIDAtIndex(parent.treeID, parent.id, indexInParent - 1));
var info = GetChildIDAtIndex(parent.treeID, parent.id, indexInParent - 1);
if (info)
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
},
get nextSibling() {
......@@ -472,8 +488,9 @@ AutomationNodeImpl.prototype = {
return undefined;
parent = privates(parent).impl;
var indexInParent = GetIndexInParent(this.treeID, this.id);
return this.rootImpl.get(
GetChildIDAtIndex(parent.treeID, parent.id, indexInParent + 1));
var info = GetChildIDAtIndex(parent.treeID, parent.id, indexInParent + 1);
if (info)
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
},
get nameFrom() {
......@@ -508,6 +525,26 @@ AutomationNodeImpl.prototype = {
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() {
this.performAction_('doDefault');
},
......@@ -648,6 +685,17 @@ AutomationNodeImpl.prototype = {
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) {
this.removeEventListener(eventType, callback);
if (!this.listeners[eventType])
......@@ -702,26 +750,25 @@ AutomationNodeImpl.prototype = {
toString: function() {
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 childIDs = [];
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);
}
var name = GetStringAttribute(this.treeID, this.id, 'name');
var result = 'node id=' + this.id +
' role=' + this.role +
' state=' + $JSON.stringify(this.state) +
' parentID=' + parentID +
' childIds=' + $JSON.stringify(childIDs);
if (this.hostNode_) {
var hostNodeImpl = privates(this.hostNode_).impl;
result += ' host treeID=' + hostNodeImpl.treeID +
' host nodeID=' + hostNodeImpl.id;
}
if (childTreeID)
result += ' childTreeID=' + childTreeID;
if (name)
result += ' name=' + name;
return result;
},
......@@ -899,6 +946,7 @@ var stringAttributes = [
'display',
'htmlTag',
'imageDataUrl',
'innerHtml',
'language',
'liveRelevant',
'liveStatus',
......@@ -950,7 +998,7 @@ var intAttributes = [
// Int attribute, relation property to expose, reverse relation to expose.
var nodeRefAttributes = [
['activedescendantId', 'activeDescendant', null],
['activedescendantId', 'activeDescendant', 'activeDescendantFor'],
['detailsId', 'details', 'detailsFor'],
['errorMessageId', 'errorMessage', 'errorMessageFor'],
['inPageLinkTargetId', 'inPageLinkTarget', null],
......@@ -1149,7 +1197,6 @@ function AutomationRootNodeImpl(treeID) {
$Function.call(AutomationNodeImpl, this, this);
this.treeID = treeID;
this.axNodeDataCache_ = {__proto__: null};
this.actionRequestIDToCallback_ = {__proto__: null};
}
utils.defineProperty(AutomationRootNodeImpl, 'idToAutomationRootNode_',
......@@ -1168,10 +1215,29 @@ utils.defineProperty(AutomationRootNodeImpl, 'getOrCreate', function(treeID) {
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) {
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 = {
__proto__: AutomationNodeImpl.prototype,
......@@ -1181,16 +1247,9 @@ AutomationRootNodeImpl.prototype = {
isRootNode: true,
/**
* @type {number}
* @type {string}
*/
treeID: -1,
/**
* The parent of this node from a different tree.
* @type {?AutomationNode}
* @private
*/
hostNode_: null,
treeID: "",
/**
* A map from id to AutomationNode.
......@@ -1199,10 +1258,6 @@ AutomationRootNodeImpl.prototype = {
*/
axNodeDataCache_: null,
actionRequestCounter_: 0,
actionRequestIDToCallback_: null,
get id() {
var result = GetRootID(this.treeID);
......@@ -1214,10 +1269,6 @@ AutomationRootNodeImpl.prototype = {
return result;
},
get chromeChannel() {
return GetStringAttribute(this.treeID, this.id, 'chromeChannel');
},
get docUrl() {
return GetDocURL(this.treeID);
},
......@@ -1281,7 +1332,7 @@ AutomationRootNodeImpl.prototype = {
if (id == this.id)
return this.wrapper;
var obj = this.axNodeDataCache_[id];
var obj = this.axNodeDataCache_[id];
if (obj)
return obj;
......@@ -1310,10 +1361,6 @@ AutomationRootNodeImpl.prototype = {
this.detach();
},
setHostNode(hostNode) {
this.hostNode_ = hostNode;
},
onAccessibilityEvent: function(eventParams) {
var targetNode = this.get(eventParams.targetID);
if (targetNode) {
......@@ -1334,14 +1381,15 @@ AutomationRootNodeImpl.prototype = {
},
addActionResultCallback: function(callback) {
this.actionRequestIDToCallback_[++this.actionRequestCounter_] = callback;
return this.actionRequestCounter_;
AutomationRootNodeImpl.actionRequestIDToCallback[
++AutomationRootNodeImpl.actionRequestCounter] = callback;
return AutomationRootNodeImpl.actionRequestCounter;
},
onActionResult: function(requestID, result) {
if (requestID in this.actionRequestIDToCallback_) {
this.actionRequestIDToCallback_[requestID](result);
delete this.actionRequestIDToCallback_[requestID];
if (requestID in AutomationRootNodeImpl.actionRequestIDToCallback) {
AutomationRootNodeImpl.actionRequestIDToCallback[requestID](result);
delete AutomationRootNodeImpl.actionRequestIDToCallback[requestID];
}
},
......@@ -1374,6 +1422,7 @@ utils.expose(AutomationNode, AutomationNodeImpl, {
'findAll',
'focus',
'getImageData',
'getNextTextMatch',
'hitTest',
'hitTestWithReply',
'makeVisible',
......@@ -1427,6 +1476,8 @@ utils.expose(AutomationNode, AutomationNodeImpl, {
'customActions',
'standardActions',
'unclippedLocation',
'tableCellColumnHeaders',
'tableCellRowHeaders',
]),
});
......@@ -1436,7 +1487,6 @@ function AutomationRootNode() {
utils.expose(AutomationRootNode, AutomationRootNodeImpl, {
superclass: AutomationNode,
readonly: [
'chromeChannel',
'docTitle',
'docUrl',
'docLoaded',
......
......@@ -37,7 +37,7 @@ window.automationUtil = function() {};
// TODO(aboxhall): Look into using WeakMap
var idToCallback = {};
var DESKTOP_TREE_ID = 0;
var DESKTOP_TREE_ID = "0";
automationUtil.storeTreeCallback = function(id, callback) {
if (!callback)
......@@ -198,14 +198,12 @@ automation.registerCustomHook(function(bindingsAPI) {
});
automationInternal.onChildTreeID.addListener(function(treeID,
nodeID) {
var tree = AutomationRootNode.getOrCreate(treeID);
if (!tree)
return;
automationInternal.onChildTreeID.addListener(function(childTreeId) {
var targetTree = AutomationRootNode.get(childTreeId);
var node = privates(tree).impl.get(nodeID);
if (!node)
// If the tree is already loded, or if we previously requested it be loaded
// (i.e. have a callback for it), don't try to do so again.
if (targetTree || idToCallback[childTreeId])
return;
// A WebView in the desktop tree has a different AX tree as its child.
......@@ -213,30 +211,11 @@ automationInternal.onChildTreeID.addListener(function(treeID,
// currently have cached, explicitly request that AX tree from the
// browser process and set up a callback when it loads to attach that
// tree as a child of this node and fire appropriate events.
var childTreeID = GetIntAttribute(treeID, nodeID, 'childTreeId');
if (!childTreeID)
return;
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;
automationUtil.storeTreeCallback(childTreeId, function(root) {
privates(root).impl.dispatchEvent('loadComplete', 'page');
}, true);
privates(root).impl.setHostNode(node);
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.enableFrame(childTreeId);
});
automationInternal.onTreeChange.addListener(function(observerID,
......@@ -259,7 +238,7 @@ automationInternal.onTreeChange.addListener(function(observerID,
observer({target: node, type: changeType});
} catch (e) {
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