Commit 99e10123 authored by dtseng's avatar dtseng Committed by Commit bot

Minimize the Classic API and enable it for Next

TEST=manual; verify Docs, Sheets, and Slides work with regard to whitespace, key echo, etc when braille mode is off.
BUG=690635,690636,618099
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2740793002
Cr-Commit-Position: refs/heads/master@{#456133}
parent b6b3ddb9
......@@ -43,7 +43,6 @@ chromevox_modules = [
"chromevox/background/tabs_api_handler.js",
"chromevox/injected/active_indicator.js",
"chromevox/injected/api_implementation.js",
"chromevox/injected/api_util.js",
"chromevox/injected/console_tts.js",
"chromevox/injected/event_suspender.js",
"chromevox/injected/event_watcher.js",
......@@ -349,9 +348,7 @@ run_jsbundler("chromevox_copied_files") {
"images/options-hover-19.png",
"images/triangle-6.png",
]
if (chromevox_compress_js) {
sources += [ "chromevox/injected/api_util.js" ]
} else {
if (!chromevox_compress_js) {
sources += chromevox_modules
sources += [
"closure/closure_preinit.js",
......
// 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.
/**
* @fileoverview Shared util methods between api.js and api_implementation.js
* for doing common tasks such as passing node references between page script
* and ChromeVox.
*/
if (typeof(goog) != 'undefined' && goog.provide){
goog.provide('cvox.ApiUtil');
}
if (!window['cvox']) {
window['cvox'] = {};
}
/**
* @constructor
*/
cvox.ApiUtils = function() {
};
/**
* The next id to use for the cvoxid attribute that we add to elements
* in order to be able to find them from the content script.
* @type {number}
*/
cvox.ApiUtils.nextCvoxId_ = 1;
/**
* Makes a serializable reference to a node.
* If the node or its parent has an ID, reference it directly. Otherwise,
* add a temporary cvoxid attribute. This has a corresponding method in
* api_implementation.js to decode this and return a node.
* @param {Node} targetNode The node to reference.
* @return {Object} A serializable node reference.
*/
cvox.ApiUtils.makeNodeReference = function(targetNode) {
if (targetNode.id && document.getElementById(targetNode.id) == targetNode) {
return {'id': targetNode.id};
} else if (targetNode instanceof HTMLElement) {
var cvoxid = cvox.ApiUtils.nextCvoxId_;
targetNode.setAttribute('cvoxid', cvoxid);
cvox.ApiUtils.nextCvoxId_ = (cvox.ApiUtils.nextCvoxId_ + 1) % 100;
return {'cvoxid': cvoxid};
} else if (targetNode.parentElement) {
var parent = targetNode.parentElement;
var childIndex = -1;
for (var i = 0; i < parent.childNodes.length; i++) {
if (parent.childNodes[i] == targetNode) {
childIndex = i;
break;
}
}
if (childIndex >= 0) {
var cvoxid = cvox.ApiUtils.nextCvoxId_;
parent.setAttribute('cvoxid', cvoxid);
cvox.ApiUtils.nextCvoxId_ = (cvox.ApiUtils.nextCvoxId_ + 1) % 100;
return {'cvoxid': cvoxid, 'childIndex': childIndex};
}
}
throw 'Cannot reference node: ' + targetNode;
};
/**
* Retrieves a node from its serializable node reference.
*
* @param {Object} nodeRef A serializable reference to a node.
* @return {Node} The node on the page that this object refers to.
*/
cvox.ApiUtils.getNodeFromRef = function(nodeRef) {
if (nodeRef['id']) {
return document.getElementById(nodeRef['id']);
} else if (nodeRef['cvoxid']) {
var selector = '*[cvoxid="' + nodeRef['cvoxid'] + '"]';
var element = document.querySelector(selector);
if (element && element.removeAttribute) {
element.removeAttribute('cvoxid');
}
if (nodeRef['childIndex'] != null) {
return element.childNodes[nodeRef['childIndex']];
} else {
return element;
}
}
throw 'Bad node reference: ' + cvox.ChromeVoxJSON.stringify(nodeRef);
};
......@@ -11,7 +11,6 @@ goog.provide('cvox.ChromeVoxEventWatcher');
goog.provide('cvox.ChromeVoxEventWatcherUtil');
goog.require('cvox.ActiveIndicator');
goog.require('cvox.ApiImplementation');
goog.require('cvox.AriaUtil');
goog.require('cvox.ChromeVox');
goog.require('cvox.ChromeVoxEditableTextBase');
......@@ -593,8 +592,9 @@ cvox.ChromeVoxEventWatcher.mouseOverEventWatcher = function(evt) {
cvox.ChromeVox.navigationManager.stopReading(true);
var target = /** @type {Node} */(evt.target);
cvox.Focuser.setFocus(target);
cvox.ApiImplementation.syncToNode(
cvox.ChromeVox.navigationManager.syncToNode(
target, true, cvox.ChromeVoxEventWatcher.queueMode_());
cvox.ChromeVoxEventWatcher.announcedMouseOverNode = target;
});
}, mouseoverDelayMs);
......@@ -688,7 +688,7 @@ cvox.ChromeVoxEventWatcher.focusHandler = function(evt) {
// Navigate to this control so that it will be the same for focus as for
// regular navigation.
cvox.ApiImplementation.syncToNode(
cvox.ChromeVox.navigationManager.syncToNode(
target, !document.webkitHidden, queueMode);
if ((evt.target.constructor == HTMLVideoElement) ||
......@@ -1138,9 +1138,10 @@ cvox.ChromeVoxEventWatcher.handleControlChanged = function(control) {
parentControl != control &&
document.activeElement == control)) {
// Sync ChromeVox to the newly selected control.
cvox.ApiImplementation.syncToNode(
cvox.ChromeVox.navigationManager.syncToNode(
activeDescendant || control, true,
cvox.ChromeVoxEventWatcher.queueMode_());
announceChange = false;
} else if (activeDescendant) {
cvox.ChromeVox.navigationManager.updateSelToArbitraryNode(
......@@ -1436,7 +1437,8 @@ cvox.ChromeVoxEventWatcher.handleEvent_ = function(evt) {
cvox.ChromeVoxEventWatcher.setUpTextHandler();
break;
case 'click':
cvox.ApiImplementation.syncToNode(/** @type {Node} */(evt.target), true);
cvox.ChromeVox.navigationManager.syncToNode(
/** @type {Node} */(evt.target), true);
break;
case 'focus':
cvox.ChromeVoxEventWatcher.focusHandler(evt);
......
......@@ -9,7 +9,6 @@
goog.provide('cvox.InitGlobals');
goog.require('cvox.ApiImplementation');
goog.require('cvox.ChromeVox');
goog.require('cvox.ChromeVoxEventWatcher');
goog.require('cvox.CompositeTts');
......@@ -48,8 +47,8 @@ cvox.InitGlobals.initGlobals = function() {
cvox.ChromeVox.isActive = true;
cvox.ChromeVox.navigationManager = new cvox.NavigationManager();
cvox.ChromeVox.navigationManager.updateIndicator();
cvox.ChromeVox.syncToNode = cvox.ApiImplementation.syncToNode;
cvox.ChromeVox.speakNode = cvox.ApiImplementation.speakNode;
cvox.ChromeVox.syncToNode = cvox.ChromeVox.navigationManager.syncToNode.bind(
cvox.ChromeVox.navigationManager);
cvox.ChromeVox.serializer = new cvox.Serializer();
......
......@@ -15,6 +15,7 @@
goog.provide('cvox.NavigationManager');
goog.require('cvox.ActiveIndicator');
goog.require('cvox.AriaUtil');
goog.require('cvox.ChromeVox');
goog.require('cvox.ChromeVoxEventSuspender');
goog.require('cvox.CursorSelection');
......@@ -382,6 +383,52 @@ cvox.NavigationManager.prototype.syncAll = function(opt_skipText) {
};
/**
* Synchronizes ChromeVox's internal cursor to the targetNode.
* Note that this will NOT trigger reading unless given the optional argument;
* it is for setting the internal ChromeVox cursor so that when the user resumes
* reading, they will be starting from a reasonable position.
*
* @param {Node} targetNode The node that ChromeVox should be synced to.
* @param {boolean=} opt_speakNode If true, speaks out the node.
* @param {number=} opt_queueMode The queue mode to use for speaking.
*/
cvox.NavigationManager.prototype.syncToNode = function(
targetNode, opt_speakNode, opt_queueMode) {
if (!cvox.ChromeVox.isActive) {
return;
}
if (opt_queueMode == undefined) {
opt_queueMode = cvox.QueueMode.CATEGORY_FLUSH;
}
this.updateSelToArbitraryNode(targetNode, true);
this.updateIndicator();
if (opt_speakNode == undefined) {
opt_speakNode = false;
}
// Don't speak anything if the node is hidden or invisible.
if (cvox.AriaUtil.isHiddenRecursive(targetNode)) {
opt_speakNode = false;
}
if (opt_speakNode) {
this.speakDescriptionArray(this.getDescription(),
/** @type {cvox.QueueMode} */ (opt_queueMode),
null,
null,
cvox.TtsCategory.NAV);
}
cvox.ChromeVox.braille.write(this.getBraille());
this.updatePosition(targetNode);
};
/**
* Clears a DOM selection made via a CursorSelection.
* @param {boolean=} opt_announce True to announce the clearing.
......
......@@ -10,7 +10,6 @@
goog.provide('cvox.ScriptInstaller');
goog.require('cvox.DomUtil');
/**
* URL pattern where we do not allow script installation.
......@@ -93,7 +92,8 @@ cvox.ScriptInstaller.installScriptHelper_ = function(srcs, uid, opt_onload,
apiScript.setAttribute('chromevoxScriptBase',
opt_chromevoxScriptBase);
}
cvox.DomUtil.addNodeToHead(apiScript);
var scriptOwner = document.head || document.body;
scriptOwner.appendChild(apiScript);
next();
}
};
......
......@@ -114,7 +114,7 @@ cvox.Widget.prototype.hide = function(opt_noSync) {
if (!opt_noSync) {
this.initialNode = this.initialNode.nodeType == 1 ?
this.initialNode : this.initialNode.parentNode;
cvox.ApiImplementation.syncToNode(this.initialNode,
cvox.ChromeVox.navigationManager.syncToNode(this.initialNode,
true,
cvox.QueueMode.QUEUE);
}
......
......@@ -205,14 +205,6 @@ cvox.ChromeVox.markInUserCommand = function() {};
cvox.ChromeVox.syncToNode = function(
targetNode, speakNode, opt_queueMode) {};
/**
* Speaks the given node.
* @param {Node} targetNode The node that ChromeVox should be synced to.
* @param {number=} queueMode The queue mode to use for speaking.
* @param {Object=} properties Speech properties to use for this utterance.
*/
cvox.ChromeVox.speakNode = function(targetNode, queueMode, properties) {};
/**
* Provide a way for modules that can't depend on cvox.ChromeVoxUserCommands
* to execute commands.
......
......@@ -72,17 +72,6 @@ Background = function() {
this.classicBlacklistRegExp_ = Background.globsToRegExp_(
chrome.runtime.getManifest()['content_scripts'][0]['exclude_globs']);
/**
* Regular expression for whitelisting Next compat.
* @type {RegExp}
* @private
*/
this.nextCompatRegExp_ = Background.globsToRegExp_([
'*docs.google.com/document/*',
'*docs.google.com/spreadsheets/*',
'*docs.google.com/presentation/*'
]);
/**
* @type {cursors.Range}
* @private
......@@ -106,8 +95,7 @@ Background = function() {
Object.defineProperty(cvox.ChromeVox, 'earcons', {
get: (function() {
if (this.mode === ChromeVoxMode.FORCE_NEXT ||
this.mode === ChromeVoxMode.NEXT ||
this.mode === ChromeVoxMode.NEXT_COMPAT) {
this.mode === ChromeVoxMode.NEXT) {
return this.nextEarcons_;
} else {
return this.classicEarcons_;
......@@ -255,13 +243,9 @@ Background.prototype = {
var docUrl = topLevelRoot.docUrl || '';
var nextSite = this.isWhitelistedForNext_(docUrl);
var nextCompat = this.nextCompatRegExp_.test(docUrl) &&
this.chromeChannel_ != 'dev';
var classicCompat =
this.isWhitelistedForClassicCompat_(docUrl);
if (nextCompat && useNext)
return ChromeVoxMode.NEXT_COMPAT;
else if (classicCompat && !useNext)
if (classicCompat && !useNext)
return ChromeVoxMode.CLASSIC_COMPAT;
else if (nextSite)
return ChromeVoxMode.NEXT;
......@@ -289,8 +273,7 @@ Background.prototype = {
// misc states that are not handled above.
// Classic modes do not use the new focus highlight.
if (newMode == ChromeVoxMode.CLASSIC ||
newMode == ChromeVoxMode.NEXT_COMPAT)
if (newMode == ChromeVoxMode.CLASSIC)
chrome.accessibilityPrivate.setFocusRing([]);
// Switch on/off content scripts.
......@@ -304,23 +287,19 @@ Background.prototype = {
// Generally, we don't want to inject classic content scripts as it is
// done by the extension system at document load. The exception is when
// we toggle classic on manually as part of a user command.
// Note that classic -> next_compat is ignored here because classic
// should have already enabled content scripts.
if (oldMode == ChromeVoxMode.FORCE_NEXT) {
cvox.ChromeVox.injectChromeVoxIntoTabs(tabs);
}
} else if (newMode === ChromeVoxMode.FORCE_NEXT) {
// Disable ChromeVox everywhere except for things whitelisted
// for next compat.
this.disableClassicChromeVox_({forNextCompat: true});
} else if (newMode != ChromeVoxMode.NEXT_COMPAT) {
this.disableClassicChromeVox_();
} else {
// If we're focused in the desktop tree, do nothing.
if (cur && !cur.isWebRange())
return;
// If we're entering classic compat mode or next mode for just one tab,
// disable Classic for that tab only.
this.disableClassicChromeVox_({tabs: tabs});
this.disableClassicChromeVox_(tabs);
}
}.bind(this));
......@@ -581,10 +560,9 @@ Background.prototype = {
* @private
*/
shouldEnableClassicForUrl_: function(url) {
return (this.nextCompatRegExp_.test(url) &&this.chromeChannel_ != 'dev') ||
(this.mode != ChromeVoxMode.FORCE_NEXT &&
return this.mode != ChromeVoxMode.FORCE_NEXT &&
!this.isBlacklistedForClassic_(url) &&
!this.isWhitelistedForNext_(url));
!this.isWhitelistedForNext_(url);
},
/**
......@@ -625,27 +603,17 @@ Background.prototype = {
/**
* Disables classic ChromeVox in current web content.
* @param {{tabs: (Array<Tab>|undefined),
* forNextCompat: (boolean|undefined)}} params
* tabs: The tabs where ChromeVox scripts should be disabled. If null, will
* disable ChromeVox everywhere.
* forNextCompat: filters out tabs that have been listed for next compat (i.e.
* should retain content script).
* @param {Array<Tab>=} opt_tabs The tabs where ChromeVox scripts should be
* disabled. If null, will disable ChromeVox everywhere.
*/
disableClassicChromeVox_: function(params) {
disableClassicChromeVox_: function(opt_tabs) {
var disableChromeVoxCommand = {
message: 'SYSTEM_COMMAND',
command: 'killChromeVox'
};
if (params.forNextCompat) {
var reStr = this.nextCompatRegExp_.toString();
disableChromeVoxCommand['excludeUrlRegExp'] =
reStr.substring(1, reStr.length - 1);
}
if (params.tabs) {
for (var i = 0, tab; tab = params.tabs[i]; i++)
if (opt_tabs) {
for (var i = 0, tab; tab = opt_tabs[i]; i++)
chrome.tabs.sendMessage(tab.id, disableChromeVoxCommand);
} else {
// Send to all ChromeVox clients.
......
......@@ -564,7 +564,7 @@ TEST_F('BackgroundTest', 'ModeSwitching', function() {
localStorage['useClassic'] = false;
fakeWebRoot.docUrl = 'http://docs.google.com/document/#123123';
bk.setCurrentRange(cursors.Range.fromNode(fakeWebRoot));
assertEquals('next_compat', bk.mode);
assertEquals('force_next', bk.mode);
// And, back to force next.
fakeWebRoot.docUrl = 'http://docs.google.com/form/123';
......
......@@ -21,7 +21,6 @@ ChromeVoxMode = {
CLASSIC: 'classic',
CLASSIC_COMPAT: 'classic_compat',
NEXT: 'next',
NEXT_COMPAT: 'next_compat',
FORCE_NEXT: 'force_next'
};
......
......@@ -201,8 +201,7 @@ CommandHandler.onCommand = function(command) {
return true;
// Next/classic compat commands hereafter.
if (ChromeVoxState.instance.mode == ChromeVoxMode.CLASSIC ||
ChromeVoxState.instance.mode == ChromeVoxMode.NEXT_COMPAT)
if (ChromeVoxState.instance.mode == ChromeVoxMode.CLASSIC)
return true;
var current = ChromeVoxState.instance.currentRange_;
......
......@@ -39,7 +39,6 @@ BackgroundKeyboardHandler.prototype = {
return false;
if (ChromeVoxState.instance.mode != ChromeVoxMode.CLASSIC &&
ChromeVoxState.instance.mode != ChromeVoxMode.NEXT_COMPAT &&
!cvox.ChromeVoxKbHandler.basicKeyDownActionsListener(evt)) {
evt.preventDefault();
evt.stopPropagation();
......@@ -81,8 +80,7 @@ BackgroundKeyboardHandler.prototype = {
* @param {ChromeVoxMode?} oldMode
*/
onModeChanged: function(newMode, oldMode) {
if (newMode == ChromeVoxMode.CLASSIC ||
newMode == ChromeVoxMode.NEXT_COMPAT) {
if (newMode == ChromeVoxMode.CLASSIC) {
chrome.accessibilityPrivate.setKeyboardListener(false, false);
} else {
chrome.accessibilityPrivate.setKeyboardListener(
......@@ -90,8 +88,7 @@ BackgroundKeyboardHandler.prototype = {
}
if (newMode === ChromeVoxMode.NEXT ||
newMode === ChromeVoxMode.FORCE_NEXT ||
newMode === ChromeVoxMode.NEXT_COMPAT) {
newMode === ChromeVoxMode.FORCE_NEXT) {
// Switching out of classic, classic compat, or uninitialized
// (on startup).
window['prefs'].switchToKeyMap('keymap_next');
......
......@@ -2,14 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
goog.require('cvox.ApiImplementation');
goog.require('KeyboardHandler');
/**
* Initializes minimal content script.
*/
function initMin() {
if (cvox.ChromeVox.isChromeOS)
if (cvox.ChromeVox.isChromeOS) {
cvox.ApiImplementation.init();
return;
}
if (cvox.ChromeVox.isClassicEnabled_ === undefined) {
window.setTimeout(function() {
......
......@@ -31,7 +31,8 @@ cvox.SearchResults.speakResultBySelectTexts = function(result, selectTexts) {
if (selectText.select) {
var elems = result.querySelectorAll(selectText.select);
for (var i = 0; i < elems.length; i++) {
cvox.ChromeVox.speakNode(elems.item(i), cvox.QueueMode.QUEUE);
cvox.ChromeVox.tts.speak(cvox.DomUtil.getName(elems.item(i)),
cvox.QueueMode.QUEUE);
}
}
if (selectText.text) {
......@@ -96,8 +97,10 @@ cvox.NormalResult.prototype.speak = function(result) {
var discussTitles = result.querySelectorAll(DISCUSS_TITLE_SELECT);
var discussDates = result.querySelectorAll(DISCUSS_DATE_SELECT);
for (var i = 0; i < discussTitles.length; i++) {
cvox.ChromeVox.speakNode(discussTitles.item(i), cvox.QueueMode.QUEUE);
cvox.ChromeVox.speakNode(discussDates.item(i), cvox.QueueMode.QUEUE);
cvox.ChromeVox.tts.speak(
cvox.DomUtil.getName(discussTitles.item(i)), cvox.QueueMode.QUEUE);
cvox.ChromeVox.tts.speak(
cvox.DomUtil.getName(discussDates.item(i)), cvox.QueueMode.QUEUE);
}
return true;
};
......@@ -214,7 +217,9 @@ cvox.KnowResult.prototype.isType = function(result) {
* @override
*/
cvox.KnowResult.prototype.speak = function(result) {
cvox.ChromeVox.speakNode(result, cvox.QueueMode.QUEUE);
cvox.ChromeVox.tts.speak(
cvox.DomUtil.getName(result), cvox.QueueMode.QUEUE);
return true;
};
......@@ -375,7 +380,9 @@ cvox.CategoryResult.prototype.speak = function(result) {
}
var LABEL_SELECT = '.rg_bb_label';
var label = result.querySelector(LABEL_SELECT);
cvox.ChromeVox.speakNode(label, cvox.QueueMode.QUEUE);
cvox.ChromeVox.tts.speak(
cvox.DomUtil.getName(label), cvox.QueueMode.QUEUE);
return true;
};
......
......@@ -11,7 +11,6 @@
goog.provide('cvox.ChromeHost');
goog.require('cvox.AbstractHost');
goog.require('cvox.ApiImplementation');
goog.require('cvox.BrailleOverlayWidget');
goog.require('cvox.ChromeVox');
goog.require('cvox.ChromeVoxEventWatcher');
......@@ -91,20 +90,6 @@ cvox.ChromeHost.prototype.init = function() {
cvox.ChromeVox.modKeyStr = prefs['cvoxKey'];
}
var apiPrefsChanged = (
prefs['siteSpecificScriptLoader'] !=
cvox.ApiImplementation.siteSpecificScriptLoader ||
prefs['siteSpecificScriptBase'] !=
cvox.ApiImplementation.siteSpecificScriptBase);
cvox.ApiImplementation.siteSpecificScriptLoader =
prefs['siteSpecificScriptLoader'];
cvox.ApiImplementation.siteSpecificScriptBase =
prefs['siteSpecificScriptBase'];
if (apiPrefsChanged) {
var searchInit = prefs['siteSpecificEnhancements'] === 'true' ?
cvox.SearchLoader.init : undefined;
cvox.ApiImplementation.init(searchInit);
}
cvox.BrailleOverlayWidget.getInstance().setActive(
prefs['brailleCaptions'] == 'true');
}
......
......@@ -10,7 +10,6 @@
goog.provide('cvox.ChromeMathJax');
goog.require('cvox.AbstractMathJax');
goog.require('cvox.ApiImplementation');
goog.require('cvox.ChromeVox');
goog.require('cvox.HostFactory');
goog.require('cvox.ScriptInstaller');
......@@ -97,14 +96,6 @@ cvox.ChromeMathJax.prototype.retrieveCallback_ = function(idStr) {
*/
cvox.ChromeMathJax.prototype.init = function() {
window.addEventListener('message', goog.bind(this.portSetup, this), true);
var scripts = new Array();
scripts.push(cvox.ChromeVox.host.getFileSrc(
'chromevox/injected/mathjax_external_util.js'));
scripts.push(cvox.ChromeVox.host.getFileSrc('chromevox/injected/mathjax.js'));
scripts.push(cvox.ApiImplementation.siteSpecificScriptLoader);
this.initialized_ = cvox.ScriptInstaller.installScript(
scripts, 'mathjax', undefined,
cvox.ApiImplementation.siteSpecificScriptBase);
};
......
......@@ -145,7 +145,7 @@ cvox.AbstractHost.prototype.onStateChanged_ = function(state) {
cvox.ChromeVoxEventWatcher.init(window);
if (document.activeElement) {
var speakNodeAlso = cvox.ChromeVox.documentHasFocus();
cvox.ApiImplementation.syncToNode(
cvox.ChromeVox.navigationManager.syncToNode(
document.activeElement, speakNodeAlso);
} else {
cvox.ChromeVox.navigationManager.updateIndicator();
......
......@@ -58,7 +58,6 @@
"web_accessible_resources": [
"chromevox/background/keymaps/next_keymap.json",
"chromevox/injected/api.js",
"chromevox/injected/api_util.js",
"chromevox/injected/mathjax.js",
"chromevox/injected/mathjax_external_util.js"
],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment