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 exceptionHandler = require('uncaught_exception_handler'); | 13 var exceptionHandler = require('uncaught_exception_handler'); |
14 var forEach = require('utils').forEach; | 14 var forEach = require('utils').forEach; |
15 var lastError = require('lastError'); | 15 var lastError = require('lastError'); |
16 var logging = requireNative('logging'); | 16 var logging = requireNative('logging'); |
17 var nativeAutomationInternal = requireNative('automationInternal'); | 17 var nativeAutomationInternal = requireNative('automationInternal'); |
18 var GetRoutingID = nativeAutomationInternal.GetRoutingID; | 18 var GetRoutingID = nativeAutomationInternal.GetRoutingID; |
19 var GetSchemaAdditions = nativeAutomationInternal.GetSchemaAdditions; | 19 var GetSchemaAdditions = nativeAutomationInternal.GetSchemaAdditions; |
20 var DestroyAccessibilityTree = | 20 var DestroyAccessibilityTree = |
21 nativeAutomationInternal.DestroyAccessibilityTree; | 21 nativeAutomationInternal.DestroyAccessibilityTree; |
22 var GetIntAttribute = nativeAutomationInternal.GetIntAttribute; | 22 var GetIntAttribute = nativeAutomationInternal.GetIntAttribute; |
23 var StartCachingAccessibilityTrees = | 23 var StartCachingAccessibilityTrees = |
24 nativeAutomationInternal.StartCachingAccessibilityTrees; | 24 nativeAutomationInternal.StartCachingAccessibilityTrees; |
25 var AddTreeChangeObserver = nativeAutomationInternal.AddTreeChangeObserver; | 25 var AddTreeChangeObserver = nativeAutomationInternal.AddTreeChangeObserver; |
26 var RemoveTreeChangeObserver = | 26 var RemoveTreeChangeObserver = |
27 nativeAutomationInternal.RemoveTreeChangeObserver; | 27 nativeAutomationInternal.RemoveTreeChangeObserver; |
| 28 var GetFocus = nativeAutomationInternal.GetFocus; |
28 var schema = GetSchemaAdditions(); | 29 var schema = GetSchemaAdditions(); |
29 | 30 |
30 /** | 31 /** |
31 * A namespace to export utility functions to other files in automation. | 32 * A namespace to export utility functions to other files in automation. |
32 */ | 33 */ |
33 window.automationUtil = function() {}; | 34 window.automationUtil = function() {}; |
34 | 35 |
35 // TODO(aboxhall): Look into using WeakMap | 36 // TODO(aboxhall): Look into using WeakMap |
36 var idToCallback = {}; | 37 var idToCallback = {}; |
37 | 38 |
(...skipping 21 matching lines...) Expand all Loading... |
59 * @type {Object<number, TreeChangeObserver>} | 60 * @type {Object<number, TreeChangeObserver>} |
60 */ | 61 */ |
61 automationUtil.treeChangeObserverMap = {}; | 62 automationUtil.treeChangeObserverMap = {}; |
62 | 63 |
63 /** | 64 /** |
64 * The id of the next tree change observer. | 65 * The id of the next tree change observer. |
65 * @type {number} | 66 * @type {number} |
66 */ | 67 */ |
67 automationUtil.nextTreeChangeObserverId = 1; | 68 automationUtil.nextTreeChangeObserverId = 1; |
68 | 69 |
| 70 /** |
| 71 * @type {AutomationNode} The current focused node. This is only updated |
| 72 * when calling automationUtil.updateFocusedNode. |
| 73 */ |
| 74 automationUtil.focusedNode = null; |
| 75 |
| 76 /** |
| 77 * Update automationUtil.focusedNode to be the node that currently has focus. |
| 78 */ |
| 79 automationUtil.updateFocusedNode = function() { |
| 80 automationUtil.focusedNode = null; |
| 81 var focusedNodeInfo = GetFocus(DESKTOP_TREE_ID); |
| 82 if (!focusedNodeInfo) |
| 83 return; |
| 84 var tree = AutomationRootNode.getOrCreate(focusedNodeInfo.treeId); |
| 85 if (tree) { |
| 86 automationUtil.focusedNode = |
| 87 privates(tree).impl.get(focusedNodeInfo.nodeId); |
| 88 } |
| 89 }; |
| 90 |
69 automation.registerCustomHook(function(bindingsAPI) { | 91 automation.registerCustomHook(function(bindingsAPI) { |
70 var apiFunctions = bindingsAPI.apiFunctions; | 92 var apiFunctions = bindingsAPI.apiFunctions; |
71 | 93 |
72 // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj. | 94 // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj. |
73 apiFunctions.setHandleRequest('getTree', function getTree(tabID, callback) { | 95 apiFunctions.setHandleRequest('getTree', function getTree(tabID, callback) { |
74 var routingID = GetRoutingID(); | 96 var routingID = GetRoutingID(); |
75 StartCachingAccessibilityTrees(); | 97 StartCachingAccessibilityTrees(); |
76 | 98 |
77 // enableTab() ensures the renderer for the active or specified tab has | 99 // enableTab() ensures the renderer for the active or specified tab has |
78 // accessibility enabled, and fetches its ax tree id to use as | 100 // accessibility enabled, and fetches its ax tree id to use as |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 AutomationRootNode.destroy(DESKTOP_TREE_ID); | 133 AutomationRootNode.destroy(DESKTOP_TREE_ID); |
112 callback(); | 134 callback(); |
113 return; | 135 return; |
114 } | 136 } |
115 }); | 137 }); |
116 } else { | 138 } else { |
117 callback(desktopTree); | 139 callback(desktopTree); |
118 } | 140 } |
119 }); | 141 }); |
120 | 142 |
| 143 apiFunctions.setHandleRequest('getFocus', function(callback) { |
| 144 automationUtil.updateFocusedNode(); |
| 145 callback(automationUtil.focusedNode); |
| 146 }); |
| 147 |
121 function removeTreeChangeObserver(observer) { | 148 function removeTreeChangeObserver(observer) { |
122 for (var id in automationUtil.treeChangeObserverMap) { | 149 for (var id in automationUtil.treeChangeObserverMap) { |
123 if (automationUtil.treeChangeObserverMap[id] == observer) { | 150 if (automationUtil.treeChangeObserverMap[id] == observer) { |
124 RemoveTreeChangeObserver(id); | 151 RemoveTreeChangeObserver(id); |
125 delete automationUtil.treeChangeObserverMap[id]; | 152 delete automationUtil.treeChangeObserverMap[id]; |
126 return; | 153 return; |
127 } | 154 } |
128 } | 155 } |
129 } | 156 } |
130 apiFunctions.setHandleRequest('removeTreeChangeObserver', function(observer) { | 157 apiFunctions.setHandleRequest('removeTreeChangeObserver', function(observer) { |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
224 automationInternal.onNodesRemoved.addListener(function(treeID, nodeIDs) { | 251 automationInternal.onNodesRemoved.addListener(function(treeID, nodeIDs) { |
225 var tree = AutomationRootNode.getOrCreate(treeID); | 252 var tree = AutomationRootNode.getOrCreate(treeID); |
226 if (!tree) | 253 if (!tree) |
227 return; | 254 return; |
228 | 255 |
229 for (var i = 0; i < nodeIDs.length; i++) { | 256 for (var i = 0; i < nodeIDs.length; i++) { |
230 privates(tree).impl.remove(nodeIDs[i]); | 257 privates(tree).impl.remove(nodeIDs[i]); |
231 } | 258 } |
232 }); | 259 }); |
233 | 260 |
234 // Listen to the automationInternal.onAccessibilityEvent event, which is | 261 /** |
235 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the | 262 * Dispatch accessibility events fired on individual nodes to its |
236 // renderer. | 263 * corresponding AutomationNode. Handle focus events specially |
237 automationInternal.onAccessibilityEvent.addListener(function(data) { | 264 * (see below). |
238 var id = data.treeID; | 265 */ |
| 266 automationInternal.onAccessibilityEvent.addListener(function(eventParams) { |
| 267 var id = eventParams.treeID; |
239 var targetTree = AutomationRootNode.getOrCreate(id); | 268 var targetTree = AutomationRootNode.getOrCreate(id); |
240 | 269 |
241 if (!privates(targetTree).impl.onAccessibilityEvent(data)) | 270 // When we get a focus event, ignore the actual event target, and instead |
| 271 // check what node has focus globally. If that represents a focus change, |
| 272 // fire a focus event on the correct target. |
| 273 if (eventParams.eventType == schema.EventType.focus) { |
| 274 var previousFocusedNode = automationUtil.focusedNode; |
| 275 automationUtil.updateFocusedNode(); |
| 276 if (automationUtil.focusedNode && |
| 277 automationUtil.focusedNode == previousFocusedNode) { |
| 278 return; |
| 279 } |
| 280 |
| 281 if (automationUtil.focusedNode) { |
| 282 targetTree = automationUtil.focusedNode.root; |
| 283 eventParams.treeID = privates(targetTree).impl.treeID; |
| 284 eventParams.targetID = privates(automationUtil.focusedNode).impl.id; |
| 285 } |
| 286 } |
| 287 |
| 288 if (!privates(targetTree).impl.onAccessibilityEvent(eventParams)) |
242 return; | 289 return; |
243 | 290 |
244 // If we're not waiting on a callback to getTree(), we can early out here. | 291 // If we're not waiting on a callback to getTree(), we can early out here. |
245 if (!(id in idToCallback)) | 292 if (!(id in idToCallback)) |
246 return; | 293 return; |
247 | 294 |
248 // We usually get a 'placeholder' tree first, which doesn't have any url | 295 // We usually get a 'placeholder' tree first, which doesn't have any url |
249 // attribute or child nodes. If we've got that, wait for the full tree before | 296 // attribute or child nodes. If we've got that, wait for the full tree before |
250 // calling the callback. | 297 // calling the callback. |
251 // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553) | 298 // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553) |
(...skipping 26 matching lines...) Expand all Loading... |
278 }); | 325 }); |
279 | 326 |
280 exports.binding = automation.generate(); | 327 exports.binding = automation.generate(); |
281 | 328 |
282 // Add additional accessibility bindings not specified in the automation IDL. | 329 // Add additional accessibility bindings not specified in the automation IDL. |
283 // Accessibility and automation share some APIs (see | 330 // Accessibility and automation share some APIs (see |
284 // ui/accessibility/ax_enums.idl). | 331 // ui/accessibility/ax_enums.idl). |
285 forEach(schema, function(k, v) { | 332 forEach(schema, function(k, v) { |
286 exports.binding[k] = v; | 333 exports.binding[k] = v; |
287 }); | 334 }); |
OLD | NEW |