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

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

Issue 667713006: Implement automatic load of composed/embedded automation trees (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkcr
Patch Set: Created 6 years, 1 month 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 schema = requireNative('automationInternal').GetSchemaAdditions(); 16 var schema = requireNative('automationInternal').GetSchemaAdditions();
17 17
18 /**
19 * A namespace to export utility functions to other files in automation.
20 */
21 window.automationUtil = function() {};
aboxhall 2014/11/03 17:15:49 I'm really not sure about the implications of this
David Tseng 2014/11/03 19:31:53 kalman can probably better comment, but window is
22
18 // TODO(aboxhall): Look into using WeakMap 23 // TODO(aboxhall): Look into using WeakMap
19 var idToAutomationRootNode = {}; 24 automationUtil.idToAutomationRootNode = {};
20 var idToCallback = {}; 25 var idToCallback = {};
21 26
22 // TODO(dtseng): Move out to automation/automation_util.js or as a static member 27 automationUtil.DESKTOP_TREE_ID = 0;
aboxhall 2014/11/03 17:15:49 Why does DESKTOP_TREE_ID need to be on automationU
David Tseng 2014/11/03 19:31:53 Removed.
23 // of AutomationRootNode to keep this file clean. 28
24 /* 29 automationUtil.storeTreeCallback = function(id, callback) {
25 * Creates an id associated with a particular AutomationRootNode based upon a 30 if (!callback)
26 * renderer/renderer host pair's process and routing id. 31 return;
27 */ 32
28 var createAutomationRootNodeID = function(pid, rid) { 33 var targetTree = automationUtil.idToAutomationRootNode[id];
29 return pid + '_' + rid; 34 if (!targetTree) {
35 // If we haven't cached the tree, hold the callback until the tree is
36 // populated by the initial onAccessibilityEvent call.
37 if (id in idToCallback)
38 idToCallback[id].push(callback);
39 else
40 idToCallback[id] = [callback];
41 } else {
42 callback(targetTree);
43 }
30 }; 44 };
31 45
32 var DESKTOP_TREE_ID = createAutomationRootNodeID(0, 0);
33
34 automation.registerCustomHook(function(bindingsAPI) { 46 automation.registerCustomHook(function(bindingsAPI) {
35 var apiFunctions = bindingsAPI.apiFunctions; 47 var apiFunctions = bindingsAPI.apiFunctions;
36 48
37 // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj. 49 // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj.
38 apiFunctions.setHandleRequest('getTree', function getTree(tabId, callback) { 50 apiFunctions.setHandleRequest('getTree', function getTree(tabId, callback) {
39 // enableTab() ensures the renderer for the active or specified tab has 51 // enableTab() ensures the renderer for the active or specified tab has
40 // accessibility enabled, and fetches its process and routing ids to use as 52 // accessibility enabled, and fetches its ax tree id to use as
41 // a key in the idToAutomationRootNode map. The callback to enableTab is is 53 // a key in the automationUtil.idToAutomationRootNode map. The callback to
42 // bound to the callback passed in to getTree(), so that once the tree is 54 // enableTab is bound to the callback passed in to getTree(), so that once
43 // available (either due to having been cached earlier, or after an 55 // the tree is available (either due to having been cached earlier, or after
44 // accessibility event occurs which causes the tree to be populated), the 56 // an accessibility event occurs which causes the tree to be populated), the
45 // callback can be called. 57 // callback can be called.
46 automationInternal.enableTab(tabId, function onEnable(pid, rid) { 58 automationInternal.enableTab(tabId, function onEnable(id) {
47 if (lastError.hasError(chrome)) { 59 if (lastError.hasError(chrome)) {
48 callback(); 60 callback();
49 return; 61 return;
50 } 62 }
51 var id = createAutomationRootNodeID(pid, rid); 63 automationUtil.storeTreeCallback(id, callback);
52 var targetTree = idToAutomationRootNode[id];
53 if (!targetTree) {
54 // If we haven't cached the tree, hold the callback until the tree is
55 // populated by the initial onAccessibilityEvent call.
56 if (id in idToCallback)
57 idToCallback[id].push(callback);
58 else
59 idToCallback[id] = [callback];
60 } else {
61 callback(targetTree);
62 }
63 }); 64 });
64 }); 65 });
65 66
66 var desktopTree = null; 67 var desktopTree = null;
67 apiFunctions.setHandleRequest('getDesktop', function(callback) { 68 apiFunctions.setHandleRequest('getDesktop', function(callback) {
68 desktopTree = idToAutomationRootNode[DESKTOP_TREE_ID]; 69 desktopTree =
70 automationUtil.idToAutomationRootNode[automationUtil.DESKTOP_TREE_ID];
69 if (!desktopTree) { 71 if (!desktopTree) {
70 if (DESKTOP_TREE_ID in idToCallback) 72 if (automationUtil.DESKTOP_TREE_ID in idToCallback)
71 idToCallback[DESKTOP_TREE_ID].push(callback); 73 idToCallback[automationUtil.DESKTOP_TREE_ID].push(callback);
72 else 74 else
73 idToCallback[DESKTOP_TREE_ID] = [callback]; 75 idToCallback[automationUtil.DESKTOP_TREE_ID] = [callback];
74 76
75 // TODO(dtseng): Disable desktop tree once desktop object goes out of 77 // TODO(dtseng): Disable desktop tree once desktop object goes out of
76 // scope. 78 // scope.
77 automationInternal.enableDesktop(function() { 79 automationInternal.enableDesktop(function() {
78 if (lastError.hasError(chrome)) { 80 if (lastError.hasError(chrome)) {
79 delete idToAutomationRootNode[DESKTOP_TREE_ID]; 81 delete automationUtil.idToAutomationRootNode[
82 automationUtil.DESKTOP_TREE_ID];
80 callback(); 83 callback();
81 return; 84 return;
82 } 85 }
83 }); 86 });
84 } else { 87 } else {
85 callback(desktopTree); 88 callback(desktopTree);
86 } 89 }
87 }); 90 });
88 }); 91 });
89 92
90 // Listen to the automationInternal.onAccessibilityEvent event, which is 93 // Listen to the automationInternal.onAccessibilityEvent event, which is
91 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the 94 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the
92 // renderer. 95 // renderer.
93 automationInternal.onAccessibilityEvent.addListener(function(data) { 96 automationInternal.onAccessibilityEvent.addListener(function(data) {
94 var pid = data.processID; 97 var id = data.treeID;
95 var rid = data.routingID; 98 var targetTree = automationUtil.idToAutomationRootNode[id];
96 var id = createAutomationRootNodeID(pid, rid);
97 var targetTree = idToAutomationRootNode[id];
98 if (!targetTree) { 99 if (!targetTree) {
99 // If this is the first time we've gotten data for this tree, it will 100 // If this is the first time we've gotten data for this tree, it will
100 // contain all of the tree's data, so create a new tree which will be 101 // contain all of the tree's data, so create a new tree which will be
101 // bootstrapped from |data|. 102 // bootstrapped from |data|.
102 targetTree = new AutomationRootNode(pid, rid); 103 targetTree = new AutomationRootNode(id);
103 idToAutomationRootNode[id] = targetTree; 104 automationUtil.idToAutomationRootNode[id] = targetTree;
104 } 105 }
105 if (!privates(targetTree).impl.onAccessibilityEvent(data)) 106 if (!privates(targetTree).impl.onAccessibilityEvent(data))
106 return; 107 return;
107 108
108 // If we're not waiting on a callback to getTree(), we can early out here. 109 // If we're not waiting on a callback to getTree(), we can early out here.
109 if (!(id in idToCallback)) 110 if (!(id in idToCallback))
110 return; 111 return;
111 112
112 // We usually get a 'placeholder' tree first, which doesn't have any url 113 // We usually get a 'placeholder' tree first, which doesn't have any url
113 // attribute or child nodes. If we've got that, wait for the full tree before 114 // attribute or child nodes. If we've got that, wait for the full tree before
114 // calling the callback. 115 // calling the callback.
115 // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553) 116 // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553)
116 if (id != DESKTOP_TREE_ID && !targetTree.attributes.url && 117 if (id != automationUtil.DESKTOP_TREE_ID && !targetTree.attributes.url &&
117 targetTree.children.length == 0) { 118 targetTree.children.length == 0) {
118 return; 119 return;
119 } 120 }
120 121
121 // If the tree wasn't available when getTree() was called, the callback will 122 // If the tree wasn't available when getTree() was called, the callback will
122 // have been cached in idToCallback, so call and delete it now that we 123 // have been cached in idToCallback, so call and delete it now that we
123 // have the complete tree. 124 // have the complete tree.
124 for (var i = 0; i < idToCallback[id].length; i++) { 125 for (var i = 0; i < idToCallback[id].length; i++) {
125 console.log('calling getTree() callback'); 126 console.log('calling getTree() callback');
126 var callback = idToCallback[id][i]; 127 var callback = idToCallback[id][i];
127 callback(targetTree); 128 callback(targetTree);
128 } 129 }
129 delete idToCallback[id]; 130 delete idToCallback[id];
130 }); 131 });
131 132
132 automationInternal.onAccessibilityTreeDestroyed.addListener(function(pid, rid) { 133 automationInternal.onAccessibilityTreeDestroyed.addListener(function(id) {
133 var id = createAutomationRootNodeID(pid, rid); 134 var targetTree = automationUtil.idToAutomationRootNode[id];
134 var targetTree = idToAutomationRootNode[id];
135 if (targetTree) { 135 if (targetTree) {
136 privates(targetTree).impl.destroy(); 136 privates(targetTree).impl.destroy();
137 delete idToAutomationRootNode[id]; 137 delete automationUtil.idToAutomationRootNode[id];
138 } else { 138 } else {
139 logging.WARNING('no targetTree to destroy'); 139 logging.WARNING('no targetTree to destroy');
140 } 140 }
141 delete idToAutomationRootNode[id]; 141 delete automationUtil.idToAutomationRootNode[id];
142 }); 142 });
143 143
144 exports.binding = automation.generate(); 144 exports.binding = automation.generate();
145 145
146 // Add additional accessibility bindings not specified in the automation IDL. 146 // Add additional accessibility bindings not specified in the automation IDL.
147 // Accessibility and automation share some APIs (see 147 // Accessibility and automation share some APIs (see
148 // ui/accessibility/ax_enums.idl). 148 // ui/accessibility/ax_enums.idl).
149 forEach(schema, function(k, v) { 149 forEach(schema, function(k, v) {
150 exports.binding[k] = v; 150 exports.binding[k] = v;
151 }); 151 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698