OLD | NEW |
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() {}; |
| 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 /** |
23 // of AutomationRootNode to keep this file clean. | |
24 /* | |
25 * Creates an id associated with a particular AutomationRootNode based upon a | 28 * Creates an id associated with a particular AutomationRootNode based upon a |
26 * renderer/renderer host pair's process and routing id. | 29 * renderer/renderer host pair's process and routing id. |
27 */ | 30 */ |
28 var createAutomationRootNodeID = function(pid, rid) { | 31 automationUtil.createAutomationRootNodeID = function(pid, rid) { |
29 return pid + '_' + rid; | 32 return pid + '_' + rid; |
30 }; | 33 }; |
31 | 34 |
32 var DESKTOP_TREE_ID = createAutomationRootNodeID(0, 0); | 35 automationUtil.DESKTOP_TREE_ID = |
| 36 automationUtil.createAutomationRootNodeID(0, 0); |
| 37 |
| 38 automationUtil.onTreeLoaded = function(pid, rid, callback) { |
| 39 var id = automationUtil.createAutomationRootNodeID(pid, rid); |
| 40 var targetTree = automationUtil.idToAutomationRootNode[id]; |
| 41 if (!targetTree) { |
| 42 // If we haven't cached the tree, hold the callback until the tree is |
| 43 // populated by the initial onAccessibilityEvent call. |
| 44 if (id in idToCallback) |
| 45 idToCallback[id].push(callback); |
| 46 else |
| 47 idToCallback[id] = [callback]; |
| 48 } else { |
| 49 callback(targetTree); |
| 50 } |
| 51 }; |
33 | 52 |
34 automation.registerCustomHook(function(bindingsAPI) { | 53 automation.registerCustomHook(function(bindingsAPI) { |
35 var apiFunctions = bindingsAPI.apiFunctions; | 54 var apiFunctions = bindingsAPI.apiFunctions; |
36 | 55 |
37 // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj. | 56 // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj. |
38 apiFunctions.setHandleRequest('getTree', function getTree(tabId, callback) { | 57 apiFunctions.setHandleRequest('getTree', function getTree(tabId, callback) { |
39 // enableTab() ensures the renderer for the active or specified tab has | 58 // enableTab() ensures the renderer for the active or specified tab has |
40 // accessibility enabled, and fetches its process and routing ids to use as | 59 // accessibility enabled, and fetches its process and routing ids to use as |
41 // a key in the idToAutomationRootNode map. The callback to enableTab is is | 60 // a key in the automationUtil.idToAutomationRootNode map. The callback to e
nableTab is |
42 // bound to the callback passed in to getTree(), so that once the tree is | 61 // bound to the callback passed in to getTree(), so that once the tree is |
43 // available (either due to having been cached earlier, or after an | 62 // available (either due to having been cached earlier, or after an |
44 // accessibility event occurs which causes the tree to be populated), the | 63 // accessibility event occurs which causes the tree to be populated), the |
45 // callback can be called. | 64 // callback can be called. |
46 automationInternal.enableTab(tabId, function onEnable(pid, rid) { | 65 automationInternal.enableTab(tabId, function onEnable(pid, rid) { |
47 if (lastError.hasError(chrome)) { | 66 if (lastError.hasError(chrome)) { |
48 callback(); | 67 callback(); |
49 return; | 68 return; |
50 } | 69 } |
51 var id = createAutomationRootNodeID(pid, rid); | 70 automationUtil.onTreeLoaded(pid, rid, 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 }); | 71 }); |
64 }); | 72 }); |
65 | 73 |
66 var desktopTree = null; | 74 var desktopTree = null; |
67 apiFunctions.setHandleRequest('getDesktop', function(callback) { | 75 apiFunctions.setHandleRequest('getDesktop', function(callback) { |
68 desktopTree = idToAutomationRootNode[DESKTOP_TREE_ID]; | 76 desktopTree = automationUtil.idToAutomationRootNode[automationUtil.DESKTOP_T
REE_ID]; |
69 if (!desktopTree) { | 77 if (!desktopTree) { |
70 if (DESKTOP_TREE_ID in idToCallback) | 78 if (automationUtil.DESKTOP_TREE_ID in idToCallback) |
71 idToCallback[DESKTOP_TREE_ID].push(callback); | 79 idToCallback[automationUtil.DESKTOP_TREE_ID].push(callback); |
72 else | 80 else |
73 idToCallback[DESKTOP_TREE_ID] = [callback]; | 81 idToCallback[automationUtil.DESKTOP_TREE_ID] = [callback]; |
74 | 82 |
75 // TODO(dtseng): Disable desktop tree once desktop object goes out of | 83 // TODO(dtseng): Disable desktop tree once desktop object goes out of |
76 // scope. | 84 // scope. |
77 automationInternal.enableDesktop(function() { | 85 automationInternal.enableDesktop(function() { |
78 if (lastError.hasError(chrome)) { | 86 if (lastError.hasError(chrome)) { |
79 delete idToAutomationRootNode[DESKTOP_TREE_ID]; | 87 delete automationUtil.idToAutomationRootNode[automationUtil.DESKTOP_TR
EE_ID]; |
80 callback(); | 88 callback(); |
81 return; | 89 return; |
82 } | 90 } |
83 }); | 91 }); |
84 } else { | 92 } else { |
85 callback(desktopTree); | 93 callback(desktopTree); |
86 } | 94 } |
87 }); | 95 }); |
88 }); | 96 }); |
89 | 97 |
90 // Listen to the automationInternal.onAccessibilityEvent event, which is | 98 // Listen to the automationInternal.onAccessibilityEvent event, which is |
91 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the | 99 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the |
92 // renderer. | 100 // renderer. |
93 automationInternal.onAccessibilityEvent.addListener(function(data) { | 101 automationInternal.onAccessibilityEvent.addListener(function(data) { |
94 var pid = data.processID; | 102 var pid = data.processID; |
95 var rid = data.routingID; | 103 var rid = data.routingID; |
96 var id = createAutomationRootNodeID(pid, rid); | 104 var id = automationUtil.createAutomationRootNodeID(pid, rid); |
97 var targetTree = idToAutomationRootNode[id]; | 105 var targetTree = automationUtil.idToAutomationRootNode[id]; |
98 if (!targetTree) { | 106 if (!targetTree) { |
99 // If this is the first time we've gotten data for this tree, it will | 107 // 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 | 108 // contain all of the tree's data, so create a new tree which will be |
101 // bootstrapped from |data|. | 109 // bootstrapped from |data|. |
102 targetTree = new AutomationRootNode(pid, rid); | 110 targetTree = new AutomationRootNode(pid, rid); |
103 idToAutomationRootNode[id] = targetTree; | 111 automationUtil.idToAutomationRootNode[id] = targetTree; |
104 } | 112 } |
105 if (!privates(targetTree).impl.onAccessibilityEvent(data)) | 113 if (!privates(targetTree).impl.onAccessibilityEvent(data)) |
106 return; | 114 return; |
107 | 115 |
108 // If we're not waiting on a callback to getTree(), we can early out here. | 116 // If we're not waiting on a callback to getTree(), we can early out here. |
109 if (!(id in idToCallback)) | 117 if (!(id in idToCallback)) |
110 return; | 118 return; |
111 | 119 |
112 // We usually get a 'placeholder' tree first, which doesn't have any url | 120 // 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 | 121 // attribute or child nodes. If we've got that, wait for the full tree before |
114 // calling the callback. | 122 // calling the callback. |
115 // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553) | 123 // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553) |
116 if (id != DESKTOP_TREE_ID && !targetTree.attributes.url && | 124 if (id != automationUtil.DESKTOP_TREE_ID && !targetTree.attributes.url && |
117 targetTree.children.length == 0) { | 125 targetTree.children.length == 0) { |
118 return; | 126 return; |
119 } | 127 } |
120 | 128 |
121 // If the tree wasn't available when getTree() was called, the callback will | 129 // 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 | 130 // have been cached in idToCallback, so call and delete it now that we |
123 // have the complete tree. | 131 // have the complete tree. |
124 for (var i = 0; i < idToCallback[id].length; i++) { | 132 for (var i = 0; i < idToCallback[id].length; i++) { |
125 console.log('calling getTree() callback'); | 133 console.log('calling getTree() callback'); |
126 var callback = idToCallback[id][i]; | 134 var callback = idToCallback[id][i]; |
127 callback(targetTree); | 135 callback(targetTree); |
128 } | 136 } |
129 delete idToCallback[id]; | 137 delete idToCallback[id]; |
130 }); | 138 }); |
131 | 139 |
132 automationInternal.onAccessibilityTreeDestroyed.addListener(function(pid, rid) { | 140 automationInternal.onAccessibilityTreeDestroyed.addListener(function(pid, rid) { |
133 var id = createAutomationRootNodeID(pid, rid); | 141 var id = automationUtil.createAutomationRootNodeID(pid, rid); |
134 var targetTree = idToAutomationRootNode[id]; | 142 var targetTree = automationUtil.idToAutomationRootNode[id]; |
135 if (targetTree) { | 143 if (targetTree) { |
136 privates(targetTree).impl.destroy(); | 144 privates(targetTree).impl.destroy(); |
137 delete idToAutomationRootNode[id]; | 145 delete automationUtil.idToAutomationRootNode[id]; |
138 } else { | 146 } else { |
139 logging.WARNING('no targetTree to destroy'); | 147 logging.WARNING('no targetTree to destroy'); |
140 } | 148 } |
141 delete idToAutomationRootNode[id]; | 149 delete automationUtil.idToAutomationRootNode[id]; |
142 }); | 150 }); |
143 | 151 |
144 exports.binding = automation.generate(); | 152 exports.binding = automation.generate(); |
145 | 153 |
146 // Add additional accessibility bindings not specified in the automation IDL. | 154 // Add additional accessibility bindings not specified in the automation IDL. |
147 // Accessibility and automation share some APIs (see | 155 // Accessibility and automation share some APIs (see |
148 // ui/accessibility/ax_enums.idl). | 156 // ui/accessibility/ax_enums.idl). |
149 forEach(schema, function(k, v) { | 157 forEach(schema, function(k, v) { |
150 exports.binding[k] = v; | 158 exports.binding[k] = v; |
151 }); | 159 }); |
OLD | NEW |