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,
147 /** @type {cvox.QueueMode} */ (queueMode),
153 * Speaks the given node.
155 * @param {Node} node The node that ChromeVox should be synced to.
156 * @param {number=} queueMode Valid modes are 0 for flush; 1 for queue.
157 * @param {Object=} properties Speech properties to use for this utterance.
159 cvox.ApiImplementation.speakNode = function(node, queueMode, properties) {
160 if (cvox.ChromeVox.isActive) {
161 cvox.ChromeVox.tts.speak(
162 cvox.DomUtil.getName(node),
163 /** @type {cvox.QueueMode} */ (queueMode),
169 * Speaks the given node.
171 * @param {number} callbackId The callback Id.
172 * @param {Object} nodeRef A serializable reference to a node.
173 * @param {number=} queueMode Valid modes are 0 for flush; 1 for queue.
174 * @param {Object=} properties Speech properties to use for this utterance.
176 cvox.ApiImplementation.speakNodeRef = function(
177 callbackId, nodeRef, queueMode, properties) {
178 var node = cvox.ApiUtils.getNodeFromRef(nodeRef);
182 setupEndCallback_(properties, callbackId);
183 cvox.ApiImplementation.speakNode(node, queueMode, properties);
189 cvox.ApiImplementation.stop = function() {
190 if (cvox.ChromeVox.isActive) {
191 cvox.ChromeVox.tts.stop();
196 * Plays the specified earcon sound.
198 * @param {string} earcon An earcon name.
230 * This list may expand over time.
232 cvox.ApiImplementation.playEarcon = function(earcon) {
233 if (cvox.ChromeVox.isActive) {
234 cvox.ChromeVox.earcons.playEarconByName(earcon);
239 * Synchronizes ChromeVox's internal cursor to a node by id.
241 * @param {Object} nodeRef A serializable reference to a node.
242 * @param {boolean=} speakNode If true, speaks out the node.
244 cvox.ApiImplementation.syncToNodeRef = function(nodeRef, speakNode) {
245 var node = cvox.ApiUtils.getNodeFromRef(nodeRef);
246 cvox.ApiImplementation.syncToNode(node, speakNode);
250 * Synchronizes ChromeVox's internal cursor to the targetNode.
251 * Note that this will NOT trigger reading unless given the optional argument;
252 * it is for setting the internal ChromeVox cursor so that when the user resumes
253 * reading, they will be starting from a reasonable position.
255 * @param {Node} targetNode The node that ChromeVox should be synced to.
256 * @param {boolean=} opt_speakNode If true, speaks out the node.
257 * @param {number=} opt_queueMode The queue mode to use for speaking.
259 cvox.ApiImplementation.syncToNode = function(
260 targetNode, opt_speakNode, opt_queueMode) {
261 if (!cvox.ChromeVox.isActive) {
265 if (opt_queueMode == undefined) {
266 opt_queueMode = cvox.QueueMode.CATEGORY_FLUSH;
269 cvox.ChromeVox.navigationManager.updateSelToArbitraryNode(targetNode, true);
270 cvox.ChromeVox.navigationManager.updateIndicator();
272 if (opt_speakNode == undefined) {
273 opt_speakNode = false;
276 // Don't speak anything if the node is hidden or invisible.
277 if (cvox.AriaUtil.isHiddenRecursive(targetNode)) {
278 opt_speakNode = false;
282 cvox.ChromeVox.navigationManager.speakDescriptionArray(
283 cvox.ApiImplementation.getDesc_(targetNode),
284 /** @type {cvox.QueueMode} */ (opt_queueMode),
287 cvox.TtsCategory.NAV);
290 cvox.ChromeVox.navigationManager.getBraille().write();
292 cvox.ChromeVox.navigationManager.updatePosition(targetNode);
296 * Get the current node that ChromeVox is on.
297 * @param {number} callbackId The callback Id.
299 cvox.ApiImplementation.getCurrentNode = function(callbackId) {
300 var currentNode = cvox.ChromeVox.navigationManager.getCurrentNode();
301 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
304 'currentNode': cvox.ApiUtils.makeNodeReference(currentNode)
309 * Gets the predefined description set on a node by an api call, if such
310 * a call was made. Otherwise returns the description that the NavigationManager
312 * @param {Node} node The node for which to get the description.
313 * @return {Array.<cvox.NavDescription>} The description array.
316 cvox.ApiImplementation.getDesc_ = function(node) {
317 if (!node.hasAttribute('cvoxnodedesc')) {
318 return cvox.ChromeVox.navigationManager.getDescription();
321 var preDesc = cvox.ChromeVoxJSON.parse(node.getAttribute('cvoxnodedesc'));
322 var currentDesc = new Array();
323 for (var i = 0; i < preDesc.length; ++i) {
324 var inDesc = preDesc[i];
325 // TODO: this can probably be replaced with just NavDescription(inDesc)
326 // need test case to ensure this change will work
327 currentDesc.push(new cvox.NavDescription({
328 context: inDesc.context,
330 userValue: inDesc.userValue,
331 annotation: inDesc.annotation
338 * Simulate a click on an element.
340 * @param {Object} nodeRef A serializable reference to a node.
341 * @param {boolean} shiftKey Specifies if shift is held down.
343 cvox.ApiImplementation.clickNodeRef = function(nodeRef, shiftKey) {
344 cvox.DomUtil.clickElem(
345 cvox.ApiUtils.getNodeFromRef(nodeRef), shiftKey, false);
349 * Get the ChromeVox build info string.
350 * @param {number} callbackId The callback Id.
352 cvox.ApiImplementation.getBuild = function(callbackId) {
353 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
356 'build': cvox.BuildInfo.build
361 * Get the ChromeVox version.
362 * @param {number} callbackId The callback Id.
364 cvox.ApiImplementation.getVersion = function(callbackId) {
365 var version = cvox.ChromeVox.version;
366 if (version == null) {
367 window.setTimeout(function() {
368 cvox.ApiImplementation.getVersion(callbackId);
372 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
380 * Get the ChromeVox modifier keys.
381 * @param {number} callbackId The callback Id.
383 cvox.ApiImplementation.getCvoxModKeys = function(callbackId) {
384 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
387 'keyCodes': cvox.KeyUtil.cvoxModKeyCodes()
392 * Return if the keyEvent has a key binding.
393 * @param {number} callbackId The callback Id.
394 * @param {Event} keyEvent A key event.
396 cvox.ApiImplementation.isKeyShortcut = function(callbackId, keyEvent) {
397 var keySeq = cvox.KeyUtil.keyEventToKeySequence(keyEvent);
398 cvox.ApiImplementation.port.postMessage(cvox.ChromeVoxJSON.stringify(
401 'isHandled': cvox.ChromeVoxKbHandler.handlerKeyMap.hasKey(keySeq)
406 * Set key echoing on key press.
407 * @param {boolean} keyEcho Whether key echoing should be on or off.
409 cvox.ApiImplementation.setKeyEcho = function(keyEcho) {
410 var msg = cvox.ChromeVox.keyEcho;
411 msg[document.location.href] = keyEcho;
412 cvox.ChromeVox.host.sendToBackgroundPage({
416 'value': JSON.stringify(msg)
423 cvox.ApiImplementation.Math = function() {};
426 * Defines a math speech rule.
427 * @param {string} name Rule name.
428 * @param {string} dynamic Dynamic constraint annotation. In the case of a
429 * math rule it consists of a domain.style string.
430 * @param {string} action An action of rule components.
431 * @param {string} prec XPath or custom function constraining match.
432 * @param {...string} constraints Additional constraints.
434 cvox.ApiImplementation.Math.defineRule =
435 function(name, dynamic, action, prec, constraints) {
436 var mathStore = cvox.MathmlStore.getInstance();
437 var constraintList = Array.prototype.slice.call(arguments, 4);
438 var args = [name, dynamic, action, prec].concat(constraintList);
440 mathStore.defineRule.apply(mathStore, args);