diff options
Diffstat (limited to 'chromium/chrome/renderer/resources/extensions/automation_custom_bindings.js')
-rw-r--r-- | chromium/chrome/renderer/resources/extensions/automation_custom_bindings.js | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/chromium/chrome/renderer/resources/extensions/automation_custom_bindings.js b/chromium/chrome/renderer/resources/extensions/automation_custom_bindings.js new file mode 100644 index 00000000000..8a0fe4804ed --- /dev/null +++ b/chromium/chrome/renderer/resources/extensions/automation_custom_bindings.js @@ -0,0 +1,128 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Custom bindings for the automation API. +var AutomationNode = require('automationNode').AutomationNode; +var AutomationRootNode = require('automationNode').AutomationRootNode; +var automation = require('binding').Binding.create('automation'); +var automationInternal = + require('binding').Binding.create('automationInternal').generate(); +var eventBindings = require('event_bindings'); +var Event = eventBindings.Event; +var forEach = require('utils').forEach; +var lastError = require('lastError'); +var schema = + requireNative('automationInternal').GetSchemaAdditions(); + +// TODO(aboxhall): Look into using WeakMap +var idToAutomationRootNode = {}; +var idToCallback = {}; + +// TODO(dtseng): Move out to automation/automation_util.js or as a static member +// of AutomationRootNode to keep this file clean. +/* + * Creates an id associated with a particular AutomationRootNode based upon a + * renderer/renderer host pair's process and routing id. + */ +var createAutomationRootNodeID = function(pid, rid) { + return pid + '_' + rid; +}; + +var DESKTOP_TREE_ID = createAutomationRootNodeID(0, 0); + +automation.registerCustomHook(function(bindingsAPI) { + var apiFunctions = bindingsAPI.apiFunctions; + + // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj. + apiFunctions.setHandleRequest('getTree', function getTree(tabId, callback) { + // enableTab() ensures the renderer for the active or specified tab has + // accessibility enabled, and fetches its process and routing ids to use as + // a key in the idToAutomationRootNode map. The callback to enableTab is is + // bound to the callback passed in to getTree(), so that once the tree is + // available (either due to having been cached earlier, or after an + // accessibility event occurs which causes the tree to be populated), the + // callback can be called. + automationInternal.enableTab(tabId, function onEnable(pid, rid) { + if (lastError.hasError(chrome)) { + callback(); + return; + } + var id = createAutomationRootNodeID(pid, rid); + var targetTree = idToAutomationRootNode[id]; + if (!targetTree) { + // If we haven't cached the tree, hold the callback until the tree is + // populated by the initial onAccessibilityEvent call. + if (id in idToCallback) + idToCallback[id].push(callback); + else + idToCallback[id] = [callback]; + } else { + callback(targetTree); + } + }); + }); + + var desktopTree = null; + apiFunctions.setHandleRequest('getDesktop', function(callback) { + desktopTree = idToAutomationRootNode[DESKTOP_TREE_ID]; + if (!desktopTree) { + if (DESKTOP_TREE_ID in idToCallback) + idToCallback[DESKTOP_TREE_ID].push(callback); + else + idToCallback[DESKTOP_TREE_ID] = [callback]; + + // TODO(dtseng): Disable desktop tree once desktop object goes out of + // scope. + automationInternal.enableDesktop(function() { + if (lastError.hasError(chrome)) { + delete idToAutomationRootNode[DESKTOP_TREE_ID]; + callback(); + return; + } + }); + } else { + callback(desktopTree); + } + }); +}); + +// Listen to the automationInternal.onaccessibilityEvent event, which is +// essentially a proxy for the AccessibilityHostMsg_Events IPC from the +// renderer. +automationInternal.onAccessibilityEvent.addListener(function(data) { + var pid = data.processID; + var rid = data.routingID; + var id = createAutomationRootNodeID(pid, rid); + var targetTree = idToAutomationRootNode[id]; + if (!targetTree) { + // If this is the first time we've gotten data for this tree, it will + // contain all of the tree's data, so create a new tree which will be + // bootstrapped from |data|. + targetTree = new AutomationRootNode(pid, rid); + idToAutomationRootNode[id] = targetTree; + } + privates(targetTree).impl.update(data); + var eventType = data.eventType; + if (eventType == 'loadComplete' || eventType == 'layoutComplete') { + // If the tree wasn't available when getTree() was called, the callback will + // have been cached in idToCallback, so call and delete it now that we + // have the complete tree. + if (id in idToCallback) { + for (var i = 0; i < idToCallback[id].length; i++) { + var callback = idToCallback[id][i]; + callback(targetTree); + } + delete idToCallback[id]; + } + } +}); + +exports.binding = automation.generate(); + +// Add additional accessibility bindings not specified in the automation IDL. +// Accessibility and automation share some APIs (see +// ui/accessibility/ax_enums.idl). +forEach(schema, function(k, v) { + exports.binding[k] = v; +}); |