diff options
Diffstat (limited to 'chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background')
19 files changed, 0 insertions, 5235 deletions
diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_predicate.js b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_predicate.js deleted file mode 100644 index f0ce021c40b..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_predicate.js +++ /dev/null @@ -1,202 +0,0 @@ -// 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 ChromeVox predicates for the automation extension API. - */ - -goog.provide('AutomationPredicate'); -goog.provide('AutomationPredicate.Binary'); -goog.provide('AutomationPredicate.Unary'); - -goog.scope(function() { -var RoleType = chrome.automation.RoleType; - -/** - * @constructor - */ -AutomationPredicate = function() {}; - -/** - * @typedef {function(chrome.automation.AutomationNode) : boolean} - */ -AutomationPredicate.Unary; - -/** - * @typedef {function(chrome.automation.AutomationNode, - * chrome.automation.AutomationNode) : boolean} - */ -AutomationPredicate.Binary; - -/** - * Constructs a predicate given a role. - * @param {RoleType} role - * @return {AutomationPredicate.Unary} - */ -AutomationPredicate.withRole = function(role) { - return function(node) { - return node.role == role; - }; -}; - -/** @type {AutomationPredicate.Unary} */ -AutomationPredicate.checkBox = AutomationPredicate.withRole(RoleType.checkBox); -/** @type {AutomationPredicate.Unary} */ -AutomationPredicate.comboBox = AutomationPredicate.withRole(RoleType.comboBox); -/** @type {AutomationPredicate.Unary} */ -AutomationPredicate.editText = AutomationPredicate.withRole(RoleType.textField); -/** @type {AutomationPredicate.Unary} */ -AutomationPredicate.heading = AutomationPredicate.withRole(RoleType.heading); -/** @type {AutomationPredicate.Unary} */ -AutomationPredicate.inlineTextBox = - AutomationPredicate.withRole(RoleType.inlineTextBox); -/** @type {AutomationPredicate.Unary} */ -AutomationPredicate.link = AutomationPredicate.withRole(RoleType.link); -/** @type {AutomationPredicate.Unary} */ -AutomationPredicate.table = AutomationPredicate.withRole(RoleType.table); - -/** - * @param {chrome.automation.AutomationNode} node - * @return {boolean} - */ -AutomationPredicate.button = function(node) { - return /button/i.test(node.role); -}; - -/** - * @param {chrome.automation.AutomationNode} node - * @return {boolean} - */ -AutomationPredicate.formField = function(node) { - switch (node.role) { - case 'button': - case 'buttonDropDown': - case 'checkBox': - case 'comboBox': - case 'date': - case 'dateTime': - case 'details': - case 'disclosureTriangle': - case 'form': - case 'menuButton': - case 'menuListPopup': - case 'popUpButton': - case 'radioButton': - case 'searchBox': - case 'slider': - case 'spinButton': - case 'switch': - case 'tab': - case 'textField': - case 'time': - case 'toggleButton': - case 'tree': - return true; - } - return false; -}; - -/** - * @param {chrome.automation.AutomationNode} node - * @return {boolean} - */ -AutomationPredicate.landmark = function(node) { - switch (node.role) { - case 'application': - case 'banner': - case 'complementary': - case 'contentInfo': - case 'form': - case 'main': - case 'navigation': - case 'search': - return true; - } - return false; -}; - -/** - * @param {chrome.automation.AutomationNode} node - * @return {boolean} - */ -AutomationPredicate.visitedLink = function(node) { - return node.state.visited; -}; - -/** - * @param {chrome.automation.AutomationNode} node - * @return {boolean} - */ -AutomationPredicate.focused = function(node) { - return node.state.focused; -}; - -/** - * @param {chrome.automation.AutomationNode} node - * @return {boolean} - */ -AutomationPredicate.leaf = function(node) { - return !node.firstChild || - node.role == RoleType.button || - node.role == RoleType.buttonDropDown || - node.role == RoleType.popUpButton || - node.role == RoleType.slider || - node.role == RoleType.textField || - node.children.every(function(n) { - return n.state.invisible; - }); -}; - -/** - * @param {chrome.automation.AutomationNode} node - * @return {boolean} - */ -AutomationPredicate.leafWithText = function(node) { - return AutomationPredicate.leaf(node) && - !!(node.name || node.value); -}; - -/** - * Matches against non-inline textbox 'nodes' which have an equivalent in the - * DOM. - * @param {chrome.automation.AutomationNode} node - * @return {boolean} - */ -AutomationPredicate.leafDomNode = function(node) { - return AutomationPredicate.leaf(node) || - node.role == RoleType.staticText; -}; - -/** - * @param {chrome.automation.AutomationNode} first - * @param {chrome.automation.AutomationNode} second - * @return {boolean} - */ -AutomationPredicate.linebreak = function(first, second) { - // TODO(dtseng): Use next/previousOnLin once available. - var fl = first.location; - var sl = second.location; - return fl.top != sl.top || - (fl.top + fl.height != sl.top + sl.height); -}; - -/** - * Leaf nodes that should be ignored while traversing the automation tree. For - * example, apply this predicate when moving to the next element. - * @param {chrome.automation.AutomationNode} node - * @return {boolean} - */ -AutomationPredicate.shouldIgnoreLeaf = function(node) { - if (node.name || node.value) - return false; - - return AutomationPredicate.leaf(node) && - (node.role == RoleType.client || - node.role == RoleType.div || - node.role == RoleType.group || - node.role == RoleType.image || - node.role == RoleType.staticText); -}; - -}); // goog.scope diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js deleted file mode 100644 index 17b52ffb4f6..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js +++ /dev/null @@ -1,277 +0,0 @@ -// 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 ChromeVox utilities for the automation extension API. - */ - -goog.provide('AutomationUtil'); -goog.provide('AutomationUtil.Dir'); - -goog.require('AutomationPredicate'); - -/** - * @constructor - */ -AutomationUtil = function() {}; - -/** - * Possible directions to perform tree traversals. - * @enum {string} - */ -AutomationUtil.Dir = { - // Search from left to right. - FORWARD: 'forward', - - // Search from right to left. - BACKWARD: 'backward' -}; - - -goog.scope(function() { -var AutomationNode = chrome.automation.AutomationNode; -var Dir = AutomationUtil.Dir; - -/** - * Find a node in subtree of |cur| satisfying |pred| using pre-order traversal. - * @param {AutomationNode} cur Node to begin the search from. - * @param {Dir} dir - * @param {AutomationPredicate.Unary} pred A predicate to apply - * to a candidate node. - * @return {AutomationNode} - */ -AutomationUtil.findNodePre = function(cur, dir, pred) { - if (pred(cur)) - return cur; - - var child = dir == Dir.BACKWARD ? cur.lastChild : cur.firstChild; - while (child) { - var ret = AutomationUtil.findNodePre(child, dir, pred); - if (ret) - return ret; - child = dir == Dir.BACKWARD ? - child.previousSibling : child.nextSibling; - } -}; - -/** - * Find a node in subtree of |cur| satisfying |pred| using post-order traversal. - * @param {AutomationNode} cur Node to begin the search from. - * @param {Dir} dir - * @param {AutomationPredicate.Unary} pred A predicate to apply - * to a candidate node. - * @return {AutomationNode} - */ -AutomationUtil.findNodePost = function(cur, dir, pred) { - var child = dir == Dir.BACKWARD ? cur.lastChild : cur.firstChild; - while (child) { - var ret = AutomationUtil.findNodePost(child, dir, pred); - if (ret) - return ret; - child = dir == Dir.BACKWARD ? - child.previousSibling : child.nextSibling; - } - - if (pred(cur)) - return cur; -}; - -/** - * Find the next node in the given direction that is either an immediate sibling - * or a sibling of an ancestor. - * @param {AutomationNode} cur Node to start search from. - * @param {Dir} dir - * @return {AutomationNode} - */ -AutomationUtil.findNextSubtree = function(cur, dir) { - while (cur) { - var next = dir == Dir.BACKWARD ? - cur.previousSibling : cur.nextSibling; - if (!AutomationUtil.isInSameTree(cur, next)) - return null; - if (next) - return next; - if (!AutomationUtil.isInSameTree(cur, cur.parent)) - return null; - cur = cur.parent; - if (AutomationUtil.isTraversalRoot_(cur)) - return null; - } -}; - -/** - * Find the next node in the given direction in depth first order. - * @param {AutomationNode} cur Node to begin the search from. - * @param {Dir} dir - * @param {AutomationPredicate.Unary} pred A predicate to apply - * to a candidate node. - * @return {AutomationNode} - */ -AutomationUtil.findNextNode = function(cur, dir, pred) { - var next = cur; - do { - if (!(next = AutomationUtil.findNextSubtree(cur, dir))) - return null; - cur = next; - next = AutomationUtil.findNodePre(next, dir, pred); - if (next && AutomationPredicate.shouldIgnoreLeaf(next)) { - cur = next; - next = null; - } - } while (!next); - return next; -}; - -/** - * Given nodes a_1, ..., a_n starting at |cur| in pre order traversal, apply - * |pred| to a_i and a_(i - 1) until |pred| is satisfied. Returns a_(i - 1) or - * a_i (depending on opt_options.before) or null if no match was found. - * @param {AutomationNode} cur - * @param {Dir} dir - * @param {AutomationPredicate.Binary} pred - * @param {{filter: (AutomationPredicate.Unary|undefined), - * before: boolean?}=} opt_options - * filter - Filters which candidate nodes to consider. Defaults to leaf - * only. - * before - True to return a_(i - 1); a_i otherwise. Defaults to false. - * @return {AutomationNode} - */ -AutomationUtil.findNodeUntil = function(cur, dir, pred, opt_options) { - opt_options = - opt_options || {filter: AutomationPredicate.leaf, before: false}; - if (!opt_options.filter) - opt_options.filter = AutomationPredicate.leaf; - - var before = null; - var after = null; - var prev = cur; - AutomationUtil.findNextNode(cur, - dir, - function(candidate) { - if (!opt_options.filter(candidate)) - return false; - - var satisfied = pred(prev, candidate); - - prev = candidate; - if (!satisfied) - before = candidate; - else - after = candidate; - return satisfied; - }); - return opt_options.before ? before : after; -}; - -/** - * Returns an array containing ancestors of node starting at root down to node. - * @param {!AutomationNode} node - * @return {!Array<AutomationNode>} - */ -AutomationUtil.getAncestors = function(node) { - var ret = []; - var candidate = node; - while (candidate) { - ret.push(candidate); - - if (!AutomationUtil.isInSameTree(candidate, candidate.parent)) - break; - - candidate = candidate.parent; - } - return ret.reverse(); -}; - -/** - * Gets the first index where the two input arrays differ. Returns -1 if they - * do not. - * @param {!Array<AutomationNode>} ancestorsA - * @param {!Array<AutomationNode>} ancestorsB - * @return {number} - */ -AutomationUtil.getDivergence = function(ancestorsA, ancestorsB) { - for (var i = 0; i < ancestorsA.length; i++) { - if (ancestorsA[i] !== ancestorsB[i]) - return i; - } - if (ancestorsA.length == ancestorsB.length) - return -1; - return ancestorsA.length; -}; - -/** - * Returns ancestors of |node| that are not also ancestors of |prevNode|. - * @param {!AutomationNode} prevNode - * @param {!AutomationNode} node - * @return {!Array<AutomationNode>} - */ -AutomationUtil.getUniqueAncestors = function(prevNode, node) { - var prevAncestors = AutomationUtil.getAncestors(prevNode); - var ancestors = AutomationUtil.getAncestors(node); - var divergence = AutomationUtil.getDivergence(prevAncestors, ancestors); - return ancestors.slice(divergence); -}; - -/** - * Given |nodeA| and |nodeB| in that order, determines their ordering in the - * document. - * @param {!AutomationNode} nodeA - * @param {!AutomationNode} nodeB - * @return {AutomationUtil.Dir} - */ -AutomationUtil.getDirection = function(nodeA, nodeB) { - var ancestorsA = AutomationUtil.getAncestors(nodeA); - var ancestorsB = AutomationUtil.getAncestors(nodeB); - var divergence = AutomationUtil.getDivergence(ancestorsA, ancestorsB); - - // Default to Dir.FORWARD. - if (divergence == -1) - return Dir.FORWARD; - - var divA = ancestorsA[divergence]; - var divB = ancestorsB[divergence]; - - // One of the nodes is an ancestor of the other. Don't distinguish and just - // consider it Dir.FORWARD. - if (!divA || !divB || divA.parent === nodeB || divB.parent === nodeA) - return Dir.FORWARD; - - return divA.indexInParent <= divB.indexInParent ? Dir.FORWARD : Dir.BACKWARD; -}; - -/** - * Determines whether the two given nodes come from the same tree source. - * @param {AutomationNode} a - * @param {AutomationNode} b - * @return {boolean} - */ -AutomationUtil.isInSameTree = function(a, b) { - if (!a || !b) - return true; - - // Given two non-desktop roots, consider them in the "same" tree. - return a.root === b.root || - (a.root.role == b.root.role && a.root.role == 'rootWebArea'); -}; - -/** - * Returns whether the given node should not be crossed when performing - * traversals up the ancestry chain. - * @param {AutomationNode} node - * @return {boolean} - * @private - */ -AutomationUtil.isTraversalRoot_ = function(node) { - switch (node.role) { - case 'dialog': - case 'window': - return true; - case 'toolbar': - return node.root.role == 'desktop'; - default: - return false; - } -}; - -}); // goog.scope diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs deleted file mode 100644 index 2c75b9fc26b..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs +++ /dev/null @@ -1,102 +0,0 @@ -// 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. - -// Include test fixture. -GEN_INCLUDE(['../../testing/chromevox_next_e2e_test_base.js']); - -/** - * Test fixture for automation_util.js. - * @constructor - * @extends {ChromeVoxE2ETestBase} - */ -function AutomationUtilE2ETest() { - ChromeVoxNextE2ETest.call(this); -} - -AutomationUtilE2ETest.prototype = { - __proto__: ChromeVoxNextE2ETest.prototype, - - /** @override */ - setUp: function() { - window.Dir = AutomationUtil.Dir; - }, - - basicDoc: function() {/*! - <p><a href='#'></a>hello</p> - <h1><ul><li>a</ul><button></h1> - */} -}; - -TEST_F('AutomationUtilE2ETest', 'GetAncestors', function() { - this.runWithLoadedTree(this.basicDoc, function(root) { - var expectedLength = 1; - while (root) { - var ancestors = AutomationUtil.getAncestors(root); - assertEquals(expectedLength++, ancestors.length); - root = root.firstChild; - } - }); -}); - -TEST_F('AutomationUtilE2ETest', 'GetUniqueAncestors', function() { - this.runWithLoadedTree(this.basicDoc, function(root) { - var leftmost = root, rightmost = root; - while (leftmost.firstChild) - leftmost = leftmost.firstChild; - while (rightmost.lastChild) - rightmost = rightmost.lastChild; - - var leftAncestors = AutomationUtil.getAncestors(leftmost); - var rightAncestors = AutomationUtil.getAncestors(rightmost); - assertEquals(chrome.automation.RoleType.link, leftmost.role); - assertEquals(chrome.automation.RoleType.button, rightmost.role); - assertEquals( - 1, AutomationUtil.getDivergence(leftAncestors, rightAncestors)); - assertEquals( - -1, AutomationUtil.getDivergence(leftAncestors, leftAncestors)); - - var uniqueAncestorsLeft = - AutomationUtil.getUniqueAncestors(rightmost, leftmost); - var uniqueAncestorsRight = - AutomationUtil.getUniqueAncestors(leftmost, rightmost); - - assertEquals(2, uniqueAncestorsLeft.length); - assertEquals(chrome.automation.RoleType.paragraph, - uniqueAncestorsLeft[0].role); - assertEquals(chrome.automation.RoleType.link, - uniqueAncestorsLeft[1].role); - - assertEquals(3, uniqueAncestorsRight.length); - assertEquals(chrome.automation.RoleType.heading, - uniqueAncestorsRight[0].role); - assertEquals(chrome.automation.RoleType.group, - uniqueAncestorsRight[1].role); - assertEquals(chrome.automation.RoleType.button, - uniqueAncestorsRight[2].role); - - assertEquals( - 1, AutomationUtil.getUniqueAncestors(leftmost, leftmost).length); - - }); -}); - -TEST_F('AutomationUtilE2ETest', 'GetDirection', function() { - this.runWithLoadedTree(this.basicDoc, function(root) { - var left = root, right = root; - - // Same node. - assertEquals(Dir.FORWARD, AutomationUtil.getDirection(left, right)); - - // Ancestor. - left = left.firstChild; - assertEquals(Dir.FORWARD, AutomationUtil.getDirection(left, right)); - assertEquals(Dir.FORWARD, AutomationUtil.getDirection(right, left)); - - // Ordered. - right = right.lastChild; - assertEquals(Dir.BACKWARD, AutomationUtil.getDirection(right, left)); - assertEquals(Dir.FORWARD, AutomationUtil.getDirection(left, right)); - - }); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.html b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.html deleted file mode 100644 index 84d67865599..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.html +++ /dev/null @@ -1,7 +0,0 @@ -<!-- Components to load uncompressed ChromeVox --> -<script src="../../closure/base.js"></script> -<script src="../../deps.js"></script> - -<!-- ChromeVox Next --> -<script src="loader.js"></script> -<script src="../../chromeVox2ChromeBackgroundScript.js"></script> diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js deleted file mode 100644 index aaf20bdd961..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js +++ /dev/null @@ -1,810 +0,0 @@ -// 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 The entry point for all ChromeVox2 related code for the - * background page. - */ - -goog.provide('Background'); -goog.provide('global'); - -goog.require('AutomationPredicate'); -goog.require('AutomationUtil'); -goog.require('ClassicCompatibility'); -goog.require('Output'); -goog.require('Output.EventType'); -goog.require('cursors.Cursor'); -goog.require('cvox.BrailleKeyCommand'); -goog.require('cvox.ChromeVoxEditableTextBase'); -goog.require('cvox.ExtensionBridge'); -goog.require('cvox.NavBraille'); - -goog.scope(function() { -var AutomationNode = chrome.automation.AutomationNode; -var Dir = AutomationUtil.Dir; -var EventType = chrome.automation.EventType; -var RoleType = chrome.automation.RoleType; - -/** - * All possible modes ChromeVox can run. - * @enum {string} - */ -var ChromeVoxMode = { - CLASSIC: 'classic', - COMPAT: 'compat', - NEXT: 'next', - FORCE_NEXT: 'force_next' -}; - -/** - * ChromeVox2 background page. - * @constructor - */ -Background = function() { - /** - * A list of site substring patterns to use with ChromeVox next. Keep these - * strings relatively specific. - * @type {!Array<string>} - * @private - */ - this.whitelist_ = ['chromevox_next_test']; - - /** - * @type {cursors.Range} - * @private - */ - this.currentRange_ = null; - - /** - * Which variant of ChromeVox is active. - * @type {ChromeVoxMode} - * @private - */ - this.mode_ = ChromeVoxMode.COMPAT; - - /** @type {!ClassicCompatibility} @private */ - this.compat_ = new ClassicCompatibility(); - - // Manually bind all functions to |this|. - for (var func in this) { - if (typeof(this[func]) == 'function') - this[func] = this[func].bind(this); - } - - /** - * Maps an automation event to its listener. - * @type {!Object<EventType, function(Object) : void>} - */ - this.listeners_ = { - alert: this.onAlert, - focus: this.onFocus, - hover: this.onEventDefault, - loadComplete: this.onLoadComplete, - menuStart: this.onEventDefault, - menuEnd: this.onEventDefault, - textChanged: this.onTextOrTextSelectionChanged, - textSelectionChanged: this.onTextOrTextSelectionChanged, - valueChanged: this.onValueChanged - }; - - /** - * The object that speaks changes to an editable text field. - * @type {?cvox.ChromeVoxEditableTextBase} - */ - this.editableTextHandler_ = null; - - chrome.automation.getDesktop(this.onGotDesktop); - - // Handle messages directed to the Next background page. - cvox.ExtensionBridge.addMessageListener(function(msg, port) { - var target = msg['target']; - var action = msg['action']; - - switch (target) { - case 'next': - if (action == 'getIsClassicEnabled') { - var url = msg['url']; - var isClassicEnabled = this.shouldEnableClassicForUrl_(url); - port.postMessage({ - target: 'next', - isClassicEnabled: isClassicEnabled - }); - } - break; - } - }.bind(this)); -}; - -Background.prototype = { - /** Forces ChromeVox Next to be active for all tabs. */ - forceChromeVoxNextActive: function() { - this.setChromeVoxMode(ChromeVoxMode.FORCE_NEXT); - }, - - /** - * Handles all setup once a new automation tree appears. - * @param {chrome.automation.AutomationNode} desktop - */ - onGotDesktop: function(desktop) { - // Register all automation event listeners. - for (var eventType in this.listeners_) - desktop.addEventListener(eventType, this.listeners_[eventType], true); - - // Register a tree change observer. - chrome.automation.addTreeChangeObserver(this.onTreeChange); - - // The focused state gets set on the containing webView node. - var webView = desktop.find({role: RoleType.webView, - state: {focused: true}}); - if (webView) { - var root = webView.find({role: RoleType.rootWebArea}); - if (root) { - this.onLoadComplete( - {target: root, - type: chrome.automation.EventType.loadComplete}); - } - } - }, - - /** - * Handles chrome.commands.onCommand. - * @param {string} command - * @param {boolean=} opt_skipCompat Whether to skip compatibility checks. - */ - onGotCommand: function(command, opt_skipCompat) { - if (!this.currentRange_) - return; - - if (!opt_skipCompat && this.mode_ === ChromeVoxMode.COMPAT) { - if (this.compat_.onGotCommand(command)) - return; - } - - var current = this.currentRange_; - var dir = Dir.FORWARD; - var pred = null; - var predErrorMsg = undefined; - switch (command) { - case 'nextCharacter': - current = current.move(cursors.Unit.CHARACTER, Dir.FORWARD); - break; - case 'previousCharacter': - current = current.move(cursors.Unit.CHARACTER, Dir.BACKWARD); - break; - case 'nextWord': - current = current.move(cursors.Unit.WORD, Dir.FORWARD); - break; - case 'previousWord': - current = current.move(cursors.Unit.WORD, Dir.BACKWARD); - break; - case 'nextLine': - current = current.move(cursors.Unit.LINE, Dir.FORWARD); - break; - case 'previousLine': - current = current.move(cursors.Unit.LINE, Dir.BACKWARD); - break; - case 'nextButton': - dir = Dir.FORWARD; - pred = AutomationPredicate.button; - predErrorMsg = 'no_next_button'; - break; - case 'previousButton': - dir = Dir.BACKWARD; - pred = AutomationPredicate.button; - predErrorMsg = 'no_previous_button'; - break; - case 'nextCheckBox': - dir = Dir.FORWARD; - pred = AutomationPredicate.checkBox; - predErrorMsg = 'no_next_checkbox'; - break; - case 'previousCheckBox': - dir = Dir.BACKWARD; - pred = AutomationPredicate.checkBox; - predErrorMsg = 'no_previous_checkbox'; - break; - case 'nextComboBox': - dir = Dir.FORWARD; - pred = AutomationPredicate.comboBox; - predErrorMsg = 'no_next_combo_box'; - break; - case 'previousComboBox': - dir = Dir.BACKWARD; - pred = AutomationPredicate.comboBox; - predErrorMsg = 'no_previous_combo_box'; - break; - case 'nextEditText': - dir = Dir.FORWARD; - pred = AutomationPredicate.editText; - predErrorMsg = 'no_next_edit_text'; - break; - case 'previousEditText': - dir = Dir.BACKWARD; - pred = AutomationPredicate.editText; - predErrorMsg = 'no_previous_edit_text'; - break; - case 'nextFormField': - dir = Dir.FORWARD; - pred = AutomationPredicate.formField; - predErrorMsg = 'no_next_form_field'; - break; - case 'previousFormField': - dir = Dir.BACKWARD; - pred = AutomationPredicate.formField; - predErrorMsg = 'no_previous_form_field'; - break; - case 'nextHeading': - dir = Dir.FORWARD; - pred = AutomationPredicate.heading; - predErrorMsg = 'no_next_heading'; - break; - case 'previousHeading': - dir = Dir.BACKWARD; - pred = AutomationPredicate.heading; - predErrorMsg = 'no_previous_heading'; - break; - case 'nextLink': - dir = Dir.FORWARD; - pred = AutomationPredicate.link; - predErrorMsg = 'no_next_link'; - break; - case 'previousLink': - dir = Dir.BACKWARD; - pred = AutomationPredicate.link; - predErrorMsg = 'no_previous_link'; - break; - case 'nextTable': - dir = Dir.FORWARD; - pred = AutomationPredicate.table; - predErrorMsg = 'no_next_table'; - break; - case 'previousTable': - dir = Dir.BACKWARD; - pred = AutomationPredicate.table; - predErrorMsg = 'no_previous_table'; - break; - case 'nextVisitedLink': - dir = Dir.FORWARD; - pred = AutomationPredicate.visitedLink; - predErrorMsg = 'no_next_visited_link'; - break; - case 'previousVisitedLink': - dir = Dir.BACKWARD; - pred = AutomationPredicate.visitedLink; - predErrorMsg = 'no_previous_visited_link'; - break; - case 'nextElement': - current = current.move(cursors.Unit.DOM_NODE, Dir.FORWARD); - break; - case 'previousElement': - current = current.move(cursors.Unit.DOM_NODE, Dir.BACKWARD); - break; - case 'goToBeginning': - var node = - AutomationUtil.findNodePost(current.start.node.root, - Dir.FORWARD, - AutomationPredicate.leaf); - if (node) - current = cursors.Range.fromNode(node); - break; - case 'goToEnd': - var node = - AutomationUtil.findNodePost(current.start.node.root, - Dir.BACKWARD, - AutomationPredicate.leaf); - if (node) - current = cursors.Range.fromNode(node); - break; - case 'doDefault': - if (this.currentRange_) { - var actionNode = this.currentRange_.start.node; - if (actionNode.role == RoleType.inlineTextBox) - actionNode = actionNode.parent; - actionNode.doDefault(); - } - // Skip all other processing; if focus changes, we should get an event - // for that. - return; - case 'continuousRead': - global.isReadingContinuously = true; - var continueReading = function(prevRange) { - if (!global.isReadingContinuously || !this.currentRange_) - return; - - new Output().withSpeechAndBraille( - this.currentRange_, prevRange, Output.EventType.NAVIGATE) - .onSpeechEnd(function() { continueReading(prevRange); }) - .go(); - prevRange = this.currentRange_; - this.currentRange_ = - this.currentRange_.move(cursors.Unit.NODE, Dir.FORWARD); - - if (!this.currentRange_ || this.currentRange_.equals(prevRange)) - global.isReadingContinuously = false; - }.bind(this); - - continueReading(null); - return; - case 'showContextMenu': - if (this.currentRange_) { - var actionNode = this.currentRange_.start.node; - if (actionNode.role == RoleType.inlineTextBox) - actionNode = actionNode.parent; - actionNode.showContextMenu(); - return; - } - break; - case 'showOptionsPage': - var optionsPage = {url: 'chromevox/background/options.html'}; - chrome.tabs.create(optionsPage); - break; - } - - if (pred) { - var node = AutomationUtil.findNextNode( - current.getBound(dir).node, dir, pred); - - if (node) { - current = cursors.Range.fromNode(node); - } else { - if (predErrorMsg) { - cvox.ChromeVox.tts.speak(Msgs.getMsg(predErrorMsg), - cvox.QueueMode.FLUSH); - } - return; - } - } - - if (current) { - // TODO(dtseng): Figure out what it means to focus a range. - var actionNode = current.start.node; - if (actionNode.role == RoleType.inlineTextBox) - actionNode = actionNode.parent; - actionNode.focus(); - - var prevRange = this.currentRange_; - this.currentRange_ = current; - - new Output().withSpeechAndBraille( - this.currentRange_, prevRange, Output.EventType.NAVIGATE) - .go(); - } - }, - - /** - * Handles a braille command. - * @param {!cvox.BrailleKeyEvent} evt - * @param {!cvox.NavBraille} content - * @return {boolean} True if evt was processed. - */ - onBrailleKeyEvent: function(evt, content) { - if (this.mode_ === ChromeVoxMode.CLASSIC) - return false; - - switch (evt.command) { - case cvox.BrailleKeyCommand.PAN_LEFT: - this.onGotCommand('previousElement', true); - break; - case cvox.BrailleKeyCommand.PAN_RIGHT: - this.onGotCommand('nextElement', true); - break; - case cvox.BrailleKeyCommand.LINE_UP: - this.onGotCommand('previousLine', true); - break; - case cvox.BrailleKeyCommand.LINE_DOWN: - this.onGotCommand('nextLine', true); - break; - case cvox.BrailleKeyCommand.TOP: - this.onGotCommand('goToBeginning', true); - break; - case cvox.BrailleKeyCommand.BOTTOM: - this.onGotCommand('goToEnd', true); - break; - case cvox.BrailleKeyCommand.ROUTING: - this.brailleRoutingCommand_( - content.text, - // Cast ok since displayPosition is always defined in this case. - /** @type {number} */ (evt.displayPosition)); - break; - default: - return false; - } - return true; - }, - - /** - * Provides all feedback once ChromeVox's focus changes. - * @param {Object} evt - */ - onEventDefault: function(evt) { - var node = evt.target; - - if (!node) - return; - - var prevRange = this.currentRange_; - - this.currentRange_ = cursors.Range.fromNode(node); - - // Check to see if we've crossed roots. Continue if we've crossed roots or - // are not within web content. - if (node.root.role == 'desktop' || - !prevRange || - prevRange.start.node.root != node.root) - this.setupChromeVoxVariants_(node.root.docUrl || ''); - - // Don't process nodes inside of web content if ChromeVox Next is inactive. - if (node.root.role != RoleType.desktop && - this.mode_ === ChromeVoxMode.CLASSIC) { - chrome.accessibilityPrivate.setFocusRing([]); - return; - } - - // Don't output if focused node hasn't changed. - if (prevRange && - evt.type == 'focus' && - this.currentRange_.equals(prevRange)) - return; - - new Output().withSpeechAndBraille( - this.currentRange_, prevRange, evt.type) - .go(); - }, - - /** - * Makes an announcement without changing focus. - * @param {Object} evt - */ - onAlert: function(evt) { - var node = evt.target; - if (!node) - return; - - // Don't process nodes inside of web content if ChromeVox Next is inactive. - if (node.root.role != RoleType.desktop && - this.mode_ === ChromeVoxMode.CLASSIC) { - return; - } - - var range = cursors.Range.fromNode(node); - - new Output().withSpeechAndBraille(range, null, evt.type).go(); - }, - - /** - * Provides all feedback once a focus event fires. - * @param {Object} evt - */ - onFocus: function(evt) { - // Invalidate any previous editable text handler state. - this.editableTextHandler_ = null; - - var node = evt.target; - - - // Discard focus events on embeddedObject nodes. - if (node.role == RoleType.embeddedObject) - return; - - // It almost never makes sense to place focus directly on a rootWebArea. - if (node.role == RoleType.rootWebArea) { - // Discard focus events for root web areas when focus was previously - // placed on a descendant. - if (this.currentRange_.start.node.root == node) - return; - - // Discard focused root nodes without focused state set. - if (!node.state.focused) - return; - - // Try to find a focusable descendant. - node = node.find({state: {focused: true}}) || node; - } - - if (evt.target.role == RoleType.textField) - this.createEditableTextHandlerIfNeeded_(evt.target); - - this.onEventDefault({target: node, type: 'focus'}); - }, - - /** - * Provides all feedback once a load complete event fires. - * @param {Object} evt - */ - onLoadComplete: function(evt) { - this.setupChromeVoxVariants_(evt.target.docUrl); - - // Don't process nodes inside of web content if ChromeVox Next is inactive. - if (evt.target.root.role != RoleType.desktop && - this.mode_ === ChromeVoxMode.CLASSIC) - return; - - // If initial focus was already placed on this page (e.g. if a user starts - // tabbing before load complete), then don't move ChromeVox's position on - // the page. - if (this.currentRange_ && - this.currentRange_.start.node.role != RoleType.rootWebArea && - this.currentRange_.start.node.root.docUrl == evt.target.docUrl) - return; - - var root = evt.target; - var webView = root; - while (webView && webView.role != RoleType.webView) - webView = webView.parent; - - if (!webView || !webView.state.focused) - return; - - var node = AutomationUtil.findNodePost(root, - Dir.FORWARD, - AutomationPredicate.leaf); - - if (node) - this.currentRange_ = cursors.Range.fromNode(node); - - if (this.currentRange_) - new Output().withSpeechAndBraille( - this.currentRange_, null, evt.type) - .go(); - }, - - /** - * Provides all feedback once a text selection change event fires. - * @param {Object} evt - */ - onTextOrTextSelectionChanged: function(evt) { - // Don't process nodes inside of web content if ChromeVox Next is inactive. - if (evt.target.root.role != RoleType.desktop && - this.mode_ === ChromeVoxMode.CLASSIC) - return; - - if (!evt.target.state.focused) - return; - - if (evt.target.role != RoleType.textField) - return; - - if (!this.currentRange_) { - this.onEventDefault(evt); - this.currentRange_ = cursors.Range.fromNode(evt.target); - } - - this.createEditableTextHandlerIfNeeded_(evt.target); - var textChangeEvent = new cvox.TextChangeEvent( - evt.target.value, - evt.target.textSelStart, - evt.target.textSelEnd, - true); // triggered by user - - this.editableTextHandler_.changed(textChangeEvent); - - new Output().withBraille( - this.currentRange_, null, evt.type) - .go(); - }, - - /** - * Provides all feedback once a value changed event fires. - * @param {Object} evt - */ - onValueChanged: function(evt) { - // Don't process nodes inside of web content if ChromeVox Next is inactive. - if (evt.target.root.role != RoleType.desktop && - this.mode_ === ChromeVoxMode.CLASSIC) - return; - - if (!evt.target.state.focused) - return; - - // Value change events fire on web text fields and text areas when pressing - // enter; suppress them. - if (!this.currentRange_ || - evt.target.role != RoleType.textField) { - this.onEventDefault(evt); - this.currentRange_ = cursors.Range.fromNode(evt.target); - } - }, - - /** - * Called when the automation tree is changed. - * @param {chrome.automation.TreeChange} treeChange - */ - onTreeChange: function(treeChange) { - if (this.mode_ === ChromeVoxMode.CLASSIC) - return; - - var node = treeChange.target; - if (!node.containerLiveStatus) - return; - - if (node.containerLiveRelevant.indexOf('additions') >= 0 && - treeChange.type == 'nodeCreated') - this.outputLiveRegionChange_(node, null); - if (node.containerLiveRelevant.indexOf('text') >= 0 && - treeChange.type == 'nodeChanged') - this.outputLiveRegionChange_(node, null); - if (node.containerLiveRelevant.indexOf('removals') >= 0 && - treeChange.type == 'nodeRemoved') - this.outputLiveRegionChange_(node, '@live_regions_removed'); - }, - - /** - * Given a node that needs to be spoken as part of a live region - * change and an additional optional format string, output the - * live region description. - * @param {!chrome.automation.AutomationNode} node The changed node. - * @param {?string} opt_prependFormatStr If set, a format string for - * cvox2.Output to prepend to the output. - * @private - */ - outputLiveRegionChange_: function(node, opt_prependFormatStr) { - var range = cursors.Range.fromNode(node); - var output = new Output(); - if (opt_prependFormatStr) { - output.format(opt_prependFormatStr); - } - output.withSpeech(range, null, Output.EventType.NAVIGATE); - output.go(); - }, - - /** - * Returns true if the url should have Classic running. - * @return {boolean} - * @private - */ - shouldEnableClassicForUrl_: function(url) { - return this.mode_ != ChromeVoxMode.FORCE_NEXT && - !this.isWhitelistedForCompat_(url) && - !this.isWhitelistedForNext_(url); - }, - - /** - * @return {boolean} - * @private - */ - isWhitelistedForCompat_: function(url) { - return url.indexOf('chrome://md-settings') != -1 || - url.indexOf('chrome://downloads') != -1 || - url.indexOf('chrome://oobe/login') != -1 || - url.indexOf( - 'https://accounts.google.com/embedded/setup/chromeos') === 0 || - url === ''; - }, - - /** - * @private - * @param {string} url - * @return {boolean} Whether the given |url| is whitelisted. - */ - isWhitelistedForNext_: function(url) { - return this.whitelist_.some(function(item) { - return url.indexOf(item) != -1; - }.bind(this)); - }, - - /** - * Setup ChromeVox variants. - * @param {string} url - * @private - */ - setupChromeVoxVariants_: function(url) { - var mode = this.mode_; - if (mode != ChromeVoxMode.FORCE_NEXT) { - if (this.isWhitelistedForCompat_(url)) - mode = ChromeVoxMode.COMPAT; - else if (this.isWhitelistedForNext_(url)) - mode = ChromeVoxMode.NEXT; - else - mode = ChromeVoxMode.CLASSIC; - } - - this.setChromeVoxMode(mode); - }, - - /** - * Disables classic ChromeVox in current web content. - */ - disableClassicChromeVox_: function() { - cvox.ExtensionBridge.send({ - message: 'SYSTEM_COMMAND', - command: 'killChromeVox' - }); - }, - - /** - * Sets the current ChromeVox mode. - * @param {ChromeVoxMode} mode - */ - setChromeVoxMode: function(mode) { - if (mode === ChromeVoxMode.NEXT || - mode === ChromeVoxMode.COMPAT || - mode === ChromeVoxMode.FORCE_NEXT) { - if (chrome.commands && - !chrome.commands.onCommand.hasListener(this.onGotCommand)) - chrome.commands.onCommand.addListener(this.onGotCommand); - } else { - if (chrome.commands && - chrome.commands.onCommand.hasListener(this.onGotCommand)) - chrome.commands.onCommand.removeListener(this.onGotCommand); - } - - chrome.tabs.query({active: true}, function(tabs) { - if (mode === ChromeVoxMode.CLASSIC) { - // This case should do nothing because Classic gets injected by the - // extension system via our manifest. Once ChromeVox Next is enabled - // for tabs, re-enable. - // cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); - } else { - // When in compat mode, if the focus is within the desktop tree proper, - // then do not disable content scripts. - if (this.currentRange_.start.node.root.role == 'desktop') - return; - - this.disableClassicChromeVox_(); - } - }.bind(this)); - - this.mode_ = mode; - }, - - /** - * @param {!cvox.Spannable} text - * @param {number} position - * @private - */ - brailleRoutingCommand_: function(text, position) { - var actionNode = null; - var selectionSpan = null; - text.getSpans(position).forEach(function(span) { - if (span instanceof Output.SelectionSpan) { - selectionSpan = span; - } else if (span instanceof Output.NodeSpan) { - if (!actionNode || - (text.getSpanEnd(actionNode) - text.getSpanStart(actionNode) > - text.getSpanEnd(span) - text.getSpanStart(span))) { - actionNode = span.node; - } - } - }); - if (!actionNode) - return; - if (actionNode.role === RoleType.inlineTextBox) - actionNode = actionNode.parent; - actionNode.doDefault(); - if (selectionSpan) { - var start = text.getSpanStart(selectionSpan); - actionNode.setSelection(position - start, position - start); - } - }, - - /** - * Create an editable text handler for the given node if needed. - * @param {Object} node - */ - createEditableTextHandlerIfNeeded_: function(node) { - if (!this.editableTextHandler_ || node != this.currentRange_.start.node) { - var start = node.textSelStart; - var end = node.textSelEnd; - if (start > end) { - var tempOffset = end; - end = start; - start = tempOffset; - } - - this.editableTextHandler_ = - new cvox.ChromeVoxEditableTextBase( - node.value, - start, - end, - node.state.protected, - cvox.ChromeVox.tts); - } - } -}; - -/** @type {Background} */ -global.backgroundObj = new Background(); - -}); // goog.scope diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs deleted file mode 100644 index 82810f95a0d..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs +++ /dev/null @@ -1,370 +0,0 @@ -// 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. - -// Include test fixture. -GEN_INCLUDE(['../../testing/chromevox_next_e2e_test_base.js', - '../../testing/assert_additions.js']); - -GEN_INCLUDE(['../../testing/mock_feedback.js']); - -/** - * Test fixture for Background. - * @constructor - * @extends {ChromeVoxNextE2ETest} - */ -function BackgroundTest() { - ChromeVoxNextE2ETest.call(this); -} - -BackgroundTest.prototype = { - __proto__: ChromeVoxNextE2ETest.prototype, - - /** @override */ - setUp: function() { - global.backgroundObj.forceChromeVoxNextActive(); - }, - - /** - * @return {!MockFeedback} - */ - createMockFeedback: function() { - var mockFeedback = new MockFeedback(this.newCallback(), - this.newCallback.bind(this)); - mockFeedback.install(); - return mockFeedback; - }, - - /** - * Create a function which perform the command |cmd|. - * @param {string} cmd - * @return {function() : void} - */ - doCmd: function(cmd) { - return function() { - global.backgroundObj.onGotCommand(cmd); - }; - }, - - linksAndHeadingsDoc: function() {/*! - <p>start</p> - <a href='#a'>alpha</a> - <a href='#b'>beta</a> - <p> - <h1>charlie</h1> - <a href='foo'>delta</a> - </p> - <a href='#bar'>echo</a> - <h2>foxtraut</h2> - <p>end<span>of test</span></p> - */}, - - formsDoc: function() {/*! - <select id="fruitSelect"> - <option>apple</option> - <option>grape</option> - <option> banana</option> - </select> - */} -}; - -/** Tests that ChromeVox classic is in this context. */ -SYNC_TEST_F('BackgroundTest', 'ClassicNamespaces', function() { - assertEquals('object', typeof(cvox)); - assertEquals('function', typeof(cvox.ChromeVoxBackground)); -}); - -/** Tests that ChromeVox next is in this context. */ -SYNC_TEST_F('BackgroundTest', 'NextNamespaces', function() { - assertEquals('function', typeof(Background)); -}); - -/** Tests consistency of navigating forward and backward. */ -TEST_F('BackgroundTest', 'ForwardBackwardNavigation', function() { - var mockFeedback = this.createMockFeedback(); - this.runWithLoadedTree(this.linksAndHeadingsDoc, function() { - var doCmd = this.doCmd.bind(this); - - mockFeedback.expectSpeech('start').expectBraille('start'); - - mockFeedback.call(doCmd('nextLink')) - .expectSpeech('alpha', 'Link') - .expectBraille('alpha lnk'); - mockFeedback.call(doCmd('nextLink')) - .expectSpeech('beta', 'Link') - .expectBraille('beta lnk'); - mockFeedback.call(doCmd('nextLink')) - .expectSpeech('delta', 'Link') - .expectBraille('delta lnk'); - mockFeedback.call(doCmd('previousLink')) - .expectSpeech('beta', 'Link') - .expectBraille('beta lnk'); - mockFeedback.call(doCmd('nextHeading')) - .expectSpeech('Heading 1', 'charlie') - .expectBraille('h1 charlie'); - mockFeedback.call(doCmd('nextHeading')) - .expectSpeech('Heading 2', 'foxtraut') - .expectBraille('h2 foxtraut'); - mockFeedback.call(doCmd('previousHeading')) - .expectSpeech('Heading 1', 'charlie') - .expectBraille('h1 charlie'); - - mockFeedback.call(doCmd('nextElement')) - .expectSpeech('delta', 'Link') - .expectBraille('delta lnk'); - mockFeedback.call(doCmd('nextElement')) - .expectSpeech('echo', 'Link') - .expectBraille('echo lnk'); - mockFeedback.call(doCmd('nextElement')) - .expectSpeech('Heading 2', 'foxtraut') - .expectBraille('h2 foxtraut'); - mockFeedback.call(doCmd('nextElement')) - .expectSpeech('end') - .expectBraille('end'); - mockFeedback.call(doCmd('previousElement')) - .expectSpeech('Heading 2', 'foxtraut') - .expectBraille('h2 foxtraut'); - mockFeedback.call(doCmd('nextLine')) - .expectSpeech('end', 'of test') - .expectBraille('end of test'); - - mockFeedback.call(doCmd('goToBeginning')) - .expectSpeech('start') - .expectBraille('start'); - mockFeedback.call(doCmd('goToEnd')) - .expectSpeech('of test') - .expectBraille('of test'); - - mockFeedback.replay(); - }); -}); - -TEST_F('BackgroundTest', 'CaretNavigation', function() { - // TODO(plundblad): Add braille expectaions when crbug.com/523285 is fixed. - var mockFeedback = this.createMockFeedback(); - this.runWithLoadedTree(this.linksAndHeadingsDoc, function() { - var doCmd = this.doCmd.bind(this); - - mockFeedback.expectSpeech('start'); - mockFeedback.call(doCmd('nextCharacter')) - .expectSpeech('t'); - mockFeedback.call(doCmd('nextCharacter')) - .expectSpeech('a'); - mockFeedback.call(doCmd('nextWord')) - .expectSpeech('Link', 'alpha'); - mockFeedback.call(doCmd('nextWord')) - .expectSpeech('Link', 'beta'); - mockFeedback.call(doCmd('nextWord')) - .expectSpeech('Heading 1', 'charlie'); - mockFeedback.call(doCmd('nextLine')) - .expectSpeech('Link', 'delta'); - mockFeedback.call(doCmd('nextLine')) - .expectSpeech('Link', 'echo'); - mockFeedback.call(doCmd('nextLine')) - .expectSpeech('Heading 2', 'foxtraut'); - mockFeedback.call(doCmd('nextLine')) - .expectSpeech('end', 'of test'); - mockFeedback.call(doCmd('nextCharacter')) - .expectSpeech('n'); - mockFeedback.call(doCmd('previousCharacter')) - .expectSpeech('e'); - mockFeedback.call(doCmd('previousCharacter')) - .expectSpeech('Heading 2', 't'); - mockFeedback.call(doCmd('previousWord')) - .expectSpeech('foxtraut'); - mockFeedback.call(doCmd('previousWord')) - .expectSpeech('Link', 'echo'); - mockFeedback.call(doCmd('previousCharacter')) - .expectSpeech('Link', 'a'); - mockFeedback.call(doCmd('previousCharacter')) - .expectSpeech('t'); - mockFeedback.call(doCmd('nextWord')) - .expectSpeech('Link', 'echo'); - mockFeedback.replay(); - }); -}); - -TEST_F('BackgroundTest', 'SelectSingleBasic', function() { - var mockFeedback = this.createMockFeedback(); - this.runWithLoadedTree(this.formsDoc, function() { - var sendDownToSelect = - this.sendKeyToElement.bind(this, undefined, 'Down', '#fruitSelect'); - mockFeedback.expectSpeech('apple', 'Menu item', /1 of 3/) - .expectBraille('apple mnuitm 1/3') - .call(sendDownToSelect) - .expectSpeech('grape', /2 of 3/) - .expectBraille('grape mnuitm 2/3') - .call(sendDownToSelect) - .expectSpeech('banana', /3 of 3/) - .expectBraille('banana mnuitm 3/3'); - mockFeedback.replay(); - }); -}); - -TEST_F('BackgroundTest', 'ContinuousRead', function() { - var mockFeedback = this.createMockFeedback(); - this.runWithLoadedTree(this.linksAndHeadingsDoc, function() { - mockFeedback.expectSpeech('start') - .call(this.doCmd('continuousRead')) - .expectSpeech( - 'start', - 'alpha', 'Link', - 'beta', 'Link', - 'Heading 1', 'charlie'); - mockFeedback.replay(); - }); -}); - -TEST_F('BackgroundTest', 'LiveRegionAddElement', function() { - var mockFeedback = this.createMockFeedback(); - this.runWithLoadedTree( - function() {/*! - <h1>Document with live region</h1> - <p id="live" aria-live="polite"></p> - <button id="go">Go</button> - <script> - document.getElementById('go').addEventListener('click', function() { - document.getElementById('live').innerHTML = 'Hello, world'; - }, false); - </script> - */}, - function(rootNode) { - var go = rootNode.find({ role: chrome.automation.RoleType.button }); - mockFeedback.call(go.doDefault.bind(go)) - .expectSpeech('Hello, world'); - mockFeedback.replay(); - }); -}); - -TEST_F('BackgroundTest', 'LiveRegionRemoveElement', function() { - var mockFeedback = this.createMockFeedback(); - this.runWithLoadedTree( - function() {/*! - <h1>Document with live region</h1> - <p id="live" aria-live="polite" aria-relevant="removals">Hello, world</p> - <button id="go">Go</button> - <script> - document.getElementById('go').addEventListener('click', function() { - document.getElementById('live').innerHTML = ''; - }, false); - </script> - */}, - function(rootNode) { - var go = rootNode.find({ role: chrome.automation.RoleType.button }); - go.doDefault(); - mockFeedback.expectSpeech('removed:') - .expectSpeech('Hello, world'); - mockFeedback.replay(); - }); -}); - -TEST_F('BackgroundTest', 'InitialFocus', function() { - var mockFeedback = this.createMockFeedback(); - this.runWithLoadedTree('<a href="a">a</a>', - function(rootNode) { - mockFeedback.expectSpeech('a') - .expectSpeech('Link'); - mockFeedback.replay(); - }); -}); - -TEST_F('BackgroundTest', 'AriaLabel', function() { - var mockFeedback = this.createMockFeedback(); - this.runWithLoadedTree('<a aria-label="foo" href="a">a</a>', - function(rootNode) { - rootNode.find({role: 'link'}).focus(); - mockFeedback.expectSpeech('foo') - .expectSpeech('Link') - .expectBraille('foo lnk'); - mockFeedback.replay(); - } - ); -}); - -TEST_F('BackgroundTest', 'ShowContextMenu', function() { - var mockFeedback = this.createMockFeedback(); - this.runWithLoadedTree('<a href="a">a</a>', - function(rootNode) { - mockFeedback.expectSpeech(/menu opened/) - .call(function() { - // When shown, the context menu pushes a new message loop so test - // messages sent to the browser do not get processed. Ensure we - // exit the context menu here. - go.showContextMenu(); - }); - mockFeedback.replay(); - - var go = rootNode.find({ role: chrome.automation.RoleType.link }); - this.listenOnce(go, 'focus', function(e) { - this.doCmd('showContextMenu')(); - }.bind(this), true); - go.focus(); - }.bind(this)); -}); - -TEST_F('BackgroundTest', 'BrailleRouting', function() { - var mockFeedback = this.createMockFeedback(); - var route = function(position) { - assertTrue(global.backgroundObj.onBrailleKeyEvent( - {command: cvox.BrailleKeyCommand.ROUTING, - displayPosition: position}, - mockFeedback.lastMatchedBraille)); - }; - this.runWithLoadedTree( - function() {/*! - <p>start</p> - <button id="btn1">Click me</button> - <p>Some text</p> - <button id="btn2">Focus me</button> - <p>Some more text</p> - <input type="text" id ="text" value="Edit me"> - <script> - document.getElementById('btn1').addEventListener('click', function() { - document.getElementById('btn2').focus(); - }, false); - </script> - */}, - function(rootNode) { - var button1 = rootNode.find({role: chrome.automation.RoleType.button, - name: 'Click me'}); - var textField = rootNode.find( - {role: chrome.automation.RoleType.textField}); - mockFeedback.expectBraille('start') - .call(button1.focus.bind(button1)) - .expectBraille(/^Click me btn/) - .call(route.bind(null, 5)) - .expectBraille(/Focus me btn/) - .call(textField.focus.bind(textField)) - .expectBraille('Edit me ed', {startIndex: 0}) - .call(route.bind(null, 3)) - .expectBraille('Edit me ed', {startIndex: 3}) - .call(function() { - assertEquals(3, textField.textSelStart); - }); - mockFeedback.replay(); - }); -}); - -TEST_F('BackgroundTest', 'FocusInputElement', function() { - var mockFeedback = this.createMockFeedback(); - this.runWithLoadedTree( - function() {/*! - <input id="name" value="Lancelot"> - <input id="quest" value="Grail"> - <input id="color" value="Blue"> - */}, - function(rootNode) { - var name = rootNode.find({ attributes: { value: 'Lancelot' } }); - var quest = rootNode.find({ attributes: { value: 'Grail' } }); - var color = rootNode.find({ attributes: { value: 'Blue' } }); - - mockFeedback.call(quest.focus.bind(quest)) - .expectSpeech('Grail', 'Edit text') - .call(color.focus.bind(color)) - .expectSpeech('Blue', 'Edit text') - .call(name.focus.bind(name)) - .expectNextSpeechUtteranceIsNot('Blue') - .expectSpeech('Lancelot', 'Edit text'); - mockFeedback.replay(); - }.bind(this)); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/classic_compatibility.js b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/classic_compatibility.js deleted file mode 100644 index 35eb9ca3bad..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/classic_compatibility.js +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2015 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 Provides a compatibility layer for ChromeVox Classic during the - * transition to ChromeVox Next. - */ - -goog.provide('ClassicCompatibility'); - -goog.require('cvox.ExtensionBridge'); -goog.require('cvox.KeyMap'); -goog.require('cvox.KeySequence'); -goog.require('cvox.KeyUtil'); -goog.require('cvox.SimpleKeyEvent'); - -/** - * @constructor - */ -var ClassicCompatibility = function() { - /** - * @type {!Array<{description: string, name: string, shortcut: string}>} - * @private - */ - this.commands_ = []; - - // We grab the list of commands from the manifest because - // chrome.commands.getAll is buggy. - /** @type {!Object} */ - var commands = chrome.runtime.getManifest()['commands']; - for (var key in commands) { - /** @type {{suggested_key: {chromeos: string}}} */ - var command = commands[key]; - this.commands_.push({name: key, shortcut: command.suggested_key.chromeos}); - } -}; - -ClassicCompatibility.prototype = { - /** - * Processes a ChromeVox Next command. - * @param {string} command - * @return {boolean} Whether the command was successfully processed. - */ - onGotCommand: function(command) { - var evt = this.buildKeyEvent_(command); - if (evt) { - this.simulateKeyDownNext_(evt); - return true; - } - cvox.KeyUtil.sequencing = false; - }, - - /** - * Processes a ChromeVox Next command while in CLASSIC mode. - * @param {string} command - * @return {boolean} Whether the command was successfully processed. - */ - onGotClassicCommand: function(command) { - var evt = this.buildKeyEvent_(command); - if (!evt) - return false; - this.simulateKeyDownClassic_(evt); - return true; - }, - - /** - * @param {string} command - * @return {cvox.SimpleKeyEvent?} - */ - buildKeyEvent_: function(command) { - var commandInfo = this.commands_.filter(function(c) { - return c.name == command; - }.bind(this))[0]; - if (!commandInfo) - return null; - var shortcut = commandInfo.shortcut; - return this.convertCommandShortcutToKeyEvent_(shortcut); - }, - - /** - * @param {cvox.SimpleKeyEvent} evt - * @private - */ - simulateKeyDownNext_: function(evt) { - var keySequence = cvox.KeyUtil.keyEventToKeySequence(evt); - var classicCommand = - cvox.KeyMap.fromCurrentKeyMap().commandForKey(keySequence); - if (classicCommand) { - var nextCommand = this.getNextCommand_(classicCommand); - if (nextCommand) - global.backgroundObj.onGotCommand(nextCommand, true); - } - }, - - /** - * @param {cvox.SimpleKeyEvent} evt - * @private - */ - simulateKeyDownClassic_: function(evt) { - var keySequence = cvox.KeyUtil.keyEventToKeySequence(evt); - var classicCommand = - cvox.KeyMap.fromCurrentKeyMap().commandForKey(keySequence); - if (classicCommand) { - cvox.ExtensionBridge.send({ - 'message': 'USER_COMMAND', - 'command': classicCommand - }); - } - }, - - /** - * @param {string} shortcut - * @return {cvox.SimpleKeyEvent} - * @private - */ - convertCommandShortcutToKeyEvent_: function(shortcut) { - var evt = {}; - shortcut.split('+').forEach(function(token) { - // Known tokens. - switch (token) { - case 'Ctrl': - evt.ctrlKey = true; - break; - case 'Shift': - evt.shiftKey = true; - break; - case 'Alt': - evt.altKey = true; - break; - case 'Search': - evt.searchKeyHeld = true; - break; - case 'Space': - evt.keyCode = 32; - break; - case 'Left': - evt.keyCode = 37; - break; - case 'Up': - evt.keyCode = 38; - break; - case 'Right': - evt.keyCode = 39; - break; - case 'Down': - evt.keyCode = 40; - break; - default: - evt.keyCode = token.charCodeAt(0); - } - }); - - return evt; - }, - - /** - * Maps a Classic command to an approximate equivalent in Next. - * @param {string} classicCommand - * @return {string} - * @private - */ - getNextCommand_: function(classicCommand) { - switch (classicCommand) { - case 'right': - return 'nextElement'; - case 'forward': - return 'nextLine'; - case 'left': - return 'previousElement'; - case 'backward': - return 'previousLine'; - case 'forceClickOnCurrentItem': - return 'doDefault'; - case 'readFromHere': - return 'continuousRead'; - default: - return classicCommand; - } - } -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js deleted file mode 100644 index d92d55adcce..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js +++ /dev/null @@ -1,397 +0,0 @@ -// 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 Classes related to cursors that point to and select parts of - * the automation tree. - */ - -goog.provide('cursors.Cursor'); -goog.provide('cursors.Movement'); -goog.provide('cursors.Range'); -goog.provide('cursors.Unit'); - -goog.require('AutomationUtil'); - -/** - * The special index that represents a cursor pointing to a node without - * pointing to any part of its accessible text. - */ -cursors.NODE_INDEX = -1; - -/** - * Represents units of CursorMovement. - * @enum {string} - */ -cursors.Unit = { - /** A single character within accessible name or value. */ - CHARACTER: 'character', - - /** A range of characters (given by attributes on automation nodes). */ - WORD: 'word', - - /** A leaf node. */ - NODE: 'node', - - /** A leaf DOM-node. */ - DOM_NODE: 'dom_node', - - /** Formed by a set of leaf nodes that are inline. */ - LINE: 'line' -}; - -/** - * Represents the ways in which cursors can move given a cursor unit. - * @enum {string} - */ -cursors.Movement = { - /** Move to the beginning or end of the current unit. */ - BOUND: 'bound', - - /** Move to the next unit in a particular direction. */ - DIRECTIONAL: 'directional' -}; - -goog.scope(function() { -var AutomationNode = chrome.automation.AutomationNode; -var Dir = AutomationUtil.Dir; -var Movement = cursors.Movement; -var Role = chrome.automation.RoleType; -var Unit = cursors.Unit; - -/** - * Represents a position within the automation tree. - * @constructor - * @param {!AutomationNode} node - * @param {number} index A 0-based index into either this cursor's name or value - * attribute. Relies on the fact that a node has either a name or a value but - * not both. An index of |cursors.NODE_INDEX| means the node as a whole is - * pointed to and covers the case where the accessible text is empty. - */ -cursors.Cursor = function(node, index) { - /** @type {!AutomationNode} @private */ - this.node_ = node; - /** @type {number} @private */ - this.index_ = index; -}; - -/** - * Convenience method to construct a Cursor from a node. - * @param {!AutomationNode} node - * @return {!cursors.Cursor} - */ -cursors.Cursor.fromNode = function(node) { - return new cursors.Cursor(node, cursors.NODE_INDEX); -}; - -cursors.Cursor.prototype = { - /** - * Returns true if |rhs| is equal to this cursor. - * @param {!cursors.Cursor} rhs - * @return {boolean} - */ - equals: function(rhs) { - return this.node_ === rhs.node && - this.index_ === rhs.getIndex(); - }, - - /** - * @return {!AutomationNode} - */ - get node() { - return this.node_; - }, - - /** - * @return {number} - */ - getIndex: function() { - return this.index_; - }, - - /** - * Gets the accessible text of the node associated with this cursor. - * - * Note that only one of |name| or |value| attribute is ever nonempty on an - * automation node. If either contains whitespace, we still treat it as we do - * for a nonempty string. - * @param {!AutomationNode=} opt_node Use this node rather than this cursor's - * node. - * @return {string} - */ - getText: function(opt_node) { - var node = opt_node || this.node_; - return node.name || node.value || ''; - }, - - /** - * Makes a Cursor which has been moved from this cursor by the unit in the - * given direction using the given movement type. - * @param {Unit} unit - * @param {Movement} movement - * @param {Dir} dir - * @return {!cursors.Cursor} The moved cursor. - */ - move: function(unit, movement, dir) { - var newNode = this.node_; - var newIndex = this.index_; - - if ((unit != Unit.NODE || unit != Unit.DOM_NODE) && - newIndex === cursors.NODE_INDEX) - newIndex = 0; - - switch (unit) { - case Unit.CHARACTER: - // BOUND and DIRECTIONAL are the same for characters. - newIndex = dir == Dir.FORWARD ? newIndex + 1 : newIndex - 1; - if (newIndex < 0 || newIndex >= this.getText().length) { - newNode = AutomationUtil.findNextNode( - newNode, dir, AutomationPredicate.leafWithText); - if (newNode) { - newIndex = - dir == Dir.FORWARD ? 0 : this.getText(newNode).length - 1; - newIndex = newIndex == -1 ? 0 : newIndex; - } else { - newIndex = this.index_; - } - } - break; - case Unit.WORD: - switch (movement) { - case Movement.BOUND: - if (newNode.role == Role.inlineTextBox) { - var start, end; - for (var i = 0; i < newNode.wordStarts.length; i++) { - if (newIndex >= newNode.wordStarts[i] && - newIndex <= newNode.wordEnds[i]) { - start = newNode.wordStarts[i]; - end = newNode.wordEnds[i]; - break; - } - } - if (goog.isDef(start) && goog.isDef(end)) - newIndex = dir == Dir.FORWARD ? end : start; - } else { - // TODO(dtseng): Figure out what to do in this case. - } - break; - case Movement.DIRECTIONAL: - if (newNode.role == Role.inlineTextBox) { - var start, end; - for (var i = 0; i < newNode.wordStarts.length; i++) { - if (newIndex >= newNode.wordStarts[i] && - newIndex <= newNode.wordEnds[i]) { - var nextIndex = dir == Dir.FORWARD ? i + 1 : i - 1; - start = newNode.wordStarts[nextIndex]; - end = newNode.wordEnds[nextIndex]; - break; - } - } - if (goog.isDef(start)) { - newIndex = start; - } else { - // The backward case is special at the beginning of nodes. - if (dir == Dir.BACKWARD && newIndex != 0) { - newIndex = 0; - } else { - newNode = AutomationUtil.findNextNode(newNode, dir, - AutomationPredicate.leaf); - if (newNode) { - newIndex = 0; - if (dir == Dir.BACKWARD && - newNode.role == Role.inlineTextBox) { - var starts = newNode.wordStarts; - newIndex = starts[starts.length - 1] || 0; - } else { - // TODO(dtseng): Figure out what to do for general nodes. - } - } - } - } - } else { - // TODO(dtseng): Figure out what to do in this case. - } - } - break; - case Unit.NODE: - case Unit.DOM_NODE: - switch (movement) { - case Movement.BOUND: - newIndex = dir == Dir.FORWARD ? this.getText().length - 1 : 0; - break; - case Movement.DIRECTIONAL: - var pred = unit == Unit.NODE ? - AutomationPredicate.leaf : AutomationPredicate.leafDomNode; - newNode = AutomationUtil.findNextNode( - newNode, dir, pred) || this.node_; - newIndex = cursors.NODE_INDEX; - break; - } - break; - case Unit.LINE: - newIndex = 0; - switch (movement) { - case Movement.BOUND: - newNode = AutomationUtil.findNodeUntil(newNode, dir, - AutomationPredicate.linebreak, {before: true}); - newNode = newNode || this.node_; - newIndex = - dir == Dir.FORWARD ? this.getText(newNode).length : 0; - break; - case Movement.DIRECTIONAL: - newNode = AutomationUtil.findNodeUntil( - newNode, dir, AutomationPredicate.linebreak); - break; - } - break; - default: - throw 'Unrecognized unit: ' + unit; - } - newNode = newNode || this.node_; - newIndex = goog.isDef(newIndex) ? newIndex : this.index_; - return new cursors.Cursor(newNode, newIndex); - } -}; - -/** - * Represents a range in the automation tree. There is no visible selection on - * the page caused by usage of this object. - * It is assumed that the caller provides |start| and |end| in document order. - * @param {!cursors.Cursor} start - * @param {!cursors.Cursor} end - * @constructor - */ -cursors.Range = function(start, end) { - /** @type {!cursors.Cursor} @private */ - this.start_ = start; - /** @type {!cursors.Cursor} @private */ - this.end_ = end; -}; - -/** - * Convenience method to construct a Range surrounding one node. - * @param {!AutomationNode} node - * @return {!cursors.Range} - */ -cursors.Range.fromNode = function(node) { - var cursor = cursors.Cursor.fromNode(node); - return new cursors.Range(cursor, cursor); -}; - - /** - * Given |rangeA| and |rangeB| in order, determine which |Dir| - * relates them. - * @param {!cursors.Range} rangeA - * @param {!cursors.Range} rangeB - * @return {Dir} - */ -cursors.Range.getDirection = function(rangeA, rangeB) { - if (!rangeA || !rangeB) - return Dir.FORWARD; - - // They are the same range. - if (rangeA.start.node === rangeB.start.node && - rangeB.end.node === rangeA.end.node) - return Dir.FORWARD; - - var testDirA = - AutomationUtil.getDirection( - rangeA.start.node, rangeB.end.node); - var testDirB = - AutomationUtil.getDirection( - rangeB.start.node, rangeA.end.node); - - // The two ranges are either partly overlapping or non overlapping. - if (testDirA == Dir.FORWARD && testDirB == Dir.BACKWARD) - return Dir.FORWARD; - else if (testDirA == Dir.BACKWARD && testDirB == Dir.FORWARD) - return Dir.BACKWARD; - else - return testDirA; -}; - -cursors.Range.prototype = { - /** - * Returns true if |rhs| is equal to this range. - * @param {!cursors.Range} rhs - * @return {boolean} - */ - equals: function(rhs) { - return this.start_.equals(rhs.start) && - this.end_.equals(rhs.end); - }, - - /** - * Gets a cursor bounding this range. - * @param {Dir} dir Which endpoint cursor to return; Dir.FORWARD for end, - * Dir.BACKWARD for start. - * @param {boolean=} opt_reverse Specify to have Dir.BACKWARD return end, - * Dir.FORWARD return start. - * @return {!cursors.Cursor} - */ - getBound: function(dir, opt_reverse) { - if (opt_reverse) - return dir == Dir.BACKWARD ? this.end_ : this.start_; - return dir == Dir.FORWARD ? this.end_ : this.start_; - }, - - /** - * @return {!cursors.Cursor} - */ - get start() { - return this.start_; - }, - - /** - * @return {!cursors.Cursor} - */ - get end() { - return this.end_; - }, - - /** - * Returns true if this range covers less than a node. - * @return {boolean} - */ - isSubNode: function() { - return this.start.node === this.end.node && - this.start.getIndex() > -1 && - this.end.getIndex() > -1; - }, - - /** - * Makes a Range which has been moved from this range by the given unit and - * direction. - * @param {Unit} unit - * @param {Dir} dir - * @return {cursors.Range} - */ - move: function(unit, dir) { - var newStart = this.start_; - var newEnd = newStart; - switch (unit) { - case Unit.CHARACTER: - newStart = newStart.move(unit, Movement.BOUND, dir); - newEnd = newStart.move(unit, Movement.BOUND, Dir.FORWARD); - // Character crossed a node; collapses to the end of the node. - if (newStart.node !== newEnd.node) - newEnd = newStart; - break; - case Unit.WORD: - case Unit.LINE: - newStart = newStart.move(unit, Movement.DIRECTIONAL, dir); - newStart = newStart.move(unit, Movement.BOUND, Dir.BACKWARD); - newEnd = newStart.move(unit, Movement.BOUND, Dir.FORWARD); - break; - case Unit.NODE: - case Unit.DOM_NODE: - newStart = newStart.move(unit, Movement.DIRECTIONAL, dir); - newEnd = newStart; - break; - } - return new cursors.Range(newStart, newEnd); - } -}; - -}); // goog.scope diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs deleted file mode 100644 index 346001d4d1b..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs +++ /dev/null @@ -1,294 +0,0 @@ -// 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. - -// Include test fixture. -GEN_INCLUDE(['../../testing/chromevox_next_e2e_test_base.js']); - -/** - * Test fixture for cvox2.cursors. - * @constructor - * @extends {ChromeVoxNextE2ETest} - */ -function CursorsTest() { - ChromeVoxNextE2ETest.call(this); -} - -CursorsTest.prototype = { - __proto__: ChromeVoxNextE2ETest.prototype, - - /** Test cursors.Cursor. @const {string} */ - CURSOR: 'cursor', - - /** Test cursors.Range. @const {string} */ - RANGE: 'range', - - /** @override */ - setUp: function() { - // Various aliases. - window.BACKWARD = AutomationUtil.Dir.BACKWARD; - window.FORWARD = AutomationUtil.Dir.FORWARD; - window.CHARACTER = cursors.Unit.CHARACTER; - window.WORD = cursors.Unit.WORD; - window.LINE = cursors.Unit.LINE; - window.DOM_NODE = cursors.Unit.DOM_NODE; - window.BOUND = cursors.Movement.BOUND; - window.DIRECTIONAL = cursors.Movement.DIRECTIONAL; - }, - - /** - * Performs a series of operations on a cursor and asserts the result. - * @param {cursors.Cursor} cursor The starting cursor. - * @param {!Array<Array< - * cursors.Unit| - * cursors.Movement| - * automationUtil.Dir| - * Object>>} - * moves An array of arrays. Each inner array contains 4 items: unit, - * movement, direction, and assertions object. See example below. - */ - cursorMoveAndAssert: function(cursor, moves) { - var move = null; - while (move = moves.shift()) { - cursor = cursor.move(move[0], move[1], move[2]); - var expected = move[3]; - this.makeCursorAssertion(expected, cursor); - } - }, - - /** - * Performs a series of operations on a range and asserts the result. - * @param {cursors.Range} range The starting range. - * @param {!Array<Array< - * cursors.Unit| - * cursors.Movement| - * automationUtil.Dir| - * Object>>} - * moves An array of arrays. Each inner array contains 4 items: unit, - * direction, start and end assertions objects. See example below. - */ - rangeMoveAndAssert: function(range, moves) { - var move = null; - while (move = moves.shift()) { - range = range.move(move[0], move[1]); - var expectedStart = move[2]; - var expectedEnd = move[3]; - - this.makeCursorAssertion(expectedStart, range.start); - this.makeCursorAssertion(expectedEnd, range.end); - } - }, - - /** - * Makes assertions about the given |cursor|. - * @param {Object} expected - * @param {Cursor} cursor - */ - makeCursorAssertion: function(expected, cursor) { - if (goog.isDef(expected.index)) - assertEquals(expected.index, cursor.getIndex()); - if (goog.isDef(expected.value)) - assertEquals(expected.value, cursor.node.value); - }, - - /** - * Runs the specified moves on the |doc| and asserts expectations. - * @param {function} doc - * @param {string=} opt_testType Either CURSOR or RANGE. - */ - runCursorMovesOnDocument: function(doc, moves, opt_testType) { - this.runWithLoadedTree(doc, - function(root) { - var start = null; - - // This occurs as a result of a load complete. - var start = AutomationUtil.findNodePost(root, - FORWARD, - AutomationPredicate.leaf); - - var cursor = new cursors.Cursor(start, 0); - if (!opt_testType || opt_testType == this.CURSOR) { - var cursor = new cursors.Cursor(start, 0); - this.cursorMoveAndAssert(cursor, moves); - } else if (opt_testType == this.RANGE) { - var range = new cursors.Range(cursor, cursor); - this.rangeMoveAndAssert(range, moves); - } - }); - }, - -simpleDoc: function() {/*! - <p>start <span>same line</span> - <p>end - */}, - - multiInlineDoc: function() {/*! - <p style='max-width: 5px'>start diff line</p> - <p>end - */} -}; - -TEST_F('CursorsTest', 'CharacterCursor', function() { - this.runCursorMovesOnDocument(this.simpleDoc, [ - [CHARACTER, DIRECTIONAL, FORWARD, {index: 1, value: 'start '}], - [CHARACTER, DIRECTIONAL, BACKWARD, {index: 0, value: 'start '}], - [CHARACTER, DIRECTIONAL, BACKWARD, {index: 0, value: 'start '}], - - [CHARACTER, DIRECTIONAL, FORWARD, {index: 1, value: 'start '}], - [CHARACTER, DIRECTIONAL, FORWARD, {index: 2, value: 'start '}], - [CHARACTER, DIRECTIONAL, FORWARD, {index: 3, value: 'start '}], - [CHARACTER, DIRECTIONAL, FORWARD, {index: 4, value: 'start '}], - [CHARACTER, DIRECTIONAL, FORWARD, {index: 5, value: 'start '}], - - [CHARACTER, DIRECTIONAL, FORWARD, {index: 0, value: 'same line'}], - [CHARACTER, DIRECTIONAL, FORWARD, {index: 1, value: 'same line'}], - [CHARACTER, DIRECTIONAL, BACKWARD, {index: 0, value: 'same line'}], - - [CHARACTER, DIRECTIONAL, BACKWARD, {index: 5, value: 'start '}],]); -}); - -TEST_F('CursorsTest', 'WordCursor', function() { - this.runCursorMovesOnDocument(this.simpleDoc, [ - // Word (BOUND). - [WORD, BOUND, BACKWARD, {index: 0, value: 'start '}], - [WORD, BOUND, BACKWARD, {index: 0, value: 'start '}], - [WORD, BOUND, FORWARD, {index: 5, value: 'start '}], - [WORD, BOUND, FORWARD, {index: 5, value: 'start '}], - - // Word (DIRECTIONAL). - [WORD, DIRECTIONAL, FORWARD, {index: 0, value: 'same line'}], - [WORD, DIRECTIONAL, FORWARD, {index: 5, value: 'same line'}], - - [WORD, DIRECTIONAL, FORWARD, {index: 0, value: 'end'}], - [WORD, DIRECTIONAL, FORWARD, {index: 0, value: 'end'}], - - [WORD, DIRECTIONAL, BACKWARD, {index: 5, value: 'same line'}], - [WORD, DIRECTIONAL, BACKWARD, {index: 0, value: 'same line'}], - - [WORD, DIRECTIONAL, BACKWARD, {index: 0, value: 'start '}], - [WORD, DIRECTIONAL, BACKWARD, {index: 0, value: 'start '}]]); -}); - -TEST_F('CursorsTest', 'CharacterWordCursor', function() { - this.runCursorMovesOnDocument(this.simpleDoc, [ - [CHARACTER, DIRECTIONAL, FORWARD, {index: 1, value: 'start '}], - - [WORD, DIRECTIONAL, FORWARD, {index: 0, value: 'same line'}], - [CHARACTER, DIRECTIONAL, FORWARD, {index: 1, value: 'same line'}], - [WORD, DIRECTIONAL, FORWARD, {index: 5, value: 'same line'}], - [CHARACTER, DIRECTIONAL, BACKWARD, {index: 4, value: 'same line'}], - [WORD, DIRECTIONAL, FORWARD, {index: 5, value: 'same line'}], - [CHARACTER, DIRECTIONAL, FORWARD, {index: 6, value: 'same line'}], - [WORD, DIRECTIONAL, BACKWARD, {index: 0, value: 'same line'}], - [CHARACTER, DIRECTIONAL, BACKWARD, {index: 5, value: 'start '}], - [CHARACTER, DIRECTIONAL, BACKWARD, {index: 4, value: 'start '}], - [WORD, DIRECTIONAL, BACKWARD, {index: 0, value: 'start '}]]); -}); - -TEST_F('CursorsTest', 'LineCursor', function() { - this.runCursorMovesOnDocument(this.simpleDoc, [ - // Line (BOUND). - [LINE, BOUND, FORWARD, {value: 'same line'}], - [LINE, BOUND, FORWARD, {value: 'same line'}], - [LINE, BOUND, BACKWARD, {value: 'start '}], - [LINE, BOUND, BACKWARD, {value: 'start '}], - - // Line (DIRECTIONAL). - [LINE, DIRECTIONAL, FORWARD, {value: 'end'}], - [LINE, DIRECTIONAL, FORWARD, {value: 'end'}], - [LINE, DIRECTIONAL, BACKWARD, {value: 'same line'}], - [LINE, DIRECTIONAL, BACKWARD, {value: 'same line'}], - [LINE, BOUND, BACKWARD, {value: 'start '}], - [LINE, DIRECTIONAL, FORWARD, {value: 'end'}]]); -}); - -TEST_F('CursorsTest', 'CharacterRange', function() { - this.runCursorMovesOnDocument(this.simpleDoc, [ - [CHARACTER, FORWARD, - {value: 'start ', index: 1}, {value: 'start ', index: 2}], - [CHARACTER, FORWARD, - {value: 'start ', index: 2}, {value: 'start ', index: 3}], - [CHARACTER, FORWARD, - {value: 'start ', index: 3}, {value: 'start ', index: 4}], - [CHARACTER, FORWARD, - {value: 'start ', index: 4}, {value: 'start ', index: 5}], - [CHARACTER, FORWARD, - {value: 'start ', index: 5}, {value: 'start ', index: 5}], - - [CHARACTER, FORWARD, - {value: 'same line', index: 0}, {value: 'same line', index: 1}], - - [CHARACTER, BACKWARD, - {value: 'start ', index: 5}, {value: 'start ', index: 5}], - [CHARACTER, BACKWARD, - {value: 'start ', index: 4}, {value: 'start ', index: 5}], - [CHARACTER, BACKWARD, - {value: 'start ', index: 3}, {value: 'start ', index: 4}], - [CHARACTER, BACKWARD, - {value: 'start ', index: 2}, {value: 'start ', index: 3}], - [CHARACTER, BACKWARD, - {value: 'start ', index: 1}, {value: 'start ', index: 2}], - [CHARACTER, BACKWARD, - {value: 'start ', index: 0}, {value: 'start ', index: 1}], - [CHARACTER, BACKWARD, - {value: 'start ', index: 0}, {value: 'start ', index: 1}], - ], this.RANGE); -}); - -TEST_F('CursorsTest', 'WordRange', function() { - this.runCursorMovesOnDocument(this.simpleDoc, [ - [WORD, FORWARD, - {value: 'same line', index: 0}, {value: 'same line', index: 4}], - [WORD, FORWARD, - {value: 'same line', index: 5}, {value: 'same line', index: 9}], - - [WORD, FORWARD, - {value: 'end', index: 0}, {value: 'end', index: 3}], - [WORD, FORWARD, - {value: 'end', index: 0}, {value: 'end', index: 3}], - - [WORD, BACKWARD, - {value: 'same line', index: 5}, {value: 'same line', index: 9}], - [WORD, BACKWARD, - {value: 'same line', index: 0}, {value: 'same line', index: 4}], - - [WORD, BACKWARD, - {value: 'start ', index: 0}, {value: 'start ', index: 5}], - [WORD, BACKWARD, - {value: 'start ', index: 0}, {value: 'start ', index: 5}], - ], this.RANGE); -}); - - -TEST_F('CursorsTest', 'LineRange', function() { - this.runCursorMovesOnDocument(this.simpleDoc, [ - [LINE, FORWARD, {value: 'end', index: 0}, {value: 'end', index: 3}], - [LINE, FORWARD, {value: 'end', index: 0}, {value: 'end', index: 3}], - - [LINE, BACKWARD, - {value: 'start ', index: 0}, {value: 'same line', index: 9}], - - [LINE, BACKWARD, - {value: 'start ', index: 0}, {value: 'same line', index: 9}], - ], this.RANGE); -}); - -TEST_F('CursorsTest', 'DontSplitOnNodeNavigation', function() { - this.runWithLoadedTree(this.multiInlineDoc, function(root) { - var para = root.firstChild; - assertEquals('paragraph', para.role); - var cursor = new cursors.Cursor(para, 0); - cursor = cursor.move(DOM_NODE, DIRECTIONAL, FORWARD); - assertEquals('staticText', cursor.node.role); - assertEquals('end', cursor.node.value); - - cursor = cursor.move(DOM_NODE, DIRECTIONAL, BACKWARD); - assertEquals('staticText', cursor.node.role); - assertEquals('start diff line', cursor.node.value); - - assertEquals('inlineTextBox', cursor.node.firstChild.role); - assertEquals('start ', cursor.node.firstChild.value); - assertEquals('diff ', cursor.node.firstChild.nextSibling.value); - assertEquals('line', cursor.node.lastChild.value); - }); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcon_engine.js b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcon_engine.js deleted file mode 100644 index fc428c54827..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcon_engine.js +++ /dev/null @@ -1,711 +0,0 @@ -// Copyright 2015 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 This is the low-level class that generates ChromeVox's - * earcons. It's designed to be self-contained and not depend on the - * rest of the code. - */ - -goog.provide('EarconEngine'); - -/** - * EarconEngine generates ChromeVox's earcons using the web audio API. - * @constructor - */ -EarconEngine = function() { - // Public control parameters. All of these are meant to be adjustable. - - /** @type {number} The master volume, as an amplification factor. */ - this.masterVolume = 0.2; - - /** @type {number} The base relative pitch adjustment, in half-steps. */ - this.masterPitch = -4; - - /** @type {number} The click volume, as an amplification factor. */ - this.clickVolume = 0.4; - - /** - * @type {number} The volume of the static sound, as an - * amplification factor. - */ - this.staticVolume = 0.2; - - /** @type {number} The base delay for repeated sounds, in seconds. */ - this.baseDelay = 0.045; - - /** @type {number} The master stereo panning, from -1 to 1. */ - this.masterPan = 0; - - /** @type {number} The master reverb level as an amplification factor. */ - this.masterReverb = 0.4; - - /** - * @type {string} The choice of the reverb impulse response to use. - * Must be one of the strings from EarconEngine.REVERBS. - */ - this.reverbSound = 'small_room_2'; - - /** @type {number} The base pitch for the 'wrap' sound in half-steps. */ - this.wrapPitch = 0; - - /** @type {number} The base pitch for the 'alert' sound in half-steps. */ - this.alertPitch = 0; - - /** @type {string} The choice of base sound for most controls. */ - this.controlSound = 'control'; - - /** - * @type {number} The delay between sounds in the on/off sweep effect, - * in seconds. - */ - this.sweepDelay = 0.045; - - /** - * @type {number} The delay between echos in the on/off sweep, in seconds. - */ - this.sweepEchoDelay = 0.15; - - /** @type {number} The number of echos in the on/off sweep. */ - this.sweepEchoCount = 3; - - /** @type {number} The pitch offset of the on/off sweep, in half-steps. */ - this.sweepPitch = -7; - - /** - * @type {number} The final gain of the progress sound, as an - * amplification factor. - */ - this.progressFinalGain = 0.05; - - /** @type {number} The multiplicative decay rate of the progress ticks. */ - this.progressGain_Decay = 0.7; - - // Private variables. - - /** @type {AudioContext} @private The audio context. */ - this.context_ = new AudioContext(); - - /** @type {?ConvolverNode} @private The reverb node, lazily initialized. */ - this.reverbConvolver_ = null; - - /** - * @type {Object<string, AudioBuffer>} A map between the name of an - * audio data file and its loaded AudioBuffer. - * @private - */ - this.buffers_ = {}; - - /** - * The source audio nodes for queued tick / tocks for progress. - * Kept around so they can be canceled. - * - * @type {Array<AudioNode>} - * @private - */ - this.progressSources_ = []; - - /** @type {number} The current gain for progress sounds. @private */ - this.progressGain_ = 1.0; - - /** @type {?number} The current time for progress sounds. @private */ - this.progressTime_ = this.context_.currentTime; - - /** - * @type {?number} The window.setInterval ID for progress sounds. - * @private - */ - this.progressIntervalID_ = null; - - // Initialization: load the base sound data files asynchronously. - var allSoundFilesToLoad = EarconEngine.SOUNDS.concat(EarconEngine.REVERBS); - allSoundFilesToLoad.forEach((function(sound) { - var url = EarconEngine.BASE_URL + sound + '.wav'; - this.loadSound(sound, url); - }).bind(this)); -}; - -/** - * @type {Array<string>} The list of sound data files to load. - * @const - */ -EarconEngine.SOUNDS = [ - 'control', - 'selection', - 'selection_reverse', - 'skim', - 'static']; - -/** - * @type {Array<string>} The list of reverb data files to load. - * @const - */ -EarconEngine.REVERBS = [ - 'small_room_2']; - -/** - * @type {number} The scale factor for one half-step. - * @const - */ -EarconEngine.HALF_STEP = Math.pow(2.0, 1.0 / 12.0); - -/** - * @type {string} The base url for earcon sound resources. - * @const - */ -EarconEngine.BASE_URL = chrome.extension.getURL('cvox2/background/earcons/'); - -/** - * Fetches a sound asynchronously and loads its data into an AudioBuffer. - * - * @param {string} name The name of the sound to load. - * @param {string} url The url where the sound should be fetched from. - */ -EarconEngine.prototype.loadSound = function(name, url) { - var request = new XMLHttpRequest(); - request.open('GET', url, true); - request.responseType = 'arraybuffer'; - - // Decode asynchronously. - request.onload = (function() { - this.context_.decodeAudioData( - /** @type {ArrayBuffer} */ (request.response), - (function(buffer) { - this.buffers_[name] = buffer; - }).bind(this)); - }).bind(this); - request.send(); -}; - -/** - * Return an AudioNode containing the final processing that all - * sounds go through: master volume / gain, panning, and reverb. - * The chain is hooked up to the destination automatically, so you - * just need to connect your source to the return value from this - * method. - * - * @param {{gain: (number | undefined), - * pan: (number | undefined), - * reverb: (number | undefined)}} properties - * An object where you can override the default - * gain, pan, and reverb, otherwise these are taken from - * masterVolume, masterPan, and masterReverb. - * @return {AudioNode} The filters to be applied to all sounds, connected - * to the destination node. - */ -EarconEngine.prototype.createCommonFilters = function(properties) { - var gain = this.masterVolume; - if (properties.gain) { - gain *= properties.gain; - } - var gainNode = this.context_.createGain(); - gainNode.gain.value = gain; - var first = gainNode; - var last = gainNode; - - var pan = this.masterPan; - if (properties.pan !== undefined) { - pan = properties.pan; - } - if (pan != 0) { - var panNode = this.context_.createPanner(); - panNode.setPosition(pan, 0, -1); - panNode.setOrientation(0, 0, 1); - last.connect(panNode); - last = panNode; - } - - var reverb = this.masterReverb; - if (properties.reverb !== undefined) { - reverb = properties.reverb; - } - if (reverb) { - if (!this.reverbConvolver_) { - this.reverbConvolver_ = this.context_.createConvolver(); - this.reverbConvolver_.buffer = this.buffers_[this.reverbSound]; - this.reverbConvolver_.connect(this.context_.destination); - } - - // Dry - last.connect(this.context_.destination); - - // Wet - var reverbGainNode = this.context_.createGain(); - reverbGainNode.gain.value = reverb; - last.connect(reverbGainNode); - reverbGainNode.connect(this.reverbConvolver_); - } else { - last.connect(this.context_.destination); - } - - return first; -}; - -/** - * High-level interface to play a sound from a buffer source by name, - * with some simple adjustments like pitch change (in half-steps), - * a start time (relative to the current time, in seconds), - * gain, panning, and reverb. - * - * The only required parameter is the name of the sound. The time, pitch, - * gain, panning, and reverb are all optional and are passed in an - * object of optional properties. - * - * @param {string} sound The name of the sound to play. It must already - * be loaded in a buffer. - * @param {{pitch: (number | undefined), - * time: (number | undefined), - * gain: (number | undefined), - * pan: (number | undefined), - * reverb: (number | undefined)}=} opt_properties - * An object where you can override the default pitch, gain, pan, - * and reverb. - * @return {AudioBufferSourceNode} The source node, so you can stop it - * or set event handlers on it. - */ -EarconEngine.prototype.play = function(sound, opt_properties) { - var source = this.context_.createBufferSource(); - source.buffer = this.buffers_[sound]; - - if (!opt_properties) { - // This typecast looks silly, but the Closure compiler doesn't support - // optional fields in record types very well so this is the shortest hack. - opt_properties = /** @type {undefined} */({}); - } - - var pitch = this.masterPitch; - if (opt_properties.pitch) { - pitch += opt_properties.pitch; - } - if (pitch != 0) { - source.playbackRate.value = Math.pow(EarconEngine.HALF_STEP, pitch); - } - - var destination = this.createCommonFilters(opt_properties); - source.connect(destination); - - if (opt_properties.time) { - source.start(this.context_.currentTime + opt_properties.time); - } else { - source.start(this.context_.currentTime); - } - - return source; -}; - -/** - * Play the static sound. - */ -EarconEngine.prototype.onStatic = function() { - this.play('static', {gain: this.staticVolume}); -}; - -/** - * Play the link sound. - */ -EarconEngine.prototype.onLink = function() { - this.play('static', {gain: this.clickVolume}); - this.play(this.controlSound, {pitch: 12}); -}; - -/** - * Play the button sound. - */ -EarconEngine.prototype.onButton = function() { - this.play('static', {gain: this.clickVolume}); - this.play(this.controlSound); -}; - -/** - * Play the text field sound. - */ -EarconEngine.prototype.onTextField = function() { - this.play('static', {gain: this.clickVolume}); - this.play('static', {time: this.baseDelay * 1.5, - gain: this.clickVolume * 0.5}); - this.play(this.controlSound, {pitch: 4}); - this.play(this.controlSound, - {pitch: 4, - time: this.baseDelay * 1.5, - gain: 0.5}); -}; - -/** - * Play the pop up button sound. - */ -EarconEngine.prototype.onPopUpButton = function() { - this.play('static', {gain: this.clickVolume}); - - this.play(this.controlSound); - this.play(this.controlSound, - {time: this.baseDelay * 3, - gain: 0.2, - pitch: 12}); - this.play(this.controlSound, - {time: this.baseDelay * 4.5, - gain: 0.2, - pitch: 12}); -}; - -/** - * Play the check on sound. - */ -EarconEngine.prototype.onCheckOn = function() { - this.play('static', {gain: this.clickVolume}); - this.play(this.controlSound, {pitch: -5}); - this.play(this.controlSound, {pitch: 7, time: this.baseDelay * 2}); -}; - -/** - * Play the check off sound. - */ -EarconEngine.prototype.onCheckOff = function() { - this.play('static', {gain: this.clickVolume}); - this.play(this.controlSound, {pitch: 7}); - this.play(this.controlSound, {pitch: -5, time: this.baseDelay * 2}); -}; - -/** - * Play the select control sound. - */ -EarconEngine.prototype.onSelect = function() { - this.play('static', {gain: this.clickVolume}); - this.play(this.controlSound); - this.play(this.controlSound, {time: this.baseDelay}); - this.play(this.controlSound, {time: this.baseDelay * 2}); -}; - -/** - * Play the slider sound. - */ -EarconEngine.prototype.onSlider = function() { - this.play('static', {gain: this.clickVolume}); - this.play(this.controlSound); - this.play(this.controlSound, - {time: this.baseDelay, - gain: 0.5, - pitch: 2}); - this.play(this.controlSound, - {time: this.baseDelay * 2, - gain: 0.25, - pitch: 4}); - this.play(this.controlSound, - {time: this.baseDelay * 3, - gain: 0.125, - pitch: 6}); - this.play(this.controlSound, - {time: this.baseDelay * 4, - gain: 0.0625, - pitch: 8}); -}; - -/** - * Play the skim sound. - */ -EarconEngine.prototype.onSkim = function() { - this.play('skim'); -}; - -/** - * Play the selection sound. - */ -EarconEngine.prototype.onSelection = function() { - this.play('selection'); -}; - -/** - * Play the selection reverse sound. - */ -EarconEngine.prototype.onSelectionReverse = function() { - this.play('selection_reverse'); -}; - -/** - * Generate a synthesized musical note based on a sum of sinusoidals shaped - * by an envelope, controlled by a number of properties. - * - * The sound has a frequency of |freq|, or if |endFreq| is specified, does - * an exponential ramp from |freq| to |endFreq|. - * - * If |overtones| is greater than 1, the sound will be mixed with additional - * sinusoidals at multiples of |freq|, each one scaled by |overtoneFactor|. - * This creates a rounder tone than a pure sine wave. - * - * The envelope is shaped by the duration |dur|, the attack time |attack|, - * and the decay time |decay|, in seconds. - * - * As with other functions, |pan| and |reverb| can be used to override - * masterPan and masterReverb. - * - * @param {{gain: number, - * freq: number, - * endFreq: (number | undefined), - * time: (number | undefined), - * overtones: (number | undefined), - * overtoneFactor: (number | undefined), - * dur: (number | undefined), - * attack: (number | undefined), - * decay: (number | undefined), - * pan: (number | undefined), - * reverb: (number | undefined)}} properties - * An object containing the properties that can be used to - * control the sound, as described above. - */ -EarconEngine.prototype.generateSinusoidal = function(properties) { - var envelopeNode = this.context_.createGain(); - envelopeNode.connect(this.context_.destination); - - var time = properties.time; - if (time === undefined) { - time = 0; - } - - // Generate an oscillator for the frequency corresponding to the specified - // frequency, and then additional overtones at multiples of that frequency - // scaled by the overtoneFactor. Cue the oscillator to start and stop - // based on the start time and specified duration. - // - // If an end frequency is specified, do an exponential ramp to that end - // frequency. - var gain = properties.gain; - for (var i = 0; i < properties.overtones; i++) { - var osc = this.context_.createOscillator(); - osc.frequency.value = properties.freq * (i + 1); - - if (properties.endFreq) { - osc.frequency.setValueAtTime( - properties.freq * (i + 1), - this.context_.currentTime + time); - osc.frequency.exponentialRampToValueAtTime( - properties.endFreq * (i + 1), - this.context_.currentTime + properties.dur); - } - - osc.start(this.context_.currentTime + time); - osc.stop(this.context_.currentTime + time + properties.dur); - - var gainNode = this.context_.createGain(); - gainNode.gain.value = gain; - osc.connect(gainNode); - gainNode.connect(envelopeNode); - - gain *= properties.overtoneFactor; - } - - // Shape the overall sound by an envelope based on the attack and - // decay times. - envelopeNode.gain.setValueAtTime(0, this.context_.currentTime + time); - envelopeNode.gain.linearRampToValueAtTime( - 1, this.context_.currentTime + time + properties.attack); - envelopeNode.gain.setValueAtTime( - 1, this.context_.currentTime + time + - properties.dur - properties.decay); - envelopeNode.gain.linearRampToValueAtTime( - 0, this.context_.currentTime + time + properties.dur); - - // Route everything through the common filters like reverb at the end. - var destination = this.createCommonFilters({}); - envelopeNode.connect(destination); -}; - -/** - * Play a sweep over a bunch of notes in a scale, with an echo, - * for the ChromeVox on or off sounds. - * - * @param {boolean} reverse Whether to play in the reverse direction. - */ -EarconEngine.prototype.onChromeVoxSweep = function(reverse) { - var pitches = [-7, -5, 0, 5, 7, 12, 17, 19, 24]; - - if (reverse) { - pitches.reverse(); - } - - var attack = 0.015; - var dur = pitches.length * this.sweepDelay; - - var destination = this.createCommonFilters({reverb: 2.0}); - for (var k = 0; k < this.sweepEchoCount; k++) { - var envelopeNode = this.context_.createGain(); - var startTime = this.context_.currentTime + this.sweepEchoDelay * k; - var sweepGain = Math.pow(0.3, k); - var overtones = 2; - var overtoneGain = sweepGain; - for (var i = 0; i < overtones; i++) { - var osc = this.context_.createOscillator(); - osc.start(startTime); - osc.stop(startTime + dur); - - var gainNode = this.context_.createGain(); - osc.connect(gainNode); - gainNode.connect(envelopeNode); - - for (var j = 0; j < pitches.length; j++) { - var freqDecay; - if (reverse) { - freqDecay = Math.pow(0.75, pitches.length - j); - } else { - freqDecay = Math.pow(0.75, j); - } - var gain = overtoneGain * freqDecay; - var freq = (i + 1) * 220 * - Math.pow(EarconEngine.HALF_STEP, pitches[j] + this.sweepPitch); - if (j == 0) { - osc.frequency.setValueAtTime(freq, startTime); - gainNode.gain.setValueAtTime(gain, startTime); - } else { - osc.frequency.exponentialRampToValueAtTime( - freq, startTime + j * this.sweepDelay); - gainNode.gain.linearRampToValueAtTime( - gain, startTime + j * this.sweepDelay); - } - osc.frequency.setValueAtTime( - freq, startTime + j * this.sweepDelay + this.sweepDelay - attack); - } - - overtoneGain *= 0.1 + 0.2 * k; - } - - envelopeNode.gain.setValueAtTime(0, startTime); - envelopeNode.gain.linearRampToValueAtTime(1, startTime + this.sweepDelay); - envelopeNode.gain.setValueAtTime(1, startTime + dur - attack * 2); - envelopeNode.gain.linearRampToValueAtTime(0, startTime + dur); - envelopeNode.connect(destination); - } -}; - -/** - * Play the "ChromeVox On" sound. - */ -EarconEngine.prototype.onChromeVoxOn = function() { - this.onChromeVoxSweep(false); -}; - -/** - * Play the "ChromeVox Off" sound. - */ -EarconEngine.prototype.onChromeVoxOff = function() { - this.onChromeVoxSweep(true); -}; - -/** - * Play an alert sound. - */ -EarconEngine.prototype.onAlert = function() { - var freq1 = 220 * Math.pow(EarconEngine.HALF_STEP, this.alertPitch - 2); - var freq2 = 220 * Math.pow(EarconEngine.HALF_STEP, this.alertPitch - 3); - this.generateSinusoidal({attack: 0.02, - decay: 0.07, - dur: 0.15, - gain: 0.3, - freq: freq1, - overtones: 3, - overtoneFactor: 0.1}); - this.generateSinusoidal({attack: 0.02, - decay: 0.07, - dur: 0.15, - gain: 0.3, - freq: freq2, - overtones: 3, - overtoneFactor: 0.1}); -}; - -/** - * Play a wrap sound. - */ -EarconEngine.prototype.onWrap = function() { - this.play('static', {gain: this.clickVolume * 0.3}); - var freq1 = 220 * Math.pow(EarconEngine.HALF_STEP, this.wrapPitch - 8); - var freq2 = 220 * Math.pow(EarconEngine.HALF_STEP, this.wrapPitch + 8); - this.generateSinusoidal({attack: 0.01, - decay: 0.1, - dur: 0.15, - gain: 0.3, - freq: freq1, - endFreq: freq2, - overtones: 1, - overtoneFactor: 0.1}); -}; - -/** - * Queue up a few tick/tock sounds for a progress bar. This is called - * repeatedly by setInterval to keep the sounds going continuously. - * @private - */ -EarconEngine.prototype.generateProgressTickTocks_ = function() { - while (this.progressTime_ < this.context_.currentTime + 3.0) { - var t = this.progressTime_ - this.context_.currentTime; - this.progressSources_.push( - [this.progressTime_, - this.play('static', - {gain: 0.5 * this.progressGain_, - time: t})]); - this.progressSources_.push( - [this.progressTime_, - this.play(this.controlSound, - {pitch: 20, - time: t, - gain: this.progressGain_})]); - - if (this.progressGain_ > this.progressFinalGain) { - this.progressGain_ *= this.progressGain_Decay; - } - t += 0.5; - - this.progressSources_.push( - [this.progressTime_, - this.play('static', - {gain: 0.5 * this.progressGain_, - time: t})]); - this.progressSources_.push( - [this.progressTime_, - this.play(this.controlSound, - {pitch: 8, - time: t, - gain: this.progressGain_})]); - - if (this.progressGain_ > this.progressFinalGain) { - this.progressGain_ *= this.progressGain_Decay; - } - - this.progressTime_ += 1.0; - } - - var removeCount = 0; - while (removeCount < this.progressSources_.length && - this.progressSources_[removeCount][0] < this.context_.currentTime - 0.2) { - removeCount++; - } - this.progressSources_.splice(0, removeCount); -}; - -/** - * Start playing tick / tock progress sounds continuously until - * explicitly canceled. - */ -EarconEngine.prototype.startProgress = function() { - this.progressSources_ = []; - this.progressGain_ = 0.5; - this.progressTime_ = this.context_.currentTime; - this.generateProgressTickTocks_(); - this.progressIntervalID_ = window.setInterval( - this.generateProgressTickTocks_.bind(this), 1000); -}; - -/** - * Stop playing any tick / tock progress sounds. - */ -EarconEngine.prototype.cancelProgress = function() { - if (!this.progressIntervalID_) { - return; - } - - for (var i = 0; i < this.progressSources_.length; i++) { - this.progressSources_[i][1].stop(); - } - this.progressSources_ = []; - - window.clearInterval(this.progressIntervalID_); - this.progressIntervalID_ = null; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/control.wav b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/control.wav Binary files differdeleted file mode 100644 index 4a37991111a..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/control.wav +++ /dev/null diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/selection.wav b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/selection.wav Binary files differdeleted file mode 100644 index 7be8224dbd3..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/selection.wav +++ /dev/null diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/selection_reverse.wav b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/selection_reverse.wav Binary files differdeleted file mode 100644 index a42b0d9afb3..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/selection_reverse.wav +++ /dev/null diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/skim.wav b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/skim.wav Binary files differdeleted file mode 100644 index b74c196c18b..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/skim.wav +++ /dev/null diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/small_room_2.wav b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/small_room_2.wav Binary files differdeleted file mode 100644 index 7ef10db6c87..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/small_room_2.wav +++ /dev/null diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/static.wav b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/static.wav Binary files differdeleted file mode 100644 index d43fae18449..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/earcons/static.wav +++ /dev/null diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/loader.js b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/loader.js deleted file mode 100644 index 7f2602dc9f3..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/loader.js +++ /dev/null @@ -1,10 +0,0 @@ -// 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 Loader for the background page. - */ - -goog.require('Background'); -goog.require('cvox.ChromeVoxBackground'); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js deleted file mode 100644 index 677030882ea..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js +++ /dev/null @@ -1,1329 +0,0 @@ -// 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 Provides output services for ChromeVox. - */ - -goog.provide('Output'); -goog.provide('Output.EventType'); - -goog.require('AutomationUtil.Dir'); -goog.require('EarconEngine'); -goog.require('cursors.Cursor'); -goog.require('cursors.Range'); -goog.require('cursors.Unit'); -goog.require('cvox.AbstractEarcons'); -goog.require('cvox.NavBraille'); -goog.require('cvox.Spannable'); -goog.require('cvox.ValueSelectionSpan'); -goog.require('cvox.ValueSpan'); -goog.require('goog.i18n.MessageFormat'); - -goog.scope(function() { -var Dir = AutomationUtil.Dir; - -/** - * An Output object formats a cursors.Range into speech, braille, or both - * representations. This is typically a cvox.Spannable. - * - * The translation from Range to these output representations rely upon format - * rules which specify how to convert AutomationNode objects into annotated - * strings. - * The format of these rules is as follows. - * - * $ prefix: used to substitute either an attribute or a specialized value from - * an AutomationNode. Specialized values include role and state. - * For example, $value $role $enabled - * @ prefix: used to substitute a message. Note the ability to specify params to - * the message. For example, '@tag_html' '@selected_index($text_sel_start, - * $text_sel_end'). - * @@ prefix: similar to @, used to substitute a message, but also pulls the - * localized string through goog.i18n.MessageFormat to support locale - * aware plural handling. The first argument should be a number which will - * be passed as a COUNT named parameter to MessageFormat. - * TODO(plundblad): Make subsequent arguments normal placeholder arguments - * when needed. - * = suffix: used to specify substitution only if not previously appended. - * For example, $name= would insert the name attribute only if no name - * attribute had been inserted previously. - * @constructor - */ -Output = function() { - // TODO(dtseng): Include braille specific rules. - /** @type {!Array<!cvox.Spannable>} */ - this.speechBuffer_ = []; - /** @type {!Array<!cvox.Spannable>} */ - this.brailleBuffer_ = []; - /** @type {!Array<!Object>} */ - this.locations_ = []; - /** @type {function(?)} */ - this.speechEndCallback_; - - /** - * Current global options. - * @type {{speech: boolean, braille: boolean, location: boolean}} - */ - this.formatOptions_ = {speech: true, braille: false, location: true}; - - /** - * Speech properties to apply to the entire output. - * @type {!Object<*>} - */ - this.speechProperties_ = {}; -}; - -/** - * Delimiter to use between output values. - * @type {string} - */ -Output.SPACE = ' '; - -/** - * Metadata about supported automation roles. - * @const {Object<{msgId: string, - * earconId: (string|undefined), - * inherits: (string|undefined)}>} - * msgId: the message id of the role. - * earconId: an optional earcon to play when encountering the role. - * inherits: inherits rules from this role. - * @private - */ -Output.ROLE_INFO_ = { - alert: { - msgId: 'role_alert', - earconId: 'ALERT_NONMODAL', - }, - alertDialog: { - msgId: 'role_alertdialog' - }, - article: { - msgId: 'role_article', - inherits: 'abstractContainer' - }, - application: { - msgId: 'role_application', - inherits: 'abstractContainer' - }, - banner: { - msgId: 'role_banner', - inherits: 'abstractContainer' - }, - button: { - msgId: 'role_button', - earconId: 'BUTTON' - }, - buttonDropDown: { - msgId: 'role_button', - earconId: 'BUTTON' - }, - cell: { - msgId: 'role_gridcell' - }, - checkBox: { - msgId: 'role_checkbox' - }, - columnHeader: { - msgId: 'role_columnheader', - inherits: 'abstractContainer' - }, - comboBox: { - msgId: 'role_combobox' - }, - complementary: { - msgId: 'role_complementary', - inherits: 'abstractContainer' - }, - contentInfo: { - msgId: 'role_contentinfo', - inherits: 'abstractContainer' - }, - date: { - msgId: 'input_type_date', - inherits: 'abstractContainer' - }, - definition: { - msgId: 'role_definition', - inherits: 'abstractContainer' - }, - dialog: { - msgId: 'role_dialog' - }, - directory: { - msgId: 'role_directory', - inherits: 'abstractContainer' - }, - document: { - msgId: 'role_document', - inherits: 'abstractContainer' - }, - form: { - msgId: 'role_form', - inherits: 'abstractContainer' - }, - grid: { - msgId: 'role_grid' - }, - group: { - msgId: 'role_group' - }, - heading: { - msgId: 'role_heading', - }, - image: { - msgId: 'role_img', - }, - inputTime: { - msgId: 'input_type_time', - inherits: 'abstractContainer' - }, - link: { - msgId: 'role_link', - earconId: 'LINK' - }, - listBox: { - msgId: 'role_listbox', - earconId: 'LISTBOX' - }, - listBoxOption: { - msgId: 'role_listitem', - earconId: 'LIST_ITEM' - }, - listItem: { - msgId: 'role_listitem', - earconId: 'LIST_ITEM' - }, - log: { - msgId: 'role_log', - }, - main: { - msgId: 'role_main', - inherits: 'abstractContainer' - }, - marquee: { - msgId: 'role_marquee', - }, - math: { - msgId: 'role_math', - inherits: 'abstractContainer' - }, - menu: { - msgId: 'role_menu' - }, - menuBar: { - msgId: 'role_menubar', - }, - menuItem: { - msgId: 'role_menuitem' - }, - menuItemCheckBox: { - msgId: 'role_menuitemcheckbox' - }, - menuItemRadio: { - msgId: 'role_menuitemradio' - }, - menuListOption: { - msgId: 'role_menuitem' - }, - menuListPopup: { - msgId: 'role_menu' - }, - navigation: { - msgId: 'role_navigation', - inherits: 'abstractContainer' - }, - note: { - msgId: 'role_note', - inherits: 'abstractContainer' - }, - popUpButton: { - msgId: 'role_button', - earcon: 'LISTBOX' - }, - radioButton: { - msgId: 'role_radio' - }, - radioGroup: { - msgId: 'role_radiogroup', - }, - region: { - msgId: 'role_region', - inherits: 'abstractContainer' - }, - rowHeader: { - msgId: 'role_rowheader', - inherits: 'abstractContainer' - }, - scrollBar: { - msgId: 'role_scrollbar', - }, - search: { - msgId: 'role_search', - inherits: 'abstractContainer' - }, - separator: { - msgId: 'role_separator', - inherits: 'abstractContainer' - }, - spinButton: { - msgId: 'role_spinbutton', - earconId: 'LISTBOX' - }, - status: { - msgId: 'role_status' - }, - tab: { - msgId: 'role_tab' - }, - tabList: { - msgId: 'role_tablist' - }, - tabPanel: { - msgId: 'role_tabpanel' - }, - textBox: { - msgId: 'input_type_text', - earconId: 'EDITABLE_TEXT' - }, - textField: { - msgId: 'input_type_text', - earconId: 'EDITABLE_TEXT' - }, - time: { - msgId: 'tag_time', - inherits: 'abstractContainer' - }, - timer: { - msgId: 'role_timer' - }, - toolbar: { - msgId: 'role_toolbar' - }, - tree: { - msgId: 'role_tree' - }, - treeItem: { - msgId: 'role_treeitem' - } -}; - -/** - * Metadata about supported automation states. - * @const {!Object<{on: {msgId: string, earconId: string}, - * off: {msgId: string, earconId: string}, - * omitted: {msgId: string, earconId: string}}>} - * on: info used to describe a state that is set to true. - * off: info used to describe a state that is set to false. - * omitted: info used to describe a state that is undefined. - * @private - */ -Output.STATE_INFO_ = { - checked: { - on: { - earconId: 'CHECK_ON', - msgId: 'checkbox_checked_state' - }, - off: { - earconId: 'CHECK_OFF', - msgId: 'checkbox_unchecked_state' - }, - omitted: { - earconId: 'CHECK_OFF', - msgId: 'checkbox_unchecked_state' - } - }, - collapsed: { - on: { - msgId: 'aria_expanded_false' - }, - off: { - msgId: 'aria_expanded_true' - } - }, - expanded: { - on: { - msgId: 'aria_expanded_true' - }, - off: { - msgId: 'aria_expanded_false' - } - }, - visited: { - on: { - msgId: 'visited_state' - } - } -}; - -/** - * Maps input types to message IDs. - * @const {Object<string>} - * @private - */ -Output.INPUT_TYPE_MESSAGE_IDS_ = { - 'email': 'input_type_email', - 'number': 'input_type_number', - 'password': 'input_type_password', - 'search': 'input_type_search', - 'tel': 'input_type_number', - 'text': 'input_type_text', - 'url': 'input_type_url', -}; - -/** - * Rules specifying format of AutomationNodes for output. - * @type {!Object<Object<Object<string>>>} - */ -Output.RULES = { - navigate: { - 'default': { - speak: '$name $value $help $role', - braille: '' - }, - abstractContainer: { - enter: '$name $role', - leave: '@exited_container($role)' - }, - alert: { - speak: '!doNotInterrupt $role $descendants' - }, - alertDialog: { - enter: '$name $role $descendants' - }, - cell: { - enter: '@column_granularity $tableCellColumnIndex' - }, - checkBox: { - speak: '$name $role $checked' - }, - dialog: { - enter: '$name $role' - }, - div: { - enter: '$name', - speak: '$name' - }, - grid: { - enter: '$name $role' - }, - heading: { - enter: '@tag_h+$hierarchicalLevel', - speak: '@tag_h+$hierarchicalLevel $nameOrDescendants=' - }, - inlineTextBox: { - speak: '$value=' - }, - link: { - enter: '$name $if($visited, @visited_link, $role)', - stay: '$name= $if($visited, @visited_link, $role)', - speak: '$name= $if($visited, @visited_link, $role)' - }, - list: { - enter: '$role @@list_with_items($countChildren(listItem))' - }, - listBox: { - enter: '$name $role @@list_with_items($countChildren(listBoxOption))' - }, - listBoxOption: { - speak: '$name $role @describe_index($indexInParent, $parentChildCount)' - }, - listItem: { - enter: '$role' - }, - menu: { - enter: '$name $role @@list_with_items($countChildren(menuItem))' - }, - menuItem: { - speak: '$name $role $if($haspopup, @has_submenu) ' + - '@describe_index($indexInParent, $parentChildCount)' - }, - menuListOption: { - speak: '$name $value @role_menuitem ' + - '@describe_index($indexInParent, $parentChildCount)' - }, - paragraph: { - speak: '$descendants' - }, - popUpButton: { - speak: '$value $name $role @aria_has_popup ' + - '$if($collapsed, @aria_expanded_false, @aria_expanded_true)' - }, - radioButton: { - speak: '$if($checked, @describe_radio_selected($name), ' + - '@describe_radio_unselected($name))' - }, - radioGroup: { - enter: '$name $role' - }, - rootWebArea: { - enter: '$name' - }, - row: { - enter: '@row_granularity $tableRowIndex' - }, - slider: { - speak: '@describe_slider($value, $name) $help' - }, - staticText: { - speak: '$value=' - }, - tab: { - speak: '@describe_tab($name)' - }, - textField: { - speak: '$name $value $if(' + - '$inputType, $inputType, $role)', - braille: '' - }, - toolbar: { - enter: '$name $role' - }, - tree: { - enter: '$name $role @@list_with_items($countChildren(treeItem))' - }, - treeItem: { - enter: '$role $expanded $collapsed ' + - '@describe_index($indexInParent, $parentChildCount) ' + - '@describe_depth($hierarchicalLevel)' - }, - window: { - enter: '$name', - speak: '@describe_window($name) $earcon(OBJECT_OPEN)' - } - }, - menuStart: { - 'default': { - speak: '@chrome_menu_opened($name) $earcon(OBJECT_OPEN)' - } - }, - menuEnd: { - 'default': { - speak: '@chrome_menu_closed $earcon(OBJECT_CLOSE)' - } - }, - menuListValueChanged: { - 'default': { - speak: '$value $name ' + - '$find({"state": {"selected": true, "invisible": false}}, ' + - '@describe_index($indexInParent, $parentChildCount)) ' - } - }, - alert: { - default: { - speak: '!doNotInterrupt ' + - '@role_alert $name $earcon(ALERT_NONMODAL) $descendants' - } - } -}; - -/** - * Custom actions performed while rendering an output string. - * @constructor - */ -Output.Action = function() { -}; - -Output.Action.prototype = { - run: function() { - } -}; - -/** - * Action to play an earcon. - * @param {string} earconId - * @constructor - * @extends {Output.Action} - */ -Output.EarconAction = function(earconId) { - Output.Action.call(this); - /** @type {string} */ - this.earconId = earconId; -}; - -Output.EarconAction.prototype = { - __proto__: Output.Action.prototype, - - /** @override */ - run: function() { - cvox.ChromeVox.earcons.playEarcon(cvox.Earcon[this.earconId]); - } -}; - -/** - * Annotation for selection. - * @param {number} startIndex - * @param {number} endIndex - * @constructor - */ -Output.SelectionSpan = function(startIndex, endIndex) { - // TODO(dtseng): Direction lost below; should preserve for braille panning. - this.startIndex = startIndex < endIndex ? startIndex : endIndex; - this.endIndex = endIndex > startIndex ? endIndex : startIndex; -}; - -/** - * Wrapper for automation nodes as annotations. Since the - * {@code chrome.automation.AutomationNode} constructor isn't exposed in - * the API, this class is used to allow isntanceof checks on these - * annotations. - @ @param {chrome.automation.AutomationNode} node - * @constructor - */ -Output.NodeSpan = function(node) { - this.node = node; -}; - -/** - * Possible events handled by ChromeVox internally. - * @enum {string} - */ -Output.EventType = { - NAVIGATE: 'navigate' -}; - -Output.prototype = { - /** - * Gets the spoken output with separator '|'. - * @return {!cvox.Spannable} - */ - get speechOutputForTest() { - return this.speechBuffer_.reduce(function(prev, cur) { - if (prev === null) - return cur; - prev.append('|'); - prev.append(cur); - return prev; - }, null); - }, - - /** - * Gets the output buffer for braille. - * @return {!cvox.Spannable} - */ - get brailleOutputForTest() { - return this.createBrailleOutput_(); - }, - - /** - * Specify ranges for speech. - * @param {!cursors.Range} range - * @param {cursors.Range} prevRange - * @param {chrome.automation.EventType|Output.EventType} type - * @return {!Output} - */ - withSpeech: function(range, prevRange, type) { - this.formatOptions_ = {speech: true, braille: false, location: true}; - this.render_(range, prevRange, type, this.speechBuffer_); - return this; - }, - - /** - * Specify ranges for braille. - * @param {!cursors.Range} range - * @param {cursors.Range} prevRange - * @param {chrome.automation.EventType|Output.EventType} type - * @return {!Output} - */ - withBraille: function(range, prevRange, type) { - this.formatOptions_ = {speech: false, braille: true, location: false}; - this.render_(range, prevRange, type, this.brailleBuffer_); - return this; - }, - - /** - * Specify the same ranges for speech and braille. - * @param {!cursors.Range} range - * @param {cursors.Range} prevRange - * @param {chrome.automation.EventType|Output.EventType} type - * @return {!Output} - */ - withSpeechAndBraille: function(range, prevRange, type) { - this.withSpeech(range, prevRange, type); - this.withBraille(range, prevRange, type); - return this; - }, - - /** - * Apply a format string directly to the output buffer. This lets you - * output a message directly to the buffer using the format syntax. - * @param {string} formatStr - * @return {!Output} - */ - format: function(formatStr) { - this.formatOptions_ = {speech: true, braille: false, location: true}; - this.format_(null, formatStr, this.speechBuffer_); - - this.formatOptions_ = {speech: false, braille: true, location: false}; - this.format_(null, formatStr, this.brailleBuffer_); - - return this; - }, - - /** - * Triggers callback for a speech event. - * @param {function()} callback - */ - onSpeechEnd: function(callback) { - this.speechEndCallback_ = function(opt_cleanupOnly) { - if (!opt_cleanupOnly) - callback(); - }.bind(this); - return this; - }, - - /** - * Executes all specified output. - */ - go: function() { - // Speech. - var queueMode = cvox.QueueMode.FLUSH; - this.speechBuffer_.forEach(function(buff, i, a) { - if (buff.toString()) { - (function() { - var scopedBuff = buff; - this.speechProperties_['startCallback'] = function() { - var actions = scopedBuff.getSpansInstanceOf(Output.Action); - if (actions) { - actions.forEach(function(a) { - a.run(); - }); - } - }; - }.bind(this)()); - - if (this.speechEndCallback_ && i == a.length - 1) - this.speechProperties_['endCallback'] = this.speechEndCallback_; - else - this.speechProperties_['endCallback'] = null; - cvox.ChromeVox.tts.speak( - buff.toString(), queueMode, this.speechProperties_); - queueMode = cvox.QueueMode.QUEUE; - } - }.bind(this)); - - // Braille. - if (this.brailleBuffer_.length) { - var buff = this.createBrailleOutput_(); - var selSpan = - buff.getSpanInstanceOf(Output.SelectionSpan); - var startIndex = -1, endIndex = -1; - if (selSpan) { - // Casts ok, since the span is known to be in the spannable. - var valueStart = - /** @type {number} */ (buff.getSpanStart(selSpan)); - var valueEnd = - /** @type {number} */ (buff.getSpanEnd(selSpan)); - startIndex = valueStart + selSpan.startIndex; - endIndex = valueStart + selSpan.endIndex; - buff.setSpan(new cvox.ValueSpan(0), valueStart, valueEnd); - buff.setSpan(new cvox.ValueSelectionSpan(), startIndex, endIndex); - } - - var output = new cvox.NavBraille({ - text: buff, - startIndex: startIndex, - endIndex: endIndex - }); - - cvox.ChromeVox.braille.write(output); - } - - // Display. - chrome.accessibilityPrivate.setFocusRing(this.locations_); - }, - - /** - * Renders the given range using optional context previous range and event - * type. - * @param {!cursors.Range} range - * @param {cursors.Range} prevRange - * @param {chrome.automation.EventType|string} type - * @param {!Array<cvox.Spannable>} buff Buffer to receive rendered output. - * @private - */ - render_: function(range, prevRange, type, buff) { - if (range.isSubNode()) - this.subNode_(range, prevRange, type, buff); - else - this.range_(range, prevRange, type, buff); - }, - - /** - * Format the node given the format specifier. - * @param {chrome.automation.AutomationNode} node - * @param {string|!Object} format The output format either specified as an - * output template string or a parsed output format tree. - * @param {!Array<cvox.Spannable>} buff Buffer to receive rendered output. - * @param {!Object=} opt_exclude A set of attributes to exclude. - * @private - */ - format_: function(node, format, buff, opt_exclude) { - opt_exclude = opt_exclude || {}; - var tokens = []; - var args = null; - - // Hacky way to support args. - if (typeof(format) == 'string') { - format = format.replace(/([,:])\W/g, '$1'); - tokens = format.split(' '); - } else { - tokens = [format]; - } - - tokens.forEach(function(token) { - // Ignore empty tokens. - if (!token) - return; - - // Parse the token. - var tree; - if (typeof(token) == 'string') - tree = this.createParseTree_(token); - else - tree = token; - - // Obtain the operator token. - token = tree.value; - - // Set suffix options. - var options = {}; - options.annotation = []; - options.isUnique = token[token.length - 1] == '='; - if (options.isUnique) - token = token.substring(0, token.length - 1); - - // Annotate braille output with the corresponding automation nodes - // to support acting on nodes based on location in the output. - if (this.formatOptions_.braille) - options.annotation.push(new Output.NodeSpan(node)); - - // Process token based on prefix. - var prefix = token[0]; - token = token.slice(1); - - if (opt_exclude[token]) - return; - - // All possible tokens based on prefix. - if (prefix == '$') { - if (token == 'value') { - var text = node.value; - if (text !== undefined) { - if (node.textSelStart !== undefined) { - options.annotation.push(new Output.SelectionSpan( - node.textSelStart, - node.textSelEnd)); - } - } - // Annotate this as a name so we don't duplicate names from ancestors. - if (node.role == chrome.automation.RoleType.inlineTextBox || - node.role == chrome.automation.RoleType.staticText) - token = 'name'; - options.annotation.push(token); - this.append_(buff, text, options); - } else if (token == 'name') { - options.annotation.push(token); - if (this.formatOptions_.speech) { - var earconFinder = node; - while (earconFinder) { - var info = Output.ROLE_INFO_[earconFinder.role]; - if (info && info.earconId) { - options.annotation.push( - new Output.EarconAction(info.earconId)); - break; - } - earconFinder = earconFinder.parent; - } - } - - // Pending finalization of name calculation; we must use the - // description property to access aria-label. See crbug.com/473220. - var resolvedName = node.description || node.name; - this.append_(buff, resolvedName, options); - } else if (token == 'nameOrDescendants') { - options.annotation.push(token); - if (node.name) - this.append_(buff, node.name, options); - else - this.format_(node, '$descendants', buff); - } else if (token == 'indexInParent') { - options.annotation.push(token); - this.append_(buff, String(node.indexInParent + 1)); - } else if (token == 'parentChildCount') { - options.annotation.push(token); - if (node.parent) - this.append_(buff, String(node.parent.children.length)); - } else if (token == 'state') { - options.annotation.push(token); - Object.getOwnPropertyNames(node.state).forEach(function(s) { - this.append_(buff, s, options); - }.bind(this)); - } else if (token == 'find') { - // Find takes two arguments: JSON query string and format string. - if (tree.firstChild) { - var jsonQuery = tree.firstChild.value; - node = node.find( - /** @type {Object}*/(JSON.parse(jsonQuery))); - var formatString = tree.firstChild.nextSibling; - if (node) - this.format_(node, formatString, buff); - } - } else if (token == 'descendants') { - if (AutomationPredicate.leaf(node)) - return; - - // Construct a range to the leftmost and rightmost leaves. - var leftmost = AutomationUtil.findNodePre( - node, Dir.FORWARD, AutomationPredicate.leaf); - var rightmost = AutomationUtil.findNodePre( - node, Dir.BACKWARD, AutomationPredicate.leaf); - if (!leftmost || !rightmost) - return; - - var subrange = new cursors.Range( - new cursors.Cursor(leftmost, 0), - new cursors.Cursor(rightmost, 0)); - var prev = null; - if (node) - prev = cursors.Range.fromNode(node); - this.range_(subrange, prev, 'navigate', buff); - } else if (token == 'role') { - options.annotation.push(token); - var msg = node.role; - var info = Output.ROLE_INFO_[node.role]; - if (info) { - if (this.formatOptions_.braille) - msg = Msgs.getMsg(info.msgId + '_brl'); - else - msg = Msgs.getMsg(info.msgId); - } else { - console.error('Missing role info for ' + node.role); - } - this.append_(buff, msg, options); - } else if (token == 'inputType') { - if (!node.inputType) - return; - options.annotation.push(token); - var msgId = Output.INPUT_TYPE_MESSAGE_IDS_[node.inputType] || - 'input_type_text'; - if (this.formatOptions_.braille) - msgId = msgId + '_brl'; - this.append_(buff, Msgs.getMsg(msgId), options); - } else if (token == 'tableRowIndex' || - token == 'tableCellColumnIndex') { - var value = node[token]; - if (!value) - return; - value = String(value + 1); - options.annotation.push(token); - this.append_(buff, value, options); - } else if (node[token] !== undefined) { - options.annotation.push(token); - var value = node[token]; - if (typeof value == 'number') - value = String(value); - this.append_(buff, value, options); - } else if (Output.STATE_INFO_[token]) { - options.annotation.push('state'); - var stateInfo = Output.STATE_INFO_[token]; - var resolvedInfo = {}; - if (node.state[token] === undefined) - resolvedInfo = stateInfo.omitted; - else - resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off; - if (!resolvedInfo) - return; - if (this.formatOptions_.speech && resolvedInfo.earconId) { - options.annotation.push( - new Output.EarconAction(resolvedInfo.earconId)); - } - var msgId = - this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' : - resolvedInfo.msgId; - var msg = Msgs.getMsg(msgId); - this.append_(buff, msg, options); - } else if (tree.firstChild) { - // Custom functions. - if (token == 'if') { - var cond = tree.firstChild; - var attrib = cond.value.slice(1); - if (node[attrib] || node.state[attrib]) - this.format_(node, cond.nextSibling, buff); - else - this.format_(node, cond.nextSibling.nextSibling, buff); - } else if (token == 'earcon') { - // Ignore unless we're generating speech output. - if (!this.formatOptions_.speech) - return; - // Assumes there's existing output in our buffer. - var lastBuff = buff[buff.length - 1]; - if (!lastBuff) - return; - - lastBuff.setSpan( - new Output.EarconAction(tree.firstChild.value), 0, 0); - } else if (token == 'countChildren') { - var role = tree.firstChild.value; - var count = node.children.filter(function(e) { - return e.role == role; - }).length; - this.append_(buff, String(count)); - } - } - } else if (prefix == '@') { - var isPluralized = (token[0] == '@'); - if (isPluralized) - token = token.slice(1); - // Tokens can have substitutions. - var pieces = token.split('+'); - token = pieces.reduce(function(prev, cur) { - var lookup = cur; - if (cur[0] == '$') - lookup = node[cur.slice(1)]; - return prev + lookup; - }.bind(this), ''); - var msgId = token; - var msgArgs = []; - if (!isPluralized) { - var curArg = tree.firstChild; - while (curArg) { - if (curArg.value[0] != '$') { - console.error('Unexpected value: ' + curArg.value); - return; - } - var msgBuff = []; - this.format_(node, curArg, msgBuff); - msgArgs = msgArgs.concat(msgBuff); - curArg = curArg.nextSibling; - } - } - var msg = Msgs.getMsg(msgId, msgArgs); - try { - if (this.formatOptions_.braille) - msg = Msgs.getMsg(msgId + '_brl', msgArgs) || msg; - } catch(e) {} - - if (!msg) { - console.error('Could not get message ' + msgId); - return; - } - - if (isPluralized) { - var arg = tree.firstChild; - if (!arg || arg.nextSibling) { - console.error('Pluralized messages take exactly one argument'); - return; - } - if (arg.value[0] != '$') { - console.error('Unexpected value: ' + arg.value); - return; - } - var argBuff = []; - this.format_(node, arg, argBuff); - var namedArgs = {COUNT: Number(argBuff[0])}; - msg = new goog.i18n.MessageFormat(msg).format(namedArgs); - } - - this.append_(buff, msg, options); - } else if (prefix == '!') { - this.speechProperties_[token] = true; - } - }.bind(this)); - }, - - /** - * @param {!cursors.Range} range - * @param {cursors.Range} prevRange - * @param {chrome.automation.EventType|string} type - * @param {!Array<cvox.Spannable>} rangeBuff - * @private - */ - range_: function(range, prevRange, type, rangeBuff) { - if (!prevRange) - prevRange = cursors.Range.fromNode(range.start.node.root); - - var cursor = range.start; - var prevNode = prevRange.start.node; - - var formatNodeAndAncestors = function(node, prevNode) { - var buff = []; - this.ancestry_(node, prevNode, type, buff); - this.node_(node, prevNode, type, buff); - if (this.formatOptions_.location) - this.locations_.push(node.location); - return buff; - }.bind(this); - - while (cursor.node != range.end.node) { - var node = cursor.node; - rangeBuff.push.apply(rangeBuff, formatNodeAndAncestors(node, prevNode)); - prevNode = node; - cursor = cursor.move(cursors.Unit.NODE, - cursors.Movement.DIRECTIONAL, - Dir.FORWARD); - - // Reached a boundary. - if (cursor.node == prevNode) - break; - } - var lastNode = range.end.node; - rangeBuff.push.apply(rangeBuff, formatNodeAndAncestors(lastNode, prevNode)); - }, - - /** - * @param {!chrome.automation.AutomationNode} node - * @param {!chrome.automation.AutomationNode} prevNode - * @param {chrome.automation.EventType|string} type - * @param {!Array<cvox.Spannable>} buff - * @param {!Object=} opt_exclude A list of attributes to exclude from - * processing. - * @private - */ - ancestry_: function(node, prevNode, type, buff, opt_exclude) { - opt_exclude = opt_exclude || {}; - var prevUniqueAncestors = - AutomationUtil.getUniqueAncestors(node, prevNode); - var uniqueAncestors = AutomationUtil.getUniqueAncestors(prevNode, node); - - // First, look up the event type's format block. - // Navigate is the default event. - var eventBlock = Output.RULES[type] || Output.RULES['navigate']; - - var getMergedRoleBlock = function(role) { - var parentRole = (Output.ROLE_INFO_[role] || {}).inherits; - var roleBlock = eventBlock[role] || eventBlock['default']; - var parentRoleBlock = parentRole ? eventBlock[parentRole] : {}; - var mergedRoleBlock = {}; - for (var key in parentRoleBlock) - mergedRoleBlock[key] = parentRoleBlock[key]; - for (var key in roleBlock) - mergedRoleBlock[key] = roleBlock[key]; - return mergedRoleBlock; - }; - - for (var i = 0, formatPrevNode; - (formatPrevNode = prevUniqueAncestors[i]); - i++) { - var roleBlock = getMergedRoleBlock(formatPrevNode.role); - if (roleBlock.leave) - this.format_(formatPrevNode, roleBlock.leave, buff, opt_exclude); - } - - var enterOutputs = []; - var enterRole = {}; - for (var j = uniqueAncestors.length - 2, formatNode; - (formatNode = uniqueAncestors[j]); - j--) { - var roleBlock = getMergedRoleBlock(formatNode.role); - if (roleBlock.enter) { - if (enterRole[formatNode.role]) - continue; - enterRole[formatNode.role] = true; - var tempBuff = []; - this.format_(formatNode, roleBlock.enter, tempBuff, opt_exclude); - enterOutputs.unshift(tempBuff); - } - if (formatNode.role == 'window') - break; - } - enterOutputs.forEach(function(b) { - buff.push.apply(buff, b); - }); - - if (!opt_exclude.stay) { - var commonFormatNode = uniqueAncestors[0]; - while (commonFormatNode && commonFormatNode.parent) { - commonFormatNode = commonFormatNode.parent; - var roleBlock = - eventBlock[commonFormatNode.role] || eventBlock['default']; - if (roleBlock.stay) - this.format_(commonFormatNode, roleBlock.stay, buff, opt_exclude); - } - } - }, - - /** - * @param {!chrome.automation.AutomationNode} node - * @param {!chrome.automation.AutomationNode} prevNode - * @param {chrome.automation.EventType|string} type - * @param {!Array<cvox.Spannable>} buff - * @private - */ - node_: function(node, prevNode, type, buff) { - // Navigate is the default event. - var eventBlock = Output.RULES[type] || Output.RULES['navigate']; - var roleBlock = eventBlock[node.role] || eventBlock['default']; - var speakFormat = roleBlock.speak || eventBlock['default'].speak; - this.format_(node, speakFormat, buff); - }, - - /** - * @param {!cursors.Range} range - * @param {cursors.Range} prevRange - * @param {chrome.automation.EventType|string} type - * @param {!Array<cvox.Spannable>} buff - * @private - */ - subNode_: function(range, prevRange, type, buff) { - if (!prevRange) - prevRange = range; - var dir = cursors.Range.getDirection(prevRange, range); - var prevNode = prevRange.getBound(dir).node; - this.ancestry_( - range.start.node, prevNode, type, buff, - {stay: true, name: true, value: true}); - var startIndex = range.start.getIndex(); - var endIndex = range.end.getIndex(); - if (startIndex === endIndex) - endIndex++; - this.append_( - buff, range.start.getText().substring(startIndex, endIndex)); - }, - - /** - * Appends output to the |buff|. - * @param {!Array<cvox.Spannable>} buff - * @param {string|!cvox.Spannable} value - * @param {{isUnique: (boolean|undefined), - * annotation: !Array<*>}=} opt_options - */ - append_: function(buff, value, opt_options) { - opt_options = opt_options || {isUnique: false, annotation: []}; - - // Reject empty values without annotations. - if ((!value || value.length == 0) && opt_options.annotation.length == 0) - return; - - var spannableToAdd = new cvox.Spannable(value); - opt_options.annotation.forEach(function(a) { - spannableToAdd.setSpan(a, 0, spannableToAdd.getLength()); - }); - - // |isUnique| specifies an annotation that cannot be duplicated. - if (opt_options.isUnique) { - var annotationSansNodes = opt_options.annotation.filter( - function(annotation) { - return !(annotation instanceof Output.NodeSpan); - }); - var alreadyAnnotated = buff.some(function(s) { - return annotationSansNodes.some(function(annotation) { - var start = s.getSpanStart(annotation); - var end = s.getSpanEnd(annotation); - if (start === undefined) - return false; - return s.substring(start, end).toString() == value.toString(); - }); - }); - if (alreadyAnnotated) - return; - } - - buff.push(spannableToAdd); - }, - - /** - * Parses the token containing a custom function and returns a tree. - * @param {string} inputStr - * @return {Object} - * @private - */ - createParseTree_: function(inputStr) { - var root = {value: ''}; - var currentNode = root; - var index = 0; - var braceNesting = 0; - while (index < inputStr.length) { - if (inputStr[index] == '(') { - currentNode.firstChild = {value: ''}; - currentNode.firstChild.parent = currentNode; - currentNode = currentNode.firstChild; - } else if (inputStr[index] == ')') { - currentNode = currentNode.parent; - } else if (inputStr[index] == '{') { - braceNesting++; - currentNode.value += inputStr[index]; - } else if (inputStr[index] == '}') { - braceNesting--; - currentNode.value += inputStr[index]; - } else if (inputStr[index] == ',' && braceNesting === 0) { - currentNode.nextSibling = {value: ''}; - currentNode.nextSibling.parent = currentNode.parent; - currentNode = currentNode.nextSibling; - } else { - currentNode.value += inputStr[index]; - } - index++; - } - - if (currentNode != root) - throw 'Unbalanced parenthesis.'; - - return root; - }, - - /** - * Converts the currently rendered braille buffers to a single spannable. - * @return {!cvox.Spannable} - * @private - */ - createBrailleOutput_: function() { - var result = new cvox.Spannable(); - var separator = ''; // Changes to space as appropriate. - this.brailleBuffer_.forEach(function(cur) { - // If this chunk is empty, don't add it since it won't result - // in any output on the braille display, but node spans would - // start before the separator in that case, which is not desired. - // The exception is if this chunk contains a selectionm, in which - // case it will result in a cursor which has to be preserved. - // In this case, having separators, potentially both before and after - // the empty string is correct. - if (cur.getLength() == 0 && !cur.getSpanInstanceOf(Output.SelectionSpan)) - return; - var spansToExtend = []; - var spansToRemove = []; - // Nodes that have node spans both on the character to the left - // of the separator and to the right should also cover the separator. - // We extend the left span to cover both the separator and what the - // right span used to cover, removing the right span, mostly for - // ease of writing tests and debug. - // Note that getSpan(position) never returns zero length spans - // (because they don't cover any position). Still, we want to include - // these because they can be included (the selection span in an empty - // text field is an example), which is why we write the below code - // using getSpansInstanceOf and check the endpoints (isntead of doing - // the opposite). - result.getSpansInstanceOf(Output.NodeSpan).forEach(function(leftSpan) { - if (result.getSpanEnd(leftSpan) < result.getLength()) - return; - var newEnd = result.getLength(); - cur.getSpansInstanceOf(Output.NodeSpan).forEach(function(rightSpan) { - if (cur.getSpanStart(rightSpan) == 0 && - leftSpan.node === rightSpan.node) { - newEnd = Math.max( - newEnd, - result.getLength() + separator.length + - cur.getSpanEnd(rightSpan)); - spansToRemove.push(rightSpan); - } - }); - if (newEnd > result.getLength()) - spansToExtend.push({span: leftSpan, end: newEnd}); - }); - result.append(separator); - result.append(cur); - spansToExtend.forEach(function(elem) { - result.setSpan( - elem.span, - // Cast ok, since span is known to exist. - /** @type {number} */ (result.getSpanStart(elem.span)), - elem.end); - }); - spansToRemove.forEach(result.removeSpan.bind(result)); - separator = Output.SPACE; - }); - return result; - } -}; - -}); // goog.scope diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs b/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs deleted file mode 100644 index ad642ed0e50..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs +++ /dev/null @@ -1,545 +0,0 @@ -// 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. - -GEN_INCLUDE(['../../testing/assert_additions.js']); -GEN_INCLUDE(['../../testing/chromevox_next_e2e_test_base.js']); - -/** - * Gets the braille output and asserts that it matches expected values. - * Annotations in the output that are primitive strings are ignored. - */ -function checkBrailleOutput(expectedText, expectedSpans, output) { - var actualOutput = output.brailleOutputForTest; - // Remove string annotations. These are tested in the speech output and - // there's no need to clutter the tests with the corresponding braille - // annotations. - var actualSpans = actualOutput.spans_.filter(function(span) { - return (typeof span.value !== 'string'); - }); - assertEquals(expectedText, actualOutput.toString()); - - function describeSpan(span) { - var obj = {value: span.value, start: span.start, end: span.end}; - if (obj.value instanceof Output.NodeSpan) { - obj.value.node = (obj.value.node.name || '') + ' ' + - obj.value.node.toString(); - } - return JSON.stringify(obj); - } - - function describeActualSpans() { - return '\nAll actual spans:\n' + actualSpans.map(describeSpan).join('\n'); - } - - for (var i = 0, max = Math.max(expectedSpans.length, actualSpans.length); - i < max; ++i) { - var expectedSpan = expectedSpans[i]; - var actualSpan = actualSpans[i]; - if (!expectedSpan) - throw Error('Unexpected span in ' + expectedText + ': ' + - describeSpan(actualSpan) + describeActualSpans()); - if (!actualSpan) - throw Error('Missing expected span in ' + expectedText + ': ' + - describeSpan(expectedSpan) + describeActualSpans()); - var equal = true; - if (expectedSpan.start !== actualSpan.start || - expectedSpan.end !== actualSpan.end) { - equal = false; - } else if (expectedSpan.value instanceof Output.NodeSpan && - (!(actualSpan.value instanceof Output.NodeSpan) || - expectedSpan.value.node !== actualSpan.value.node)) { - equal = false; - } else { - equal = (JSON.stringify(expectedSpan.value) === - JSON.stringify(actualSpan.value)); - } - if (!equal) { - throw Error('Spans differ in ' + expectedText + ':\n' + - 'Expected: ' + describeSpan(expectedSpan) + '\n' + - 'Got : ' + describeSpan(actualSpan) + describeActualSpans()); - } - } -} - -/** - * Test fixture for output.js. - * @constructor - * @extends {ChromeVoxNextE2ETestBase} - */ -function OutputE2ETest() { - ChromeVoxNextE2ETest.call(this); -} - -OutputE2ETest.prototype = { - __proto__: ChromeVoxNextE2ETest.prototype, - - /** @override */ - setUp: function() { - window.Dir = AutomationUtil.Dir; - } -}; - -TEST_F('OutputE2ETest', 'Links', function() { - this.runWithLoadedTree('<a href="#">Click here</a>', - function(root) { - var el = root.firstChild.firstChild; - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON({string_: 'Click here|Link', 'spans_': [ - // Attributes. - {value: 'name', start: 0, end: 10}, - - // Link earcon (based on the name). - {value: {earconId: 'LINK'}, start: 0, end: 10}, - - {value: 'role', start: 11, end: 15} - ]}, o.speechOutputForTest); - checkBrailleOutput( - 'Click here lnk', - [{value: new Output.NodeSpan(el), start: 0, end: 14}], - o); - }); -}); - -TEST_F('OutputE2ETest', 'Checkbox', function() { - this.runWithLoadedTree('<input type="checkbox">', - function(root) { - var el = root.firstChild.firstChild; - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON({string_: '|Check box|not checked', 'spans_': [ - // Attributes. - {value: 'name', start: 0, end: 0}, - {value: 'role', start: 1, end: 10}, - {value: 'state', start: 11, end: 22}, - - // Checkbox earcon (based on the state). - {value: {earconId: 'CHECK_OFF'}, start: 11, end: 22} - ]}, o.speechOutputForTest); - checkBrailleOutput( - 'chk ( )', - [{value: new Output.NodeSpan(el), start: 0, end: 7}], - o); - }); -}); - -TEST_F('OutputE2ETest', 'InLineTextBoxValueGetsIgnored', function() { - this.runWithLoadedTree('<p>OK', - function(root) { - var el = root.firstChild.firstChild.firstChild; - assertEquals('inlineTextBox', el.role); - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON({string_: 'OK', 'spans_': [ - // Attributes. - {value: 'name', start: 0, end: 2} - ]}, o.speechOutputForTest); - checkBrailleOutput( - 'OK', - [{value: new Output.NodeSpan(el), start: 0, end: 2}], - o); - - el = root.firstChild.firstChild; - assertEquals('staticText', el.role); - range = cursors.Range.fromNode(el); - o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON({string_: 'OK', 'spans_': [ - // Attributes. - {value: 'name', start: 0, end: 2} - ]}, o.speechOutputForTest); - checkBrailleOutput( - 'OK', - [{value: new Output.NodeSpan(el), start: 0, end: 2}], - o); - }); -}); - -TEST_F('OutputE2ETest', 'Headings', function() { - this.runWithLoadedTree(function() {/*! - <h1>a</h1><h2>b</h2><h3>c</h3><h4>d</h4><h5>e</h5><h6>f</h6> - <h1><a href="a.com">b</a></h1> */}, - function(root) { - var el = root.firstChild; - for (var i = 1; i <= 6; ++i) { - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - var letter = String.fromCharCode('a'.charCodeAt(0) + i -1); - assertEqualsJSON({string_: 'Heading ' + i + '|' + letter, 'spans_': [ - // Attributes. - {value: 'nameOrDescendants', start: 10, end: 11} - ]}, o.speechOutputForTest); - checkBrailleOutput( - 'h' + i + ' ' + letter, - [{value: new Output.NodeSpan(el), start: 0, end: 4}], - o); - el = el.nextSibling; - } - - range = cursors.Range.fromNode(el); - o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON({string_: 'Heading 1|b|Link', 'spans_': [ - // Link. - {value: 'name', start: 10, end: 11}, - {value: {earconId: "LINK"}, start: 10, end: 11}, - {value: 'role', start: 12, end: 16} - ]}, o.speechOutputForTest); - checkBrailleOutput( - 'h1 b lnk', - [{value: new Output.NodeSpan(el), start: 0, end: 2}, - {value: new Output.NodeSpan(el.firstChild), start: 3, end: 8}], - o); - }); -}); - -TEST_F('OutputE2ETest', 'Audio', function() { - this.runWithLoadedTree('<audio src="foo.mp3" controls></audio>', - function(root) { - var el = root.firstChild.firstChild.firstChild.firstChild; - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON( - {string_: 'audio|Tool bar|play||begin playback|Button', - spans_: - // Entered container toolbar. - - // Button. - [{value: 'name', start: 15, end: 19}, - - // Button earcon. - {value: {earconId: "BUTTON"}, start: 15, end: 19}, - - {value: 'value', start: 20, end: 20}, - {value: 'help', start: 21, end: 35}, - {value: 'role', start: 36, end: 42}] - }, o.speechOutputForTest); - checkBrailleOutput( - 'audio tlbar play begin playback btn', - [{value: new Output.NodeSpan(el.parent), start: 0, end: 11}, - {value: new Output.NodeSpan(el), start: 12, end: 35}], - o); - - el = el.nextSibling; - var prevRange = range; - range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, prevRange, 'navigate'); - assertEqualsJSON({string_: '0, , slider|audio time scrubber', - spans_: - [{value: 'help', start: 12, end: 31}] - }, o.speechOutputForTest); - // TODO(plundblad): Investigate this. - checkBrailleOutput( - '0, , slider audio time scrubber', - [{value: new Output.NodeSpan(el), start: 0, end: 31}], - o); - }); -}); - -TEST_F('OutputE2ETest', 'Input', function() { - this.runWithLoadedTree( - '<input type="text"></input>' + - '<input type="email"></input>' + - '<input type="password"></input>' + - '<input type="tel"></input>' + - '<input type="number"></input>' + - '<input type="time"></input>' + - '<input type="date"></input>' + - '<input type="file"</input>' + - '<input type="search"</input>' + - '<input type="invalidType"</input>', - function(root) { - var expected = {string_: '', 'spans_': [ - {value: 'name', start: 0, end: 0}, - - // Earcon - {value: {earconId: 'EDITABLE_TEXT'}, start: 0, end: 0}, - - // Selection span. - {value: {startIndex: 0, endIndex: 0}, start: 1, end: 1}, - - {value: 'value', start: 1, end: 1}, - {value: 'inputType', start: 2} - ]}; - - var expectedSpeechValues = [ - '||Edit text', - '||Edit text, email entry', - '||Password edit text', - '||Edit text numeric only', - {string_: '||Spin button', spans_: [{value: 'name', start: 0, end: 0}, - {value: {earconId:'LISTBOX'}, start: 0, end: 0}, - {value: {startIndex: 0, endIndex: 0}, start: 1, end: 1}, - {value: 'value', start: 1, end: 1}, - {value: 'role', start: 2, end: 13}]}, - {string_: '||Time control', spans_: [{value: 'name', start: 0, end: 0}, - {value: 'value', start: 1, end: 1}, - {value: 'role', start: 2, end: 14}]}, - {string_: '||Date control', spans_: [{value: 'name', start: 0, end: 0}, - {value: 'value', start: 1, end: 1}, - {value: 'role', start: 2, end: 14}]}, - {string_: 'Choose File|No file chosen|Button', - spans_: [{value: 'name', start: 0, end: 11}, - {value: {earconId: "BUTTON"}, start: 0, end: 11}, - {value: 'value', start: 12, end: 26}, - {value: 'role', start: 27, end: 33}]}, - '||Edit text, search entry', - '||Edit text' - ]; - // TODO(plundblad): Some of these are wrong, there should be an initial - // space for the cursor in edit fields. - var expectedBrailleValues = [ - ' ed', - ' @ed', - ' pwded', - ' #ed', - ' spnbtn', - {string_: 'time'}, - {string_: 'date'}, - {string_: 'Choose File No file chosen btn'}, - ' srched', - ' ed' - ]; - assertEquals(expectedSpeechValues.length, expectedBrailleValues.length); - - var el = root.firstChild.firstChild; - expectedSpeechValues.forEach(function(expectedValue) { - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - if (typeof expectedValue == 'object') { - assertEqualsJSON(expectedValue, o.speechOutputForTest); - } else { - expected.string_ = expectedValue; - expected.spans_[4].end = expectedValue.length; - assertEqualsJSON(expected, o.speechOutputForTest); - } - el = el.nextSibling; - }); - - el = root.firstChild.firstChild; - expectedBrailleValues.forEach(function(expectedValue) { - var range = cursors.Range.fromNode(el); - var o = new Output().withBraille(range, null, 'navigate'); - if (typeof expectedValue === 'string') { - checkBrailleOutput( - expectedValue, - [{value: {startIndex: 0, endIndex: 0}, start: 0, end: 0}, - {value: new Output.NodeSpan(el), start: 0, - end: expectedValue.length}], - o); - } else { - checkBrailleOutput( - expectedValue.string_, - [{value: new Output.NodeSpan(el), start: 0, - end: expectedValue.string_.length}], - o); - } - el = el.nextSibling; - }); - }); -}); - -TEST_F('OutputE2ETest', 'List', function() { - this.runWithLoadedTree( - '<ul><li>a<li>b<li>c</ul>', - function(root) { - var el = root.firstChild.firstChild; - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON({string_: 'list|with 3 items|a||List item', spans_: [ - {value: 'name', start: 18, end: 19}, - {value: {earconId:'LIST_ITEM'}, start: 18, end: 19}, - {value: 'value', start:20, end: 20}, - {value: 'role', start: 21, end: 30} - ]}, o.speechOutputForTest); - // TODO(plundblad): This output is wrong. Add special handling for - // braille here. - checkBrailleOutput( - 'list +3 a lstitm', - [{value: new Output.NodeSpan(el.parent), start: 0, end: 7}, - {value: new Output.NodeSpan(el), start: 8, end: 16}], - o); - }); -}); - -TEST_F('OutputE2ETest', 'Tree', function() { - this.runWithLoadedTree(function() {/*! - <ul role="tree"> - <li aria-expanded="true" role="treeitem">a - <li role="treeitem">b - <li aria-expanded="false" role="treeitem">c - </ul> - */}, - function(root) { - var el = root.firstChild.children[0].firstChild; - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON( - {string_: '|Tree|with 3 items|Tree item|Expanded| 1 of 3 | level 1 |a', - spans_: [ - // Enter rule. - - // TreeItem. - {value: 'role','start': 19, end: 28}, - {value: 'state', start: 29, end: 37}, - - // InLineTextBox. - {value: 'name', start: 57, end: 58} - ]}, o.speechOutputForTest); - // TODO(plundblad): Braille output is wrong. - checkBrailleOutput( - 'tree +3 tritm - 1/3 level 1 a', - [{value: new Output.NodeSpan(el.parent.parent), start: 0, end: 7}, - {value: new Output.NodeSpan(el.parent), start: 8, end: 29}, - {value: new Output.NodeSpan(el), start: 30, end: 31}], - o); - - el = root.firstChild.children[1].firstChild; - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON( - {string_: '|Tree|with 3 items|Tree item| 2 of 3 | level 1 |b', - spans_: [ - // Enter rule. - - // TreeItem. - {value: 'role','start': 19, end: 28}, - - // InLineTextBox. - {value: 'name', start: 48, end: 49} - ]}, o.speechOutputForTest); - // TODO(plundblad): Braille output is wrong. - checkBrailleOutput( - 'tree +3 tritm 2/3 level 1 b', - [{value: new Output.NodeSpan(el.parent.parent), start: 0, end: 7}, - {value: new Output.NodeSpan(el.parent), start: 8, end: 27}, - {value: new Output.NodeSpan(el), start: 28, end: 29}], - o); - - el = root.firstChild.children[2].firstChild; - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON( - {string_: '|Tree|with 3 items|Tree item|Collapsed| 3 of 3 | level 1 |c', - spans_: [ - // Enter rule. - - // TreeItem. - {value: 'role','start': 19, end: 28}, - {value: 'state', start: 29, end: 38}, - - // InLineTextBox. - {value: 'name', start: 58, end: 59} - ]}, o.speechOutputForTest); - // TODO(plundblad): Braille output is wrong. - checkBrailleOutput( - 'tree +3 tritm + 3/3 level 1 c', - [{value: new Output.NodeSpan(el.parent.parent), start: 0, end: 7}, - {value: new Output.NodeSpan(el.parent), start: 8, end: 29}, - {value: new Output.NodeSpan(el), start: 30, end: 31}], - o); - }); -}); - -TEST_F('OutputE2ETest', 'Menu', function() { - this.runWithLoadedTree(function() {/*! - <div role="menu"> - <div role="menuitem">a</div> - </div> - */}, - function(root) { - var el = root.firstChild.firstChild; - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON({string_: - '|Menu|with 1 item|a|Menu item| 1 of 1 ', spans_: [ - {value: 'name', start: 18, end: 19}, - {value: 'role', start:20, end: 29} - ]}, o.speechOutputForTest); - checkBrailleOutput( - 'mnu +1 a mnuitm 1/1', - [{value: new Output.NodeSpan(el.parent), start: 0, end: 6}, - {value: new Output.NodeSpan(el), start: 7, end: 19}], - o); - }); -}); - -TEST_F('OutputE2ETest', 'ListBox', function() { - this.runWithLoadedTree(function() {/*! - <select multiple> - <option>1</option> - <option>2</option> - </select> - */}, - function(root) { - var el = root.firstChild.firstChild.firstChild; - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON({string_: '|List box|with 2 items||List item| 1 of 2 ', - spans_: [ - // ListBox. - // Earcon. - {value: {earconId:'LISTBOX'}, start: 0, end: 0}, - - {value: 'name', start: 23, end: 23}, - - // Earcon. - {value: {earconId: 'LIST_ITEM'}, start: 23, end: 23}, - - {value: 'role', start:24, end: 33} - ]}, o.speechOutputForTest); - checkBrailleOutput( - 'lstbx +2 lstitm 1/2', - [{value: new Output.NodeSpan(el.parent), start: 0, end: 8}, - {value: new Output.NodeSpan(el), start: 9, end: 19}], - o); - }); -}); - -SYNC_TEST_F('OutputE2ETest', 'MessageIdAndEarconValidity', function() { - for (var key in Output.ROLE_INFO_) { - var value = Output.ROLE_INFO_[key]; - Msgs.getMsg(value.msgId); - Msgs.getMsg(value.msgId + '_brl'); - assertFalse(/[A-Z]+/.test(value.msgId)); - if (value.earconId) - assertNotNullNorUndefined(cvox.Earcon[value.earconId]); - } - for (var key in Output.STATE_INFO_) { - var value = Output.STATE_INFO_[key]; - for (innerKey in value) { - var innerValue = value[innerKey]; - Msgs.getMsg(innerValue.msgId); - Msgs.getMsg(innerValue.msgId + '_brl'); - assertFalse(/[A-Z]+/.test(innerValue.msgId)); - if (innerValue.earconId) - assertNotNullNorUndefined(cvox.Earcon[innerValue.earconId]); - } - } - for (var key in Output.INPUT_TYPE_MESSAGE_IDS_) { - var msgId = Output.INPUT_TYPE_MESSAGE_IDS_[key]; - assertFalse(/[A-Z]+/.test(msgId)); - Msgs.getMsg(msgId); - Msgs.getMsg(msgId + '_brl'); - } -}); - -TEST_F('OutputE2ETest', 'DivOmitsRole', function() { - this.runWithLoadedTree(function() {/*! - <div>that has content</div> - <div></div> - <div role='group'><div>nested content</div></div> - */}, - function(root) { - var el = root.firstChild.firstChild; - var range = cursors.Range.fromNode(el); - var o = new Output().withSpeechAndBraille(range, null, 'navigate'); - assertEqualsJSON({string_: '|that has content', - spans_: [ - {value: 'name', start: 1, end: 17} - ]}, o.speechOutputForTest); - checkBrailleOutput( - 'that has content', - [{value: new Output.NodeSpan(el), start: 0, end: 16}], - o); - }); -}); |