| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview The entry point for all ChromeVox2 related code for the | 6 * @fileoverview The entry point for all ChromeVox2 related code for the |
| 7 * background page. | 7 * background page. |
| 8 */ | 8 */ |
| 9 | 9 |
| 10 goog.provide('Background'); | 10 goog.provide('Background'); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 goog.require('cvox.ChromeVoxEditableTextBase'); | 31 goog.require('cvox.ChromeVoxEditableTextBase'); |
| 32 goog.require('cvox.ClassicEarcons'); | 32 goog.require('cvox.ClassicEarcons'); |
| 33 goog.require('cvox.ExtensionBridge'); | 33 goog.require('cvox.ExtensionBridge'); |
| 34 goog.require('cvox.NavBraille'); | 34 goog.require('cvox.NavBraille'); |
| 35 | 35 |
| 36 goog.scope(function() { | 36 goog.scope(function() { |
| 37 var AutomationNode = chrome.automation.AutomationNode; | 37 var AutomationNode = chrome.automation.AutomationNode; |
| 38 var Dir = constants.Dir; | 38 var Dir = constants.Dir; |
| 39 var EventType = chrome.automation.EventType; | 39 var EventType = chrome.automation.EventType; |
| 40 var RoleType = chrome.automation.RoleType; | 40 var RoleType = chrome.automation.RoleType; |
| 41 var StateType = chrome.automation.StateType; |
| 41 | 42 |
| 42 /** | 43 /** |
| 43 * ChromeVox2 background page. | 44 * ChromeVox2 background page. |
| 44 * @constructor | 45 * @constructor |
| 45 * @extends {ChromeVoxState} | 46 * @extends {ChromeVoxState} |
| 46 */ | 47 */ |
| 47 Background = function() { | 48 Background = function() { |
| 48 ChromeVoxState.call(this); | 49 ChromeVoxState.call(this); |
| 49 | 50 |
| 50 /** | 51 /** |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 248 if (!target) | 249 if (!target) |
| 249 return useNext ? ChromeVoxMode.FORCE_NEXT : ChromeVoxMode.CLASSIC; | 250 return useNext ? ChromeVoxMode.FORCE_NEXT : ChromeVoxMode.CLASSIC; |
| 250 | 251 |
| 251 // Closure complains, but clearly, |target| is not null. | 252 // Closure complains, but clearly, |target| is not null. |
| 252 var topLevelRoot = | 253 var topLevelRoot = |
| 253 AutomationUtil.getTopLevelRoot(/** @type {!AutomationNode} */(target)); | 254 AutomationUtil.getTopLevelRoot(/** @type {!AutomationNode} */(target)); |
| 254 if (!topLevelRoot) | 255 if (!topLevelRoot) |
| 255 return useNext ? ChromeVoxMode.FORCE_NEXT : | 256 return useNext ? ChromeVoxMode.FORCE_NEXT : |
| 256 ChromeVoxMode.CLASSIC_COMPAT; | 257 ChromeVoxMode.CLASSIC_COMPAT; |
| 257 | 258 |
| 258 var nextSite = this.isWhitelistedForNext_(topLevelRoot.docUrl); | 259 var docUrl = topLevelRoot.docUrl || ''; |
| 259 var nextCompat = this.nextCompatRegExp_.test(topLevelRoot.docUrl); | 260 var nextSite = this.isWhitelistedForNext_(docUrl) |
| 261 var nextCompat = this.nextCompatRegExp_.test(docUrl); |
| 260 var classicCompat = | 262 var classicCompat = |
| 261 this.isWhitelistedForClassicCompat_(topLevelRoot.docUrl); | 263 this.isWhitelistedForClassicCompat_(docUrl); |
| 262 if (nextCompat && useNext) | 264 if (nextCompat && useNext) |
| 263 return ChromeVoxMode.NEXT_COMPAT; | 265 return ChromeVoxMode.NEXT_COMPAT; |
| 264 else if (classicCompat && !useNext) | 266 else if (classicCompat && !useNext) |
| 265 return ChromeVoxMode.CLASSIC_COMPAT; | 267 return ChromeVoxMode.CLASSIC_COMPAT; |
| 266 else if (nextSite) | 268 else if (nextSite) |
| 267 return ChromeVoxMode.NEXT; | 269 return ChromeVoxMode.NEXT; |
| 268 else if (!useNext) | 270 else if (!useNext) |
| 269 return ChromeVoxMode.CLASSIC; | 271 return ChromeVoxMode.CLASSIC; |
| 270 else | 272 else |
| 271 return ChromeVoxMode.FORCE_NEXT; | 273 return ChromeVoxMode.FORCE_NEXT; |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 if (oldMode != newMode) { | 411 if (oldMode != newMode) { |
| 410 this.onModeChanged_(newMode, oldMode); | 412 this.onModeChanged_(newMode, oldMode); |
| 411 this.mode_ = newMode; | 413 this.mode_ = newMode; |
| 412 } | 414 } |
| 413 | 415 |
| 414 if (this.currentRange_) { | 416 if (this.currentRange_) { |
| 415 var start = this.currentRange_.start.node; | 417 var start = this.currentRange_.start.node; |
| 416 start.makeVisible(); | 418 start.makeVisible(); |
| 417 | 419 |
| 418 var root = start.root; | 420 var root = start.root; |
| 419 if (!root || root.role == RoleType.desktop) | 421 if (!root || root.role == RoleType.DESKTOP) |
| 420 return; | 422 return; |
| 421 | 423 |
| 422 var position = {}; | 424 var position = {}; |
| 423 var loc = start.location; | 425 var loc = start.location; |
| 424 position.x = loc.left + loc.width / 2; | 426 position.x = loc.left + loc.width / 2; |
| 425 position.y = loc.top + loc.height / 2; | 427 position.y = loc.top + loc.height / 2; |
| 426 var url = root.docUrl; | 428 var url = root.docUrl; |
| 427 url = url.substring(0, url.indexOf('#')) || url; | 429 url = url.substring(0, url.indexOf('#')) || url; |
| 428 cvox.ChromeVox.position[url] = position; | 430 cvox.ChromeVox.position[url] = position; |
| 429 } | 431 } |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 492 if (this.pageSel_) | 494 if (this.pageSel_) |
| 493 this.pageSel_.select(); | 495 this.pageSel_.select(); |
| 494 } | 496 } |
| 495 } else { | 497 } else { |
| 496 // Ensure we don't select the editable when we first encounter it. | 498 // Ensure we don't select the editable when we first encounter it. |
| 497 var lca = null; | 499 var lca = null; |
| 498 if (range.start.node && prevRange.start.node) { | 500 if (range.start.node && prevRange.start.node) { |
| 499 lca = AutomationUtil.getLeastCommonAncestor(prevRange.start.node, | 501 lca = AutomationUtil.getLeastCommonAncestor(prevRange.start.node, |
| 500 range.start.node); | 502 range.start.node); |
| 501 } | 503 } |
| 502 if (!lca || lca.state.editable || !range.start.node.state.editable) | 504 if (!lca || lca.state[StateType.EDITABLE] || |
| 505 !range.start.node.state[StateType.EDITABLE]) |
| 503 range.select(); | 506 range.select(); |
| 504 } | 507 } |
| 505 | 508 |
| 506 o.withRichSpeechAndBraille( | 509 o.withRichSpeechAndBraille( |
| 507 selectedRange || range, prevRange, Output.EventType.NAVIGATE) | 510 selectedRange || range, prevRange, Output.EventType.NAVIGATE) |
| 508 .withQueueMode(cvox.QueueMode.FLUSH); | 511 .withQueueMode(cvox.QueueMode.FLUSH); |
| 509 | 512 |
| 510 if (msg) | 513 if (msg) |
| 511 o.format(msg); | 514 o.format(msg); |
| 512 | 515 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 586 }, | 589 }, |
| 587 | 590 |
| 588 /** | 591 /** |
| 589 * Compat mode is on if any of the following are true: | 592 * Compat mode is on if any of the following are true: |
| 590 * 1. a url is blacklisted for Classic. | 593 * 1. a url is blacklisted for Classic. |
| 591 * 2. the current range is not within web content. | 594 * 2. the current range is not within web content. |
| 592 * @param {string} url | 595 * @param {string} url |
| 593 * @return {boolean} | 596 * @return {boolean} |
| 594 */ | 597 */ |
| 595 isWhitelistedForClassicCompat_: function(url) { | 598 isWhitelistedForClassicCompat_: function(url) { |
| 596 return this.isBlacklistedForClassic_(url) || (this.getCurrentRange() && | 599 return (this.isBlacklistedForClassic_(url) || (this.getCurrentRange() && |
| 597 !this.getCurrentRange().isWebRange() && | 600 !this.getCurrentRange().isWebRange() && |
| 598 this.getCurrentRange().start.node.state.focused); | 601 this.getCurrentRange().start.node.state[StateType.FOCUSED])) || false; |
| 599 }, | 602 }, |
| 600 | 603 |
| 601 /** | 604 /** |
| 602 * @param {string} url | 605 * @param {string} url |
| 603 * @return {boolean} | 606 * @return {boolean} |
| 604 * @private | 607 * @private |
| 605 */ | 608 */ |
| 606 isBlacklistedForClassic_: function(url) { | 609 isBlacklistedForClassic_: function(url) { |
| 607 if (this.classicBlacklistRegExp_.test(url)) | 610 if (this.classicBlacklistRegExp_.test(url)) |
| 608 return true; | 611 return true; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 665 } else if (span instanceof Output.NodeSpan) { | 668 } else if (span instanceof Output.NodeSpan) { |
| 666 if (!actionNodeSpan || | 669 if (!actionNodeSpan || |
| 667 text.getSpanLength(span) <= text.getSpanLength(actionNodeSpan)) { | 670 text.getSpanLength(span) <= text.getSpanLength(actionNodeSpan)) { |
| 668 actionNodeSpan = span; | 671 actionNodeSpan = span; |
| 669 } | 672 } |
| 670 } | 673 } |
| 671 }); | 674 }); |
| 672 if (!actionNodeSpan) | 675 if (!actionNodeSpan) |
| 673 return; | 676 return; |
| 674 var actionNode = actionNodeSpan.node; | 677 var actionNode = actionNodeSpan.node; |
| 675 if (actionNode.role === RoleType.inlineTextBox) | 678 if (actionNode.role === RoleType.INLINE_TEXT_BOX) |
| 676 actionNode = actionNode.parent; | 679 actionNode = actionNode.parent; |
| 677 actionNode.doDefault(); | 680 actionNode.doDefault(); |
| 678 if (selectionSpan) { | 681 if (selectionSpan) { |
| 679 var start = text.getSpanStart(selectionSpan); | 682 var start = text.getSpanStart(selectionSpan); |
| 680 var targetPosition = position - start; | 683 var targetPosition = position - start; |
| 681 actionNode.setSelection(targetPosition, targetPosition); | 684 actionNode.setSelection(targetPosition, targetPosition); |
| 682 } | 685 } |
| 683 }, | 686 }, |
| 684 | 687 |
| 685 /** | 688 /** |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 785 setFocusToRange_: function(range, prevRange) { | 788 setFocusToRange_: function(range, prevRange) { |
| 786 var start = range.start.node; | 789 var start = range.start.node; |
| 787 var end = range.end.node; | 790 var end = range.end.node; |
| 788 | 791 |
| 789 // First, see if we've crossed a root. Remove once webview handles focus | 792 // First, see if we've crossed a root. Remove once webview handles focus |
| 790 // correctly. | 793 // correctly. |
| 791 if (prevRange && prevRange.start.node && start) { | 794 if (prevRange && prevRange.start.node && start) { |
| 792 var entered = AutomationUtil.getUniqueAncestors( | 795 var entered = AutomationUtil.getUniqueAncestors( |
| 793 prevRange.start.node, start); | 796 prevRange.start.node, start); |
| 794 var embeddedObject = entered.find(function(f) { | 797 var embeddedObject = entered.find(function(f) { |
| 795 return f.role == RoleType.embeddedObject; }); | 798 return f.role == RoleType.EMBEDDED_OBJECT; }); |
| 796 if (embeddedObject && !embeddedObject.state.focused) | 799 if (embeddedObject && !embeddedObject.state[StateType.FOCUSED]) |
| 797 embeddedObject.focus(); | 800 embeddedObject.focus(); |
| 798 } | 801 } |
| 799 | 802 |
| 800 if (start.state.focused || end.state.focused) | 803 if (start.state[StateType.FOCUSED] || end.state[StateType.FOCUSED]) |
| 801 return; | 804 return; |
| 802 | 805 |
| 803 var isFocusableLinkOrControl = function(node) { | 806 var isFocusableLinkOrControl = function(node) { |
| 804 return node.state.focusable && | 807 return node.state[StateType.FOCUSABLE] && |
| 805 AutomationPredicate.linkOrControl(node); | 808 AutomationPredicate.linkOrControl(node); |
| 806 }; | 809 }; |
| 807 | 810 |
| 808 // Next, try to focus the start or end node. | 811 // Next, try to focus the start or end node. |
| 809 if (!AutomationPredicate.structuralContainer(start) && | 812 if (!AutomationPredicate.structuralContainer(start) && |
| 810 start.state.focusable) { | 813 start.state[StateType.FOCUSABLE]) { |
| 811 if (!start.state.focused) | 814 if (!start.state[StateType.FOCUSED]) |
| 812 start.focus(); | 815 start.focus(); |
| 813 return; | 816 return; |
| 814 } else if (!AutomationPredicate.structuralContainer(end) && | 817 } else if (!AutomationPredicate.structuralContainer(end) && |
| 815 end.state.focusable) { | 818 end.state[StateType.FOCUSABLE]) { |
| 816 if (!end.state.focused) | 819 if (!end.state[StateType.FOCUSED]) |
| 817 end.focus(); | 820 end.focus(); |
| 818 return; | 821 return; |
| 819 } | 822 } |
| 820 | 823 |
| 821 // If a common ancestor of |start| and |end| is a link, focus that. | 824 // If a common ancestor of |start| and |end| is a link, focus that. |
| 822 var ancestor = AutomationUtil.getLeastCommonAncestor(start, end); | 825 var ancestor = AutomationUtil.getLeastCommonAncestor(start, end); |
| 823 while (ancestor && ancestor.root == start.root) { | 826 while (ancestor && ancestor.root == start.root) { |
| 824 if (isFocusableLinkOrControl(ancestor)) { | 827 if (isFocusableLinkOrControl(ancestor)) { |
| 825 if (!ancestor.state.focused) | 828 if (!ancestor.state[StateType.FOCUSED]) |
| 826 ancestor.focus(); | 829 ancestor.focus(); |
| 827 return; | 830 return; |
| 828 } | 831 } |
| 829 ancestor = ancestor.parent; | 832 ancestor = ancestor.parent; |
| 830 } | 833 } |
| 831 | 834 |
| 832 // If nothing is focusable, set the sequential focus navigation starting | 835 // If nothing is focusable, set the sequential focus navigation starting |
| 833 // point, which ensures that the next time you press Tab, you'll reach | 836 // point, which ensures that the next time you press Tab, you'll reach |
| 834 // the next or previous focusable node from |start|. | 837 // the next or previous focusable node from |start|. |
| 835 if (!start.state.offscreen) | 838 if (!start.state[StateType.OFFSCREEN]) |
| 836 start.setSequentialFocusNavigationStartingPoint(); | 839 start.setSequentialFocusNavigationStartingPoint(); |
| 837 } | 840 } |
| 838 }; | 841 }; |
| 839 | 842 |
| 840 /** | 843 /** |
| 841 * Converts a list of globs, as used in the extension manifest, to a regular | 844 * Converts a list of globs, as used in the extension manifest, to a regular |
| 842 * expression that matches if and only if any of the globs in the list matches. | 845 * expression that matches if and only if any of the globs in the list matches. |
| 843 * @param {!Array<string>} globs | 846 * @param {!Array<string>} globs |
| 844 * @return {!RegExp} | 847 * @return {!RegExp} |
| 845 * @private | 848 * @private |
| 846 */ | 849 */ |
| 847 Background.globsToRegExp_ = function(globs) { | 850 Background.globsToRegExp_ = function(globs) { |
| 848 return new RegExp('^(' + globs.map(function(glob) { | 851 return new RegExp('^(' + globs.map(function(glob) { |
| 849 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') | 852 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') |
| 850 .replace(/\*/g, '.*') | 853 .replace(/\*/g, '.*') |
| 851 .replace(/\?/g, '.'); | 854 .replace(/\?/g, '.'); |
| 852 }).join('|') + ')$'); | 855 }).join('|') + ')$'); |
| 853 }; | 856 }; |
| 854 | 857 |
| 855 new Background(); | 858 new Background(); |
| 856 | 859 |
| 857 }); // goog.scope | 860 }); // goog.scope |
| OLD | NEW |