Chromium Code Reviews| 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 nativeAutomationInternal = requireNative('automationInternal'); | 16 var nativeAutomationInternal = requireNative('automationInternal'); |
| 17 var GetRoutingID = nativeAutomationInternal.GetRoutingID; | 17 var GetRoutingID = nativeAutomationInternal.GetRoutingID; |
| 18 var DestroyAccessibilityTree = | |
| 19 nativeAutomationInternal.DestroyAccessibilityTree; | |
| 20 var GetIntAttribute = nativeAutomationInternal.GetIntAttribute; | |
| 18 var GetSchemaAdditions = nativeAutomationInternal.GetSchemaAdditions(); | 21 var GetSchemaAdditions = nativeAutomationInternal.GetSchemaAdditions(); |
| 19 var schema = GetSchemaAdditions(); | 22 var schema = GetSchemaAdditions(); |
| 20 | 23 |
| 21 /** | 24 /** |
| 22 * A namespace to export utility functions to other files in automation. | 25 * A namespace to export utility functions to other files in automation. |
| 23 */ | 26 */ |
| 24 window.automationUtil = function() {}; | 27 window.automationUtil = function() {}; |
| 25 | 28 |
| 26 // TODO(aboxhall): Look into using WeakMap | 29 // TODO(aboxhall): Look into using WeakMap |
| 27 var idToAutomationRootNode = {}; | |
| 28 var idToCallback = {}; | 30 var idToCallback = {}; |
| 29 | 31 |
| 30 var DESKTOP_TREE_ID = 0; | 32 var DESKTOP_TREE_ID = 0; |
| 31 | 33 |
| 32 automationUtil.storeTreeCallback = function(id, callback) { | 34 automationUtil.storeTreeCallback = function(id, callback) { |
| 33 if (!callback) | 35 if (!callback) |
| 34 return; | 36 return; |
| 35 | 37 |
| 36 var targetTree = idToAutomationRootNode[id]; | 38 var targetTree = AutomationRootNode.get(id); |
| 37 if (!targetTree) { | 39 if (!targetTree) { |
| 38 // If we haven't cached the tree, hold the callback until the tree is | 40 // If we haven't cached the tree, hold the callback until the tree is |
| 39 // populated by the initial onAccessibilityEvent call. | 41 // populated by the initial onAccessibilityEvent call. |
| 40 if (id in idToCallback) | 42 if (id in idToCallback) |
| 41 idToCallback[id].push(callback); | 43 idToCallback[id].push(callback); |
| 42 else | 44 else |
| 43 idToCallback[id] = [callback]; | 45 idToCallback[id] = [callback]; |
| 44 } else { | 46 } else { |
| 45 callback(targetTree); | 47 callback(targetTree); |
| 46 } | 48 } |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 72 if (lastError.hasError(chrome)) { | 74 if (lastError.hasError(chrome)) { |
| 73 callback(); | 75 callback(); |
| 74 return; | 76 return; |
| 75 } | 77 } |
| 76 automationUtil.storeTreeCallback(id, callback); | 78 automationUtil.storeTreeCallback(id, callback); |
| 77 }); | 79 }); |
| 78 }); | 80 }); |
| 79 | 81 |
| 80 var desktopTree = null; | 82 var desktopTree = null; |
| 81 apiFunctions.setHandleRequest('getDesktop', function(callback) { | 83 apiFunctions.setHandleRequest('getDesktop', function(callback) { |
| 82 desktopTree = | 84 desktopTree = AutomationRootNode.get(DESKTOP_TREE_ID); |
| 83 idToAutomationRootNode[DESKTOP_TREE_ID]; | |
| 84 if (!desktopTree) { | 85 if (!desktopTree) { |
| 85 if (DESKTOP_TREE_ID in idToCallback) | 86 if (DESKTOP_TREE_ID in idToCallback) |
| 86 idToCallback[DESKTOP_TREE_ID].push(callback); | 87 idToCallback[DESKTOP_TREE_ID].push(callback); |
| 87 else | 88 else |
| 88 idToCallback[DESKTOP_TREE_ID] = [callback]; | 89 idToCallback[DESKTOP_TREE_ID] = [callback]; |
| 89 | 90 |
| 90 var routingID = GetRoutingID(); | 91 var routingID = GetRoutingID(); |
| 91 | 92 |
| 92 // TODO(dtseng): Disable desktop tree once desktop object goes out of | 93 // TODO(dtseng): Disable desktop tree once desktop object goes out of |
| 93 // scope. | 94 // scope. |
| 94 automationInternal.enableDesktop(routingID, function() { | 95 automationInternal.enableDesktop(routingID, function() { |
| 95 if (lastError.hasError(chrome)) { | 96 if (lastError.hasError(chrome)) { |
| 96 delete idToAutomationRootNode[ | 97 AutomationRootNode.destroy(DESKTOP_TREE_ID); |
| 97 DESKTOP_TREE_ID]; | |
| 98 callback(); | 98 callback(); |
| 99 return; | 99 return; |
| 100 } | 100 } |
| 101 }); | 101 }); |
| 102 } else { | 102 } else { |
| 103 callback(desktopTree); | 103 callback(desktopTree); |
| 104 } | 104 } |
| 105 }); | 105 }); |
| 106 | 106 |
| 107 function removeTreeChangeObserver(observer) { | 107 function removeTreeChangeObserver(observer) { |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 118 function addTreeChangeObserver(observer) { | 118 function addTreeChangeObserver(observer) { |
| 119 removeTreeChangeObserver(observer); | 119 removeTreeChangeObserver(observer); |
| 120 automationUtil.treeChangeObservers.push(observer); | 120 automationUtil.treeChangeObservers.push(observer); |
| 121 } | 121 } |
| 122 apiFunctions.setHandleRequest('addTreeChangeObserver', function(observer) { | 122 apiFunctions.setHandleRequest('addTreeChangeObserver', function(observer) { |
| 123 addTreeChangeObserver(observer); | 123 addTreeChangeObserver(observer); |
| 124 }); | 124 }); |
| 125 | 125 |
| 126 }); | 126 }); |
| 127 | 127 |
| 128 automationInternal.onTreeChange.addListener(function(treeID, | |
| 129 nodeID, | |
| 130 changeType) { | |
| 131 var tree = AutomationRootNode.get(treeID); | |
| 132 if (!tree) | |
| 133 return; | |
| 134 | |
| 135 var node = privates(tree).impl.get(nodeID); | |
| 136 if (!node) | |
| 137 return; | |
| 138 | |
| 139 if (node.role == 'webView') { | |
| 140 var childTreeID = GetIntAttribute(treeID, nodeID, 'childTreeId'); | |
| 141 if (!AutomationRootNode.get(childTreeID)) { | |
| 142 automationUtil.storeTreeCallback(childTreeID, function(root) { | |
| 143 privates(root).impl.hostNode = node; | |
| 144 | |
| 145 if (root.docLoaded) | |
| 146 privates(root).impl.dispatchEvent(schema.EventType.loadComplete); | |
| 147 | |
| 148 privates(node).impl.dispatchEvent(schema.EventType.childrenChanged); | |
| 149 }); | |
| 150 | |
| 151 automationInternal.enableFrame(childTreeID); | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 var treeChange = {target: node, type: changeType}; | |
| 156 var observers = automationUtil.treeChangeObservers; | |
| 157 for (var i = 0; i < observers.length; i++) { | |
| 158 try { | |
| 159 observers[i](treeChange); | |
| 160 } catch (e) { | |
| 161 console.error('Error in tree change observer for ' + | |
| 162 treeChange.type + ': ' + e.message + | |
| 163 '\nStack trace: ' + e.stack); | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 // | |
| 168 // TODO: delete AutomationNode if 'removeNode' event | |
|
David Tseng
2015/06/10 17:48:16
?
dmazzoni
2015/06/12 17:51:35
Done. I needed to remove the mapping from the id t
| |
| 169 // | |
| 170 }); | |
| 171 | |
| 128 // Listen to the automationInternal.onAccessibilityEvent event, which is | 172 // Listen to the automationInternal.onAccessibilityEvent event, which is |
| 129 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the | 173 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the |
| 130 // renderer. | 174 // renderer. |
| 131 automationInternal.onAccessibilityEvent.addListener(function(data) { | 175 automationInternal.onAccessibilityEvent.addListener(function(data) { |
| 132 var id = data.treeID; | 176 var id = data.treeID; |
| 133 var targetTree = idToAutomationRootNode[id]; | 177 var targetTree = AutomationRootNode.getOrCreate(id); |
| 134 if (!targetTree) { | 178 |
| 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 } | |
| 141 if (!privates(targetTree).impl.onAccessibilityEvent(data)) | 179 if (!privates(targetTree).impl.onAccessibilityEvent(data)) |
| 142 return; | 180 return; |
| 143 | 181 |
| 144 // If we're not waiting on a callback to getTree(), we can early out here. | 182 // If we're not waiting on a callback to getTree(), we can early out here. |
| 145 if (!(id in idToCallback)) | 183 if (!(id in idToCallback)) |
| 146 return; | 184 return; |
| 147 | 185 |
| 148 // We usually get a 'placeholder' tree first, which doesn't have any url | 186 // We usually get a 'placeholder' tree first, which doesn't have any url |
| 149 // attribute or child nodes. If we've got that, wait for the full tree before | 187 // attribute or child nodes. If we've got that, wait for the full tree before |
| 150 // calling the callback. | 188 // calling the callback. |
| 151 // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553) | 189 // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553) |
| 152 if (id != DESKTOP_TREE_ID && !targetTree.attributes.url && | 190 if (id != DESKTOP_TREE_ID && !targetTree.url && |
| 153 targetTree.children.length == 0) { | 191 targetTree.children.length == 0) { |
| 154 return; | 192 return; |
| 155 } | 193 } |
| 156 | 194 |
| 157 // If the tree wasn't available when getTree() was called, the callback will | 195 // If the tree wasn't available when getTree() was called, the callback will |
| 158 // have been cached in idToCallback, so call and delete it now that we | 196 // have been cached in idToCallback, so call and delete it now that we |
| 159 // have the complete tree. | 197 // have the complete tree. |
| 160 for (var i = 0; i < idToCallback[id].length; i++) { | 198 for (var i = 0; i < idToCallback[id].length; i++) { |
| 161 console.log('calling getTree() callback'); | |
| 162 var callback = idToCallback[id][i]; | 199 var callback = idToCallback[id][i]; |
| 163 callback(targetTree); | 200 callback(targetTree); |
| 164 } | 201 } |
| 165 delete idToCallback[id]; | 202 delete idToCallback[id]; |
| 166 }); | 203 }); |
| 167 | 204 |
| 168 automationInternal.onAccessibilityTreeDestroyed.addListener(function(id) { | 205 automationInternal.onAccessibilityTreeDestroyed.addListener(function(id) { |
| 169 var targetTree = idToAutomationRootNode[id]; | 206 // Destroy the native cache of the accessibility tree. |
| 207 DestroyAccessibilityTree(id); | |
| 208 | |
| 209 // Destroy the AutomationRootNode. | |
| 210 var targetTree = AutomationRootNode.get(id); | |
| 170 if (targetTree) { | 211 if (targetTree) { |
| 171 privates(targetTree).impl.destroy(); | 212 privates(targetTree).impl.destroy(); |
| 172 delete idToAutomationRootNode[id]; | 213 AutomationRootNode.destroy(id); |
| 173 } else { | 214 } else { |
| 174 logging.WARNING('no targetTree to destroy'); | 215 logging.WARNING('no targetTree to destroy'); |
| 175 } | 216 } |
| 176 delete idToAutomationRootNode[id]; | |
| 177 }); | 217 }); |
| 178 | 218 |
| 179 exports.binding = automation.generate(); | 219 exports.binding = automation.generate(); |
| 180 | 220 |
| 181 // Add additional accessibility bindings not specified in the automation IDL. | 221 // Add additional accessibility bindings not specified in the automation IDL. |
| 182 // Accessibility and automation share some APIs (see | 222 // Accessibility and automation share some APIs (see |
| 183 // ui/accessibility/ax_enums.idl). | 223 // ui/accessibility/ax_enums.idl). |
| 184 forEach(schema, function(k, v) { | 224 forEach(schema, function(k, v) { |
| 185 exports.binding[k] = v; | 225 exports.binding[k] = v; |
| 186 }); | 226 }); |
| OLD | NEW |