Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / chromeos / chromevox / chromevox / injected / api_implementation.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 /**
6  * @fileoverview Implentation of ChromeVox's public API.
7  *
8  */
9
10 goog.provide('cvox.ApiImplementation');
11 goog.provide('cvox.ApiImplementation.Math');
12
13 goog.require('cvox.ApiUtil');
14 goog.require('cvox.AriaUtil');
15 goog.require('cvox.BuildInfo');
16 goog.require('cvox.ChromeVox');
17 goog.require('cvox.ChromeVoxJSON');
18 goog.require('cvox.DomUtil');
19 goog.require('cvox.ScriptInstaller');
20
21 /**
22  * @constructor
23  */
24 cvox.ApiImplementation = function() {
25 };
26
27 /**
28  * The URL to the script loader.
29  * @type {string}
30  */
31 cvox.ApiImplementation.siteSpecificScriptLoader;
32
33 /**
34  * The URL base for the site-specific scripts.
35  * @type {string}
36  */
37 cvox.ApiImplementation.siteSpecificScriptBase;
38
39 /**
40  * Inject the API into the page and set up communication with it.
41  * @param {function()=} opt_onload A function called when the script is loaded.
42  */
43 cvox.ApiImplementation.init = function(opt_onload) {
44   window.addEventListener('message', cvox.ApiImplementation.portSetup, true);
45   var scripts = new Array();
46   scripts.push(cvox.ChromeVox.host.getFileSrc(
47       'chromevox/injected/api_util.js'));
48   scripts.push(cvox.ChromeVox.host.getApiSrc());
49   scripts.push(cvox.ApiImplementation.siteSpecificScriptLoader);
50
51   var apiScript = cvox.ScriptInstaller.installScript(scripts,
52       'cvoxapi', opt_onload, cvox.ApiImplementation.siteSpecificScriptBase);
53
54   if (!apiScript) {
55     // If the API script is already installed, just re-enable it.
56     window.location.href = 'javascript:cvox.Api.internalEnable();';
57   }
58 };
59
60 /**
61  * This method is called when the content script receives a message from
62  * the page.
63  * @param {Event} event The DOM event with the message data.
64  * @return {boolean} True if default event processing should continue.
65  */
66 cvox.ApiImplementation.portSetup = function(event) {
67   if (event.data == 'cvox.PortSetup') {
68     cvox.ApiImplementation.port = event.ports[0];
69     cvox.ApiImplementation.port.onmessage = function(event) {
70       cvox.ApiImplementation.dispatchApiMessage(
71           cvox.ChromeVoxJSON.parse(event.data));
72     };
73
74     // Stop propagation since it was our message.
75     event.stopPropagation();
76     return false;
77   }
78   return true;
79 };
80
81 /**
82  * Call the appropriate API function given a message from the page.
83  * @param {*} message The message.
84  */
85 cvox.ApiImplementation.dispatchApiMessage = function(message) {
86   var method;
87   switch (message['cmd']) {
88     case 'speak': method = cvox.ApiImplementation.speak; break;
89     case 'speakNodeRef': method = cvox.ApiImplementation.speakNodeRef; break;
90     case 'stop': method = cvox.ApiImplementation.stop; break;
91     case 'playEarcon': method = cvox.ApiImplementation.playEarcon; break;
92     case 'syncToNodeRef': method = cvox.ApiImplementation.syncToNodeRef; break;
93     case 'clickNodeRef': method = cvox.ApiImplementation.clickNodeRef; break;
94     case 'getBuild': method = cvox.ApiImplementation.getBuild; break;
95     case 'getVersion': method = cvox.ApiImplementation.getVersion; break;
96     case 'getCurrentNode': method = cvox.ApiImplementation.getCurrentNode;
97         break;
98     case 'getCvoxModKeys': method = cvox.ApiImplementation.getCvoxModKeys;
99         break;
100     case 'isKeyShortcut': method = cvox.ApiImplementation.isKeyShortcut; break;
101     case 'setKeyEcho': method = cvox.ApiImplementation.setKeyEcho; break;
102     case 'Math.defineRule':
103       method = cvox.ApiImplementation.Math.defineRule; break;
104       break;
105   }
106   if (!method) {
107     throw 'Unknown API call: ' + message['cmd'];
108   }
109
110   method.apply(cvox.ApiImplementation, message['args']);
111 };
112
113 /**
114  * Sets endCallback in properties to call callbackId's function.
115  * @param {Object} properties Speech properties to use for this utterance.
116  * @param {number} callbackId The callback Id.
117  * @private
118  */
119 function setupEndCallback_(properties, callbackId) {
120   var endCallback = function() {
121     cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
122         {
123           'id': callbackId
124         }));
125   };
126   if (properties) {
127     properties['endCallback'] = endCallback;
128   }
129 }
130
131 /**
132  * Speaks the given string using the specified queueMode and properties.
133  *
134  * @param {number} callbackId The callback Id.
135  * @param {string} textString The string of text to be spoken.
136  * @param {number=} queueMode Valid modes are 0 for flush; 1 for queue.
137  * @param {Object=} properties Speech properties to use for this utterance.
138  */
139 cvox.ApiImplementation.speak = function(
140     callbackId, textString, queueMode, properties) {
141   if (cvox.ChromeVox.isActive) {
142     if (!properties) {
143       properties = {};
144     }
145     setupEndCallback_(properties, callbackId);
146     cvox.ChromeVox.tts.speak(textString, queueMode, properties);
147   }
148 };
149
150 /**
151  * Speaks the given node.
152  *
153  * @param {Node} node The node that ChromeVox should be synced to.
154  * @param {number=} queueMode Valid modes are 0 for flush; 1 for queue.
155  * @param {Object=} properties Speech properties to use for this utterance.
156  */
157 cvox.ApiImplementation.speakNode = function(node, queueMode, properties) {
158   if (cvox.ChromeVox.isActive) {
159     cvox.ChromeVox.tts.speak(
160         cvox.DomUtil.getName(node),
161         queueMode,
162         properties);
163   }
164 };
165
166 /**
167  * Speaks the given node.
168  *
169  * @param {number} callbackId The callback Id.
170  * @param {Object} nodeRef A serializable reference to a node.
171  * @param {number=} queueMode Valid modes are 0 for flush; 1 for queue.
172  * @param {Object=} properties Speech properties to use for this utterance.
173  */
174 cvox.ApiImplementation.speakNodeRef = function(
175     callbackId, nodeRef, queueMode, properties) {
176   var node = cvox.ApiUtils.getNodeFromRef(nodeRef);
177   if (!properties) {
178     properties = {};
179   }
180   setupEndCallback_(properties, callbackId);
181   cvox.ApiImplementation.speakNode(node, queueMode, properties);
182 };
183
184 /**
185  * Stops speech.
186  */
187 cvox.ApiImplementation.stop = function() {
188   if (cvox.ChromeVox.isActive) {
189     cvox.ChromeVox.tts.stop();
190   }
191 };
192
193 /**
194  * Plays the specified earcon sound.
195  *
196  * @param {string} earcon An earcon name.
197  * Valid names are:
198  *   ALERT_MODAL
199  *   ALERT_NONMODAL
200  *   BULLET
201  *   BUSY_PROGRESS_LOOP
202  *   BUSY_WORKING_LOOP
203  *   BUTTON
204  *   CHECK_OFF
205  *   CHECK_ON
206  *   COLLAPSED
207  *   EDITABLE_TEXT
208  *   ELLIPSIS
209  *   EXPANDED
210  *   FONT_CHANGE
211  *   INVALID_KEYPRESS
212  *   LINK
213  *   LISTBOX
214  *   LIST_ITEM
215  *   NEW_MAIL
216  *   OBJECT_CLOSE
217  *   OBJECT_DELETE
218  *   OBJECT_DESELECT
219  *   OBJECT_OPEN
220  *   OBJECT_SELECT
221  *   PARAGRAPH_BREAK
222  *   SEARCH_HIT
223  *   SEARCH_MISS
224  *   SECTION
225  *   TASK_SUCCESS
226  *   WRAP
227  *   WRAP_EDGE
228  * This list may expand over time.
229  */
230 cvox.ApiImplementation.playEarcon = function(earcon) {
231   if (cvox.ChromeVox.isActive) {
232     cvox.ChromeVox.earcons.playEarconByName(earcon);
233   }
234 };
235
236 /**
237  * Synchronizes ChromeVox's internal cursor to a node by id.
238  *
239  * @param {Object} nodeRef A serializable reference to a node.
240  * @param {boolean=} speakNode If true, speaks out the node.
241  */
242 cvox.ApiImplementation.syncToNodeRef = function(nodeRef, speakNode) {
243   var node = cvox.ApiUtils.getNodeFromRef(nodeRef);
244   cvox.ApiImplementation.syncToNode(node, speakNode);
245 };
246
247 /**
248  * Synchronizes ChromeVox's internal cursor to the targetNode.
249  * Note that this will NOT trigger reading unless given the optional argument;
250  * it is for setting the internal ChromeVox cursor so that when the user resumes
251  * reading, they will be starting from a reasonable position.
252  *
253  * @param {Node} targetNode The node that ChromeVox should be synced to.
254  * @param {boolean=} opt_speakNode If true, speaks out the node.
255  * @param {number=} opt_queueMode The queue mode to use for speaking.
256  */
257 cvox.ApiImplementation.syncToNode = function(
258     targetNode, opt_speakNode, opt_queueMode) {
259   if (!cvox.ChromeVox.isActive) {
260     return;
261   }
262
263   if (opt_queueMode == undefined) {
264     opt_queueMode = cvox.AbstractTts.QUEUE_MODE_CATEGORY_FLUSH;
265   }
266
267   cvox.ChromeVox.navigationManager.updateSelToArbitraryNode(targetNode, true);
268   cvox.ChromeVox.navigationManager.updateIndicator();
269
270   if (opt_speakNode == undefined) {
271     opt_speakNode = false;
272   }
273
274   // Don't speak anything if the node is hidden or invisible.
275   if (cvox.AriaUtil.isHiddenRecursive(targetNode)) {
276     opt_speakNode = false;
277   }
278
279   if (opt_speakNode) {
280     cvox.ChromeVox.navigationManager.speakDescriptionArray(
281         cvox.ApiImplementation.getDesc_(targetNode),
282         opt_queueMode,
283         null,
284         null,
285         'nav');
286   }
287
288   cvox.ChromeVox.navigationManager.getBraille().write();
289
290   cvox.ChromeVox.navigationManager.updatePosition(targetNode);
291 };
292
293 /**
294  * Get the current node that ChromeVox is on.
295  * @param {number} callbackId The callback Id.
296  */
297 cvox.ApiImplementation.getCurrentNode = function(callbackId) {
298   var currentNode = cvox.ChromeVox.navigationManager.getCurrentNode();
299   cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
300       {
301         'id': callbackId,
302         'currentNode': cvox.ApiUtils.makeNodeReference(currentNode)
303       }));
304 };
305
306 /**
307  * Gets the predefined description set on a node by an api call, if such
308  * a call was made. Otherwise returns the description that the NavigationManager
309  * would speak.
310  * @param {Node} node The node for which to get the description.
311  * @return {Array.<cvox.NavDescription>} The description array.
312  * @private
313  */
314 cvox.ApiImplementation.getDesc_ = function(node) {
315   if (!node.hasAttribute('cvoxnodedesc')) {
316     return cvox.ChromeVox.navigationManager.getDescription();
317   }
318
319   var preDesc = cvox.ChromeVoxJSON.parse(node.getAttribute('cvoxnodedesc'));
320   var currentDesc = new Array();
321   for (var i = 0; i < preDesc.length; ++i) {
322     var inDesc = preDesc[i];
323     // TODO: this can probably be replaced with just NavDescription(inDesc)
324     // need test case to ensure this change will work
325     currentDesc.push(new cvox.NavDescription({
326       context: inDesc.context,
327       text: inDesc.text,
328       userValue: inDesc.userValue,
329       annotation: inDesc.annotation
330     }));
331   }
332   return currentDesc;
333 };
334
335 /**
336  * Simulate a click on an element.
337  *
338  * @param {Object} nodeRef A serializable reference to a node.
339  * @param {boolean} shiftKey Specifies if shift is held down.
340  */
341 cvox.ApiImplementation.clickNodeRef = function(nodeRef, shiftKey) {
342   cvox.DomUtil.clickElem(
343       cvox.ApiUtils.getNodeFromRef(nodeRef), shiftKey, false);
344 };
345
346 /**
347  * Get the ChromeVox build info string.
348  * @param {number} callbackId The callback Id.
349  */
350 cvox.ApiImplementation.getBuild = function(callbackId) {
351   cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
352       {
353         'id': callbackId,
354         'build': cvox.BuildInfo.build
355       }));
356 };
357
358 /**
359  * Get the ChromeVox version.
360  * @param {number} callbackId The callback Id.
361  */
362 cvox.ApiImplementation.getVersion = function(callbackId) {
363   var version = cvox.ChromeVox.version;
364   if (version == null) {
365     window.setTimeout(function() {
366       cvox.ApiImplementation.getVersion(callbackId);
367     }, 1000);
368     return;
369   }
370   cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
371       {
372         'id': callbackId,
373         'version': version
374       }));
375 };
376
377 /**
378  * Get the ChromeVox modifier keys.
379  * @param {number} callbackId The callback Id.
380  */
381 cvox.ApiImplementation.getCvoxModKeys = function(callbackId) {
382   cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
383       {
384         'id': callbackId,
385         'keyCodes': cvox.KeyUtil.cvoxModKeyCodes()
386       }));
387 };
388
389 /**
390  * Return if the keyEvent has a key binding.
391  * @param {number} callbackId The callback Id.
392  * @param {Event} keyEvent A key event.
393  */
394 cvox.ApiImplementation.isKeyShortcut = function(callbackId, keyEvent) {
395   var keySeq = cvox.KeyUtil.keyEventToKeySequence(keyEvent);
396   cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
397       {
398         'id': callbackId,
399         'isHandled': cvox.ChromeVoxKbHandler.handlerKeyMap.hasKey(keySeq)
400       }));
401 };
402
403 /**
404 * Set key echoing on key press.
405 * @param {boolean} keyEcho Whether key echoing should be on or off.
406 */
407 cvox.ApiImplementation.setKeyEcho = function(keyEcho) {
408   var msg = cvox.ChromeVox.keyEcho;
409   msg[document.location.href] = keyEcho;
410   cvox.ChromeVox.host.sendToBackgroundPage({
411   'target': 'Prefs',
412   'action': 'setPref',
413   'pref': 'keyEcho',
414   'value': JSON.stringify(msg)
415   });
416 };
417
418 /**
419  * @constructor
420  */
421 cvox.ApiImplementation.Math = function() {};
422
423 /**
424  * Defines a math speech rule.
425  * @param {string} name Rule name.
426  * @param {string} dynamic Dynamic constraint annotation. In the case of a
427  *      math rule it consists of a domain.style string.
428  * @param {string} action An action of rule components.
429  * @param {string} prec XPath or custom function constraining match.
430  * @param {...string} constraints Additional constraints.
431  */
432 cvox.ApiImplementation.Math.defineRule =
433     function(name, dynamic, action, prec, constraints) {
434   var mathStore = cvox.MathmlStore.getInstance();
435   var constraintList = Array.prototype.slice.call(arguments, 4);
436   var args = [name, dynamic, action, prec].concat(constraintList);
437
438   mathStore.defineRule.apply(mathStore, args);
439 };