Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / renderer / resources / extensions / automation_custom_bindings.js
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
3 // found in the LICENSE file.
4
5 // Custom bindings for the automation API.
6 var AutomationNode = require('automationNode').AutomationNode;
7 var AutomationRootNode = require('automationNode').AutomationRootNode;
8 var automation = require('binding').Binding.create('automation');
9 var automationInternal =
10     require('binding').Binding.create('automationInternal').generate();
11 var eventBindings = require('event_bindings');
12 var Event = eventBindings.Event;
13 var forEach = require('utils').forEach;
14 var lastError = require('lastError');
15 var logging = requireNative('logging');
16 var schema = requireNative('automationInternal').GetSchemaAdditions();
17
18 /**
19  * A namespace to export utility functions to other files in automation.
20  */
21 window.automationUtil = function() {};
22
23 // TODO(aboxhall): Look into using WeakMap
24 var idToAutomationRootNode = {};
25 var idToCallback = {};
26
27 var DESKTOP_TREE_ID = 0;
28
29 automationUtil.storeTreeCallback = function(id, callback) {
30   if (!callback)
31     return;
32
33   var targetTree = idToAutomationRootNode[id];
34   if (!targetTree) {
35     // If we haven't cached the tree, hold the callback until the tree is
36     // populated by the initial onAccessibilityEvent call.
37     if (id in idToCallback)
38       idToCallback[id].push(callback);
39     else
40       idToCallback[id] = [callback];
41   } else {
42     callback(targetTree);
43   }
44 };
45
46 automation.registerCustomHook(function(bindingsAPI) {
47   var apiFunctions = bindingsAPI.apiFunctions;
48
49   // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj.
50   apiFunctions.setHandleRequest('getTree', function getTree(tabId, callback) {
51     // enableTab() ensures the renderer for the active or specified tab has
52     // accessibility enabled, and fetches its ax tree id to use as
53     // a key in the idToAutomationRootNode map. The callback to
54     // enableTab is bound to the callback passed in to getTree(), so that once
55     // the tree is available (either due to having been cached earlier, or after
56     // an accessibility event occurs which causes the tree to be populated), the
57     // callback can be called.
58     automationInternal.enableTab(tabId, function onEnable(id) {
59       if (lastError.hasError(chrome)) {
60         callback();
61         return;
62       }
63       automationUtil.storeTreeCallback(id, callback);
64     });
65   });
66
67   var desktopTree = null;
68   apiFunctions.setHandleRequest('getDesktop', function(callback) {
69     desktopTree =
70         idToAutomationRootNode[DESKTOP_TREE_ID];
71     if (!desktopTree) {
72       if (DESKTOP_TREE_ID in idToCallback)
73         idToCallback[DESKTOP_TREE_ID].push(callback);
74       else
75         idToCallback[DESKTOP_TREE_ID] = [callback];
76
77       // TODO(dtseng): Disable desktop tree once desktop object goes out of
78       // scope.
79       automationInternal.enableDesktop(function() {
80         if (lastError.hasError(chrome)) {
81           delete idToAutomationRootNode[
82               DESKTOP_TREE_ID];
83           callback();
84           return;
85         }
86       });
87     } else {
88       callback(desktopTree);
89     }
90   });
91 });
92
93 // Listen to the automationInternal.onAccessibilityEvent event, which is
94 // essentially a proxy for the AccessibilityHostMsg_Events IPC from the
95 // renderer.
96 automationInternal.onAccessibilityEvent.addListener(function(data) {
97   var id = data.treeID;
98   var targetTree = idToAutomationRootNode[id];
99   if (!targetTree) {
100     // If this is the first time we've gotten data for this tree, it will
101     // contain all of the tree's data, so create a new tree which will be
102     // bootstrapped from |data|.
103     targetTree = new AutomationRootNode(id);
104     idToAutomationRootNode[id] = targetTree;
105   }
106   if (!privates(targetTree).impl.onAccessibilityEvent(data))
107     return;
108
109   // If we're not waiting on a callback to getTree(), we can early out here.
110   if (!(id in idToCallback))
111     return;
112
113   // We usually get a 'placeholder' tree first, which doesn't have any url
114   // attribute or child nodes. If we've got that, wait for the full tree before
115   // calling the callback.
116   // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553)
117   if (id != DESKTOP_TREE_ID && !targetTree.attributes.url &&
118       targetTree.children.length == 0) {
119     return;
120   }
121
122   // If the tree wasn't available when getTree() was called, the callback will
123   // have been cached in idToCallback, so call and delete it now that we
124   // have the complete tree.
125   for (var i = 0; i < idToCallback[id].length; i++) {
126     console.log('calling getTree() callback');
127     var callback = idToCallback[id][i];
128     callback(targetTree);
129   }
130   delete idToCallback[id];
131 });
132
133 automationInternal.onAccessibilityTreeDestroyed.addListener(function(id) {
134   var targetTree = idToAutomationRootNode[id];
135   if (targetTree) {
136     privates(targetTree).impl.destroy();
137     delete idToAutomationRootNode[id];
138   } else {
139     logging.WARNING('no targetTree to destroy');
140   }
141   delete idToAutomationRootNode[id];
142 });
143
144 exports.binding = automation.generate();
145
146 // Add additional accessibility bindings not specified in the automation IDL.
147 // Accessibility and automation share some APIs (see
148 // ui/accessibility/ax_enums.idl).
149 forEach(schema, function(k, v) {
150   exports.binding[k] = v;
151 });