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);
};
......
......@@ -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_;
......
......@@ -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