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.
6 * @fileoverview Implentation of ChromeVox's public API.
10 goog.provide('cvox.ApiImplementation');
11 goog.provide('cvox.ApiImplementation.Math');
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');
24 cvox.ApiImplementation = function() {
28 * The URL to the script loader.
31 cvox.ApiImplementation.siteSpecificScriptLoader;
34 * The URL base for the site-specific scripts.
37 cvox.ApiImplementation.siteSpecificScriptBase;
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.
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);
51 var apiScript = cvox.ScriptInstaller.installScript(scripts,
52 'cvoxapi', opt_onload, cvox.ApiImplementation.siteSpecificScriptBase);
55 // If the API script is already installed, just re-enable it.
56 window.location.href = 'javascript:cvox.Api.internalEnable();';
61 * This method is called when the content script receives a message from
63 * @param {Event} event The DOM event with the message data.
64 * @return {boolean} True if default event processing should continue.
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));
74 // Stop propagation since it was our message.
75 event.stopPropagation();
82 * Call the appropriate API function given a message from the page.
83 * @param {*} message The message.
85 cvox.ApiImplementation.dispatchApiMessage = function(message) {
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;
98 case 'getCvoxModKeys': method = cvox.ApiImplementation.getCvoxModKeys;
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;
107 throw 'Unknown API call: ' + message['cmd'];
110 method.apply(cvox.ApiImplementation, message['args']);
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.
119 function setupEndCallback_(properties, callbackId) {
120 var endCallback = function() {
121 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
127 properties['endCallback'] = endCallback;
132 * Speaks the given string using the specified queueMode and properties.
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.
139 cvox.ApiImplementation.speak = function(
140 callbackId, textString, queueMode, properties) {
141 if (cvox.ChromeVox.isActive) {
145 setupEndCallback_(properties, callbackId);
146 cvox.ChromeVox.tts.speak(textString, queueMode, properties);
151 * Speaks the given node.
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.
157 cvox.ApiImplementation.speakNode = function(node, queueMode, properties) {
158 if (cvox.ChromeVox.isActive) {
159 cvox.ChromeVox.tts.speak(
160 cvox.DomUtil.getName(node),
167 * Speaks the given node.
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.
174 cvox.ApiImplementation.speakNodeRef = function(
175 callbackId, nodeRef, queueMode, properties) {
176 var node = cvox.ApiUtils.getNodeFromRef(nodeRef);
180 setupEndCallback_(properties, callbackId);
181 cvox.ApiImplementation.speakNode(node, queueMode, properties);
187 cvox.ApiImplementation.stop = function() {
188 if (cvox.ChromeVox.isActive) {
189 cvox.ChromeVox.tts.stop();
194 * Plays the specified earcon sound.
196 * @param {string} earcon An earcon name.
228 * This list may expand over time.
230 cvox.ApiImplementation.playEarcon = function(earcon) {
231 if (cvox.ChromeVox.isActive) {
232 cvox.ChromeVox.earcons.playEarconByName(earcon);
237 * Synchronizes ChromeVox's internal cursor to a node by id.
239 * @param {Object} nodeRef A serializable reference to a node.
240 * @param {boolean=} speakNode If true, speaks out the node.
242 cvox.ApiImplementation.syncToNodeRef = function(nodeRef, speakNode) {
243 var node = cvox.ApiUtils.getNodeFromRef(nodeRef);
244 cvox.ApiImplementation.syncToNode(node, speakNode);
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.
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.
257 cvox.ApiImplementation.syncToNode = function(
258 targetNode, opt_speakNode, opt_queueMode) {
259 if (!cvox.ChromeVox.isActive) {
263 if (opt_queueMode == undefined) {
264 opt_queueMode = cvox.AbstractTts.QUEUE_MODE_CATEGORY_FLUSH;
267 cvox.ChromeVox.navigationManager.updateSelToArbitraryNode(targetNode, true);
268 cvox.ChromeVox.navigationManager.updateIndicator();
270 if (opt_speakNode == undefined) {
271 opt_speakNode = false;
274 // Don't speak anything if the node is hidden or invisible.
275 if (cvox.AriaUtil.isHiddenRecursive(targetNode)) {
276 opt_speakNode = false;
280 cvox.ChromeVox.navigationManager.speakDescriptionArray(
281 cvox.ApiImplementation.getDesc_(targetNode),
288 cvox.ChromeVox.navigationManager.getBraille().write();
290 cvox.ChromeVox.navigationManager.updatePosition(targetNode);
294 * Get the current node that ChromeVox is on.
295 * @param {number} callbackId The callback Id.
297 cvox.ApiImplementation.getCurrentNode = function(callbackId) {
298 var currentNode = cvox.ChromeVox.navigationManager.getCurrentNode();
299 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
302 'currentNode': cvox.ApiUtils.makeNodeReference(currentNode)
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
310 * @param {Node} node The node for which to get the description.
311 * @return {Array.<cvox.NavDescription>} The description array.
314 cvox.ApiImplementation.getDesc_ = function(node) {
315 if (!node.hasAttribute('cvoxnodedesc')) {
316 return cvox.ChromeVox.navigationManager.getDescription();
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,
328 userValue: inDesc.userValue,
329 annotation: inDesc.annotation
336 * Simulate a click on an element.
338 * @param {Object} nodeRef A serializable reference to a node.
339 * @param {boolean} shiftKey Specifies if shift is held down.
341 cvox.ApiImplementation.clickNodeRef = function(nodeRef, shiftKey) {
342 cvox.DomUtil.clickElem(
343 cvox.ApiUtils.getNodeFromRef(nodeRef), shiftKey, false);
347 * Get the ChromeVox build info string.
348 * @param {number} callbackId The callback Id.
350 cvox.ApiImplementation.getBuild = function(callbackId) {
351 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
354 'build': cvox.BuildInfo.build
359 * Get the ChromeVox version.
360 * @param {number} callbackId The callback Id.
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);
370 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
378 * Get the ChromeVox modifier keys.
379 * @param {number} callbackId The callback Id.
381 cvox.ApiImplementation.getCvoxModKeys = function(callbackId) {
382 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
385 'keyCodes': cvox.KeyUtil.cvoxModKeyCodes()
390 * Return if the keyEvent has a key binding.
391 * @param {number} callbackId The callback Id.
392 * @param {Event} keyEvent A key event.
394 cvox.ApiImplementation.isKeyShortcut = function(callbackId, keyEvent) {
395 var keySeq = cvox.KeyUtil.keyEventToKeySequence(keyEvent);
396 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
399 'isHandled': cvox.ChromeVoxKbHandler.handlerKeyMap.hasKey(keySeq)
404 * Set key echoing on key press.
405 * @param {boolean} keyEcho Whether key echoing should be on or off.
407 cvox.ApiImplementation.setKeyEcho = function(keyEcho) {
408 var msg = cvox.ChromeVox.keyEcho;
409 msg[document.location.href] = keyEcho;
410 cvox.ChromeVox.host.sendToBackgroundPage({
414 'value': JSON.stringify(msg)
421 cvox.ApiImplementation.Math = function() {};
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.
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);
438 mathStore.defineRule.apply(mathStore, args);