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