Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(754)

Side by Side Diff: chrome/renderer/resources/extensions/automation_custom_bindings.js

Issue 1198613002: Revert of Reimplement automation API on top of C++-backed AXTree. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@automation_faster_2
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // Custom bindings for the automation API. 5 // Custom bindings for the automation API.
6 var AutomationNode = require('automationNode').AutomationNode; 6 var AutomationNode = require('automationNode').AutomationNode;
7 var AutomationRootNode = require('automationNode').AutomationRootNode; 7 var AutomationRootNode = require('automationNode').AutomationRootNode;
8 var automation = require('binding').Binding.create('automation'); 8 var automation = require('binding').Binding.create('automation');
9 var automationInternal = 9 var automationInternal =
10 require('binding').Binding.create('automationInternal').generate(); 10 require('binding').Binding.create('automationInternal').generate();
11 var eventBindings = require('event_bindings'); 11 var eventBindings = require('event_bindings');
12 var Event = eventBindings.Event; 12 var Event = eventBindings.Event;
13 var forEach = require('utils').forEach; 13 var forEach = require('utils').forEach;
14 var lastError = require('lastError'); 14 var lastError = require('lastError');
15 var logging = requireNative('logging'); 15 var logging = requireNative('logging');
16 var nativeAutomationInternal = requireNative('automationInternal'); 16 var nativeAutomationInternal = requireNative('automationInternal');
17 var GetRoutingID = nativeAutomationInternal.GetRoutingID; 17 var GetRoutingID = nativeAutomationInternal.GetRoutingID;
18 var GetSchemaAdditions = nativeAutomationInternal.GetSchemaAdditions; 18 var GetSchemaAdditions = nativeAutomationInternal.GetSchemaAdditions;
19 var DestroyAccessibilityTree =
20 nativeAutomationInternal.DestroyAccessibilityTree;
21 var GetIntAttribute = nativeAutomationInternal.GetIntAttribute;
22 var StartCachingAccessibilityTrees =
23 nativeAutomationInternal.StartCachingAccessibilityTrees;
24 var schema = GetSchemaAdditions(); 19 var schema = GetSchemaAdditions();
25 20
26 /** 21 /**
27 * A namespace to export utility functions to other files in automation. 22 * A namespace to export utility functions to other files in automation.
28 */ 23 */
29 window.automationUtil = function() {}; 24 window.automationUtil = function() {};
30 25
31 // TODO(aboxhall): Look into using WeakMap 26 // TODO(aboxhall): Look into using WeakMap
27 var idToAutomationRootNode = {};
32 var idToCallback = {}; 28 var idToCallback = {};
33 29
34 var DESKTOP_TREE_ID = 0; 30 var DESKTOP_TREE_ID = 0;
35 31
36 automationUtil.storeTreeCallback = function(id, callback) { 32 automationUtil.storeTreeCallback = function(id, callback) {
37 if (!callback) 33 if (!callback)
38 return; 34 return;
39 35
40 var targetTree = AutomationRootNode.get(id); 36 var targetTree = idToAutomationRootNode[id];
41 if (!targetTree) { 37 if (!targetTree) {
42 // If we haven't cached the tree, hold the callback until the tree is 38 // If we haven't cached the tree, hold the callback until the tree is
43 // populated by the initial onAccessibilityEvent call. 39 // populated by the initial onAccessibilityEvent call.
44 if (id in idToCallback) 40 if (id in idToCallback)
45 idToCallback[id].push(callback); 41 idToCallback[id].push(callback);
46 else 42 else
47 idToCallback[id] = [callback]; 43 idToCallback[id] = [callback];
48 } else { 44 } else {
49 callback(targetTree); 45 callback(targetTree);
50 } 46 }
51 }; 47 };
52 48
53 /** 49 /**
54 * Global list of tree change observers. 50 * Global list of tree change observers.
55 * @type {Array<TreeChangeObserver>} 51 * @type {Array<TreeChangeObserver>}
56 */ 52 */
57 automationUtil.treeChangeObservers = []; 53 automationUtil.treeChangeObservers = [];
58 54
59 automation.registerCustomHook(function(bindingsAPI) { 55 automation.registerCustomHook(function(bindingsAPI) {
60 var apiFunctions = bindingsAPI.apiFunctions; 56 var apiFunctions = bindingsAPI.apiFunctions;
61 57
62 // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj. 58 // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj.
63 apiFunctions.setHandleRequest('getTree', function getTree(tabID, callback) { 59 apiFunctions.setHandleRequest('getTree', function getTree(tabID, callback) {
64 var routingID = GetRoutingID(); 60 var routingID = GetRoutingID();
65 StartCachingAccessibilityTrees();
66 61
67 // enableTab() ensures the renderer for the active or specified tab has 62 // enableTab() ensures the renderer for the active or specified tab has
68 // accessibility enabled, and fetches its ax tree id to use as 63 // accessibility enabled, and fetches its ax tree id to use as
69 // a key in the idToAutomationRootNode map. The callback to 64 // a key in the idToAutomationRootNode map. The callback to
70 // enableTab is bound to the callback passed in to getTree(), so that once 65 // enableTab is bound to the callback passed in to getTree(), so that once
71 // the tree is available (either due to having been cached earlier, or after 66 // the tree is available (either due to having been cached earlier, or after
72 // an accessibility event occurs which causes the tree to be populated), the 67 // an accessibility event occurs which causes the tree to be populated), the
73 // callback can be called. 68 // callback can be called.
74 var params = { routingID: routingID, tabID: tabID }; 69 var params = { routingID: routingID, tabID: tabID };
75 automationInternal.enableTab(params, 70 automationInternal.enableTab(params,
76 function onEnable(id) { 71 function onEnable(id) {
77 if (lastError.hasError(chrome)) { 72 if (lastError.hasError(chrome)) {
78 callback(); 73 callback();
79 return; 74 return;
80 } 75 }
81 automationUtil.storeTreeCallback(id, callback); 76 automationUtil.storeTreeCallback(id, callback);
82 }); 77 });
83 }); 78 });
84 79
85 var desktopTree = null; 80 var desktopTree = null;
86 apiFunctions.setHandleRequest('getDesktop', function(callback) { 81 apiFunctions.setHandleRequest('getDesktop', function(callback) {
87 StartCachingAccessibilityTrees(); 82 desktopTree =
88 desktopTree = AutomationRootNode.get(DESKTOP_TREE_ID); 83 idToAutomationRootNode[DESKTOP_TREE_ID];
89 if (!desktopTree) { 84 if (!desktopTree) {
90 if (DESKTOP_TREE_ID in idToCallback) 85 if (DESKTOP_TREE_ID in idToCallback)
91 idToCallback[DESKTOP_TREE_ID].push(callback); 86 idToCallback[DESKTOP_TREE_ID].push(callback);
92 else 87 else
93 idToCallback[DESKTOP_TREE_ID] = [callback]; 88 idToCallback[DESKTOP_TREE_ID] = [callback];
94 89
95 var routingID = GetRoutingID(); 90 var routingID = GetRoutingID();
96 91
97 // TODO(dtseng): Disable desktop tree once desktop object goes out of 92 // TODO(dtseng): Disable desktop tree once desktop object goes out of
98 // scope. 93 // scope.
99 automationInternal.enableDesktop(routingID, function() { 94 automationInternal.enableDesktop(routingID, function() {
100 if (lastError.hasError(chrome)) { 95 if (lastError.hasError(chrome)) {
101 AutomationRootNode.destroy(DESKTOP_TREE_ID); 96 delete idToAutomationRootNode[
97 DESKTOP_TREE_ID];
102 callback(); 98 callback();
103 return; 99 return;
104 } 100 }
105 }); 101 });
106 } else { 102 } else {
107 callback(desktopTree); 103 callback(desktopTree);
108 } 104 }
109 }); 105 });
110 106
111 function removeTreeChangeObserver(observer) { 107 function removeTreeChangeObserver(observer) {
(...skipping 10 matching lines...) Expand all
122 function addTreeChangeObserver(observer) { 118 function addTreeChangeObserver(observer) {
123 removeTreeChangeObserver(observer); 119 removeTreeChangeObserver(observer);
124 automationUtil.treeChangeObservers.push(observer); 120 automationUtil.treeChangeObservers.push(observer);
125 } 121 }
126 apiFunctions.setHandleRequest('addTreeChangeObserver', function(observer) { 122 apiFunctions.setHandleRequest('addTreeChangeObserver', function(observer) {
127 addTreeChangeObserver(observer); 123 addTreeChangeObserver(observer);
128 }); 124 });
129 125
130 }); 126 });
131 127
132 automationInternal.onTreeChange.addListener(function(treeID,
133 nodeID,
134 changeType) {
135 var tree = AutomationRootNode.get(treeID);
136 if (!tree)
137 return;
138
139 var node = privates(tree).impl.get(nodeID);
140 if (!node)
141 return;
142
143 if (node.role == 'webView') {
144 // A WebView in the desktop tree has a different AX tree as its child.
145 // When we encounter a WebView with a child AX tree id that we don't
146 // currently have cached, explicitly request that AX tree from the
147 // browser process and set up a callback when it loads to attach that
148 // tree as a child of this node and fire appropriate events.
149 var childTreeID = GetIntAttribute(treeID, nodeID, 'childTreeId');
150 if (!AutomationRootNode.get(childTreeID)) {
151 automationUtil.storeTreeCallback(childTreeID, function(root) {
152 privates(root).impl.setHostNode(node);
153
154 if (root.docLoaded)
155 privates(root).impl.dispatchEvent(schema.EventType.loadComplete);
156
157 privates(node).impl.dispatchEvent(schema.EventType.childrenChanged);
158 });
159
160 automationInternal.enableFrame(childTreeID);
161 }
162 }
163
164 var treeChange = {target: node, type: changeType};
165
166 // Make a copy of the observers in case one of these callbacks tries
167 // to change the list of observers.
168 var observers = automationUtil.treeChangeObservers.slice();
169 for (var i = 0; i < observers.length; i++) {
170 try {
171 observers[i](treeChange);
172 } catch (e) {
173 logging.WARNING('Error in tree change observer for ' +
174 treeChange.type + ': ' + e.message +
175 '\nStack trace: ' + e.stack);
176 }
177 }
178
179 if (changeType == schema.TreeChangeType.nodeRemoved) {
180 privates(tree).impl.remove(nodeID);
181 }
182 });
183
184 // Listen to the automationInternal.onAccessibilityEvent event, which is 128 // Listen to the automationInternal.onAccessibilityEvent event, which is
185 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the 129 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the
186 // renderer. 130 // renderer.
187 automationInternal.onAccessibilityEvent.addListener(function(data) { 131 automationInternal.onAccessibilityEvent.addListener(function(data) {
188 var id = data.treeID; 132 var id = data.treeID;
189 var targetTree = AutomationRootNode.getOrCreate(id); 133 var targetTree = idToAutomationRootNode[id];
190 134 if (!targetTree) {
135 // If this is the first time we've gotten data for this tree, it will
136 // contain all of the tree's data, so create a new tree which will be
137 // bootstrapped from |data|.
138 targetTree = new AutomationRootNode(id);
139 idToAutomationRootNode[id] = targetTree;
140 }
191 if (!privates(targetTree).impl.onAccessibilityEvent(data)) 141 if (!privates(targetTree).impl.onAccessibilityEvent(data))
192 return; 142 return;
193 143
194 // If we're not waiting on a callback to getTree(), we can early out here. 144 // If we're not waiting on a callback to getTree(), we can early out here.
195 if (!(id in idToCallback)) 145 if (!(id in idToCallback))
196 return; 146 return;
197 147
198 // We usually get a 'placeholder' tree first, which doesn't have any url 148 // We usually get a 'placeholder' tree first, which doesn't have any url
199 // attribute or child nodes. If we've got that, wait for the full tree before 149 // attribute or child nodes. If we've got that, wait for the full tree before
200 // calling the callback. 150 // calling the callback.
201 // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553) 151 // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553)
202 if (id != DESKTOP_TREE_ID && !targetTree.url && 152 if (id != DESKTOP_TREE_ID && !targetTree.attributes.url &&
203 targetTree.children.length == 0) { 153 targetTree.children.length == 0) {
204 return; 154 return;
205 } 155 }
206 156
207 // If the tree wasn't available when getTree() was called, the callback will 157 // If the tree wasn't available when getTree() was called, the callback will
208 // have been cached in idToCallback, so call and delete it now that we 158 // have been cached in idToCallback, so call and delete it now that we
209 // have the complete tree. 159 // have the complete tree.
210 for (var i = 0; i < idToCallback[id].length; i++) { 160 for (var i = 0; i < idToCallback[id].length; i++) {
161 console.log('calling getTree() callback');
211 var callback = idToCallback[id][i]; 162 var callback = idToCallback[id][i];
212 callback(targetTree); 163 callback(targetTree);
213 } 164 }
214 delete idToCallback[id]; 165 delete idToCallback[id];
215 }); 166 });
216 167
217 automationInternal.onAccessibilityTreeDestroyed.addListener(function(id) { 168 automationInternal.onAccessibilityTreeDestroyed.addListener(function(id) {
218 // Destroy the AutomationRootNode. 169 var targetTree = idToAutomationRootNode[id];
219 var targetTree = AutomationRootNode.get(id);
220 if (targetTree) { 170 if (targetTree) {
221 privates(targetTree).impl.destroy(); 171 privates(targetTree).impl.destroy();
222 AutomationRootNode.destroy(id); 172 delete idToAutomationRootNode[id];
223 } else { 173 } else {
224 logging.WARNING('no targetTree to destroy'); 174 logging.WARNING('no targetTree to destroy');
225 } 175 }
226 176 delete idToAutomationRootNode[id];
227 // Destroy the native cache of the accessibility tree.
228 DestroyAccessibilityTree(id);
229 }); 177 });
230 178
231 exports.binding = automation.generate(); 179 exports.binding = automation.generate();
232 180
233 // Add additional accessibility bindings not specified in the automation IDL. 181 // Add additional accessibility bindings not specified in the automation IDL.
234 // Accessibility and automation share some APIs (see 182 // Accessibility and automation share some APIs (see
235 // ui/accessibility/ax_enums.idl). 183 // ui/accessibility/ax_enums.idl).
236 forEach(schema, function(k, v) { 184 forEach(schema, function(k, v) {
237 exports.binding[k] = v; 185 exports.binding[k] = v;
238 }); 186 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698