| 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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 66 | 66 |
| 67 /** | 67 /** |
| 68 * Regular expression for blacklisting classic. | 68 * Regular expression for blacklisting classic. |
| 69 * @type {RegExp} | 69 * @type {RegExp} |
| 70 * @private | 70 * @private |
| 71 */ | 71 */ |
| 72 this.classicBlacklistRegExp_ = Background.globsToRegExp_( | 72 this.classicBlacklistRegExp_ = Background.globsToRegExp_( |
| 73 chrome.runtime.getManifest()['content_scripts'][0]['exclude_globs']); | 73 chrome.runtime.getManifest()['content_scripts'][0]['exclude_globs']); |
| 74 | 74 |
| 75 /** | 75 /** |
| 76 * Regular expression for whitelisting Next compat. | |
| 77 * @type {RegExp} | |
| 78 * @private | |
| 79 */ | |
| 80 this.nextCompatRegExp_ = Background.globsToRegExp_([ | |
| 81 '*docs.google.com/document/*', | |
| 82 '*docs.google.com/spreadsheets/*', | |
| 83 '*docs.google.com/presentation/*' | |
| 84 ]); | |
| 85 | |
| 86 /** | |
| 87 * @type {cursors.Range} | 76 * @type {cursors.Range} |
| 88 * @private | 77 * @private |
| 89 */ | 78 */ |
| 90 this.currentRange_ = null; | 79 this.currentRange_ = null; |
| 91 | 80 |
| 92 // Manually bind all functions to |this|. | 81 // Manually bind all functions to |this|. |
| 93 for (var func in this) { | 82 for (var func in this) { |
| 94 if (typeof(this[func]) == 'function') | 83 if (typeof(this[func]) == 'function') |
| 95 this[func] = this[func].bind(this); | 84 this[func] = this[func].bind(this); |
| 96 } | 85 } |
| 97 | 86 |
| 98 /** @type {!cvox.AbstractEarcons} @private */ | 87 /** @type {!cvox.AbstractEarcons} @private */ |
| 99 this.classicEarcons_ = cvox.ChromeVox.earcons || new cvox.ClassicEarcons(); | 88 this.classicEarcons_ = cvox.ChromeVox.earcons || new cvox.ClassicEarcons(); |
| 100 | 89 |
| 101 /** @type {!cvox.AbstractEarcons} @private */ | 90 /** @type {!cvox.AbstractEarcons} @private */ |
| 102 this.nextEarcons_ = new NextEarcons(); | 91 this.nextEarcons_ = new NextEarcons(); |
| 103 | 92 |
| 104 // Turn cvox.ChromeVox.earcons into a getter that returns either the | 93 // Turn cvox.ChromeVox.earcons into a getter that returns either the |
| 105 // Next earcons or the Classic earcons depending on the current mode. | 94 // Next earcons or the Classic earcons depending on the current mode. |
| 106 Object.defineProperty(cvox.ChromeVox, 'earcons', { | 95 Object.defineProperty(cvox.ChromeVox, 'earcons', { |
| 107 get: (function() { | 96 get: (function() { |
| 108 if (this.mode === ChromeVoxMode.FORCE_NEXT || | 97 if (this.mode === ChromeVoxMode.FORCE_NEXT || |
| 109 this.mode === ChromeVoxMode.NEXT || | 98 this.mode === ChromeVoxMode.NEXT) { |
| 110 this.mode === ChromeVoxMode.NEXT_COMPAT) { | |
| 111 return this.nextEarcons_; | 99 return this.nextEarcons_; |
| 112 } else { | 100 } else { |
| 113 return this.classicEarcons_; | 101 return this.classicEarcons_; |
| 114 } | 102 } |
| 115 }).bind(this) | 103 }).bind(this) |
| 116 }); | 104 }); |
| 117 | 105 |
| 118 if (cvox.ChromeVox.isChromeOS) { | 106 if (cvox.ChromeVox.isChromeOS) { |
| 119 Object.defineProperty(cvox.ChromeVox, 'modKeyStr', { | 107 Object.defineProperty(cvox.ChromeVox, 'modKeyStr', { |
| 120 get: function() { | 108 get: function() { |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 248 | 236 |
| 249 // Closure complains, but clearly, |target| is not null. | 237 // Closure complains, but clearly, |target| is not null. |
| 250 var topLevelRoot = | 238 var topLevelRoot = |
| 251 AutomationUtil.getTopLevelRoot(/** @type {!AutomationNode} */(target)); | 239 AutomationUtil.getTopLevelRoot(/** @type {!AutomationNode} */(target)); |
| 252 if (!topLevelRoot) | 240 if (!topLevelRoot) |
| 253 return useNext ? ChromeVoxMode.FORCE_NEXT : | 241 return useNext ? ChromeVoxMode.FORCE_NEXT : |
| 254 ChromeVoxMode.CLASSIC_COMPAT; | 242 ChromeVoxMode.CLASSIC_COMPAT; |
| 255 | 243 |
| 256 var docUrl = topLevelRoot.docUrl || ''; | 244 var docUrl = topLevelRoot.docUrl || ''; |
| 257 var nextSite = this.isWhitelistedForNext_(docUrl); | 245 var nextSite = this.isWhitelistedForNext_(docUrl); |
| 258 var nextCompat = this.nextCompatRegExp_.test(docUrl) && | |
| 259 this.chromeChannel_ != 'dev'; | |
| 260 var classicCompat = | 246 var classicCompat = |
| 261 this.isWhitelistedForClassicCompat_(docUrl); | 247 this.isWhitelistedForClassicCompat_(docUrl); |
| 262 if (nextCompat && useNext) | 248 if (classicCompat && !useNext) |
| 263 return ChromeVoxMode.NEXT_COMPAT; | |
| 264 else if (classicCompat && !useNext) | |
| 265 return ChromeVoxMode.CLASSIC_COMPAT; | 249 return ChromeVoxMode.CLASSIC_COMPAT; |
| 266 else if (nextSite) | 250 else if (nextSite) |
| 267 return ChromeVoxMode.NEXT; | 251 return ChromeVoxMode.NEXT; |
| 268 else if (!useNext) | 252 else if (!useNext) |
| 269 return ChromeVoxMode.CLASSIC; | 253 return ChromeVoxMode.CLASSIC; |
| 270 else | 254 else |
| 271 return ChromeVoxMode.FORCE_NEXT; | 255 return ChromeVoxMode.FORCE_NEXT; |
| 272 }, | 256 }, |
| 273 | 257 |
| 274 /** | 258 /** |
| 275 * Handles a mode change. | 259 * Handles a mode change. |
| 276 * @param {ChromeVoxMode} newMode | 260 * @param {ChromeVoxMode} newMode |
| 277 * @param {?ChromeVoxMode} oldMode Can be null at startup when no range was | 261 * @param {?ChromeVoxMode} oldMode Can be null at startup when no range was |
| 278 * previously set. | 262 * previously set. |
| 279 * @private | 263 * @private |
| 280 */ | 264 */ |
| 281 onModeChanged_: function(newMode, oldMode) { | 265 onModeChanged_: function(newMode, oldMode) { |
| 282 this.keyboardHandler_.onModeChanged(newMode, oldMode); | 266 this.keyboardHandler_.onModeChanged(newMode, oldMode); |
| 283 CommandHandler.onModeChanged(newMode, oldMode); | 267 CommandHandler.onModeChanged(newMode, oldMode); |
| 284 FindHandler.onModeChanged(newMode, oldMode); | 268 FindHandler.onModeChanged(newMode, oldMode); |
| 285 Notifications.onModeChange(newMode, oldMode); | 269 Notifications.onModeChange(newMode, oldMode); |
| 286 | 270 |
| 287 // The below logic handles transition between the classic engine | 271 // The below logic handles transition between the classic engine |
| 288 // (content script) and next engine (no content script) as well as | 272 // (content script) and next engine (no content script) as well as |
| 289 // misc states that are not handled above. | 273 // misc states that are not handled above. |
| 290 | 274 |
| 291 // Classic modes do not use the new focus highlight. | 275 // Classic modes do not use the new focus highlight. |
| 292 if (newMode == ChromeVoxMode.CLASSIC || | 276 if (newMode == ChromeVoxMode.CLASSIC) |
| 293 newMode == ChromeVoxMode.NEXT_COMPAT) | |
| 294 chrome.accessibilityPrivate.setFocusRing([]); | 277 chrome.accessibilityPrivate.setFocusRing([]); |
| 295 | 278 |
| 296 // Switch on/off content scripts. | 279 // Switch on/off content scripts. |
| 297 // note that |this.currentRange_| can *change* because the request is | 280 // note that |this.currentRange_| can *change* because the request is |
| 298 // async. Save it to ensure we're looking at the currentRange at this moment | 281 // async. Save it to ensure we're looking at the currentRange at this moment |
| 299 // in time. | 282 // in time. |
| 300 var cur = this.currentRange_; | 283 var cur = this.currentRange_; |
| 301 chrome.tabs.query({active: true, | 284 chrome.tabs.query({active: true, |
| 302 lastFocusedWindow: true}, function(tabs) { | 285 lastFocusedWindow: true}, function(tabs) { |
| 303 if (newMode == ChromeVoxMode.CLASSIC) { | 286 if (newMode == ChromeVoxMode.CLASSIC) { |
| 304 // Generally, we don't want to inject classic content scripts as it is | 287 // Generally, we don't want to inject classic content scripts as it is |
| 305 // done by the extension system at document load. The exception is when | 288 // done by the extension system at document load. The exception is when |
| 306 // we toggle classic on manually as part of a user command. | 289 // we toggle classic on manually as part of a user command. |
| 307 // Note that classic -> next_compat is ignored here because classic | |
| 308 // should have already enabled content scripts. | |
| 309 if (oldMode == ChromeVoxMode.FORCE_NEXT) { | 290 if (oldMode == ChromeVoxMode.FORCE_NEXT) { |
| 310 cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); | 291 cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); |
| 311 } | 292 } |
| 312 } else if (newMode === ChromeVoxMode.FORCE_NEXT) { | 293 } else if (newMode === ChromeVoxMode.FORCE_NEXT) { |
| 313 // Disable ChromeVox everywhere except for things whitelisted | 294 this.disableClassicChromeVox_(); |
| 314 // for next compat. | 295 } else { |
| 315 this.disableClassicChromeVox_({forNextCompat: true}); | |
| 316 } else if (newMode != ChromeVoxMode.NEXT_COMPAT) { | |
| 317 // If we're focused in the desktop tree, do nothing. | 296 // If we're focused in the desktop tree, do nothing. |
| 318 if (cur && !cur.isWebRange()) | 297 if (cur && !cur.isWebRange()) |
| 319 return; | 298 return; |
| 320 | 299 |
| 321 // If we're entering classic compat mode or next mode for just one tab, | 300 // If we're entering classic compat mode or next mode for just one tab, |
| 322 // disable Classic for that tab only. | 301 // disable Classic for that tab only. |
| 323 this.disableClassicChromeVox_({tabs: tabs}); | 302 this.disableClassicChromeVox_(tabs); |
| 324 } | 303 } |
| 325 }.bind(this)); | 304 }.bind(this)); |
| 326 | 305 |
| 327 // Switching into either compat or classic. | 306 // Switching into either compat or classic. |
| 328 if (oldMode === ChromeVoxMode.NEXT || | 307 if (oldMode === ChromeVoxMode.NEXT || |
| 329 oldMode === ChromeVoxMode.FORCE_NEXT) { | 308 oldMode === ChromeVoxMode.FORCE_NEXT) { |
| 330 // Make sure we cancel the progress loading sound just in case. | 309 // Make sure we cancel the progress loading sound just in case. |
| 331 cvox.ChromeVox.earcons.cancelEarcon(cvox.Earcon.PAGE_START_LOADING); | 310 cvox.ChromeVox.earcons.cancelEarcon(cvox.Earcon.PAGE_START_LOADING); |
| 332 (new PanelCommand(PanelCommandType.DISABLE_MENUS)).send(); | 311 (new PanelCommand(PanelCommandType.DISABLE_MENUS)).send(); |
| 333 } | 312 } |
| (...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 574 } | 553 } |
| 575 return true; | 554 return true; |
| 576 }, | 555 }, |
| 577 | 556 |
| 578 /** | 557 /** |
| 579 * Returns true if the url should have Classic running. | 558 * Returns true if the url should have Classic running. |
| 580 * @return {boolean} | 559 * @return {boolean} |
| 581 * @private | 560 * @private |
| 582 */ | 561 */ |
| 583 shouldEnableClassicForUrl_: function(url) { | 562 shouldEnableClassicForUrl_: function(url) { |
| 584 return (this.nextCompatRegExp_.test(url) &&this.chromeChannel_ != 'dev') || | 563 return this.mode != ChromeVoxMode.FORCE_NEXT && |
| 585 (this.mode != ChromeVoxMode.FORCE_NEXT && | |
| 586 !this.isBlacklistedForClassic_(url) && | 564 !this.isBlacklistedForClassic_(url) && |
| 587 !this.isWhitelistedForNext_(url)); | 565 !this.isWhitelistedForNext_(url); |
| 588 }, | 566 }, |
| 589 | 567 |
| 590 /** | 568 /** |
| 591 * Compat mode is on if any of the following are true: | 569 * Compat mode is on if any of the following are true: |
| 592 * 1. a url is blacklisted for Classic. | 570 * 1. a url is blacklisted for Classic. |
| 593 * 2. the current range is not within web content. | 571 * 2. the current range is not within web content. |
| 594 * @param {string} url | 572 * @param {string} url |
| 595 * @return {boolean} | 573 * @return {boolean} |
| 596 */ | 574 */ |
| 597 isWhitelistedForClassicCompat_: function(url) { | 575 isWhitelistedForClassicCompat_: function(url) { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 618 * @private | 596 * @private |
| 619 */ | 597 */ |
| 620 isWhitelistedForNext_: function(url) { | 598 isWhitelistedForNext_: function(url) { |
| 621 return this.whitelist_.some(function(item) { | 599 return this.whitelist_.some(function(item) { |
| 622 return url.indexOf(item) != -1; | 600 return url.indexOf(item) != -1; |
| 623 }); | 601 }); |
| 624 }, | 602 }, |
| 625 | 603 |
| 626 /** | 604 /** |
| 627 * Disables classic ChromeVox in current web content. | 605 * Disables classic ChromeVox in current web content. |
| 628 * @param {{tabs: (Array<Tab>|undefined), | 606 * @param {Array<Tab>=} opt_tabs The tabs where ChromeVox scripts should be |
| 629 * forNextCompat: (boolean|undefined)}} params | 607 * disabled. If null, will disable ChromeVox everywhere. |
| 630 * tabs: The tabs where ChromeVox scripts should be disabled. If null, will | |
| 631 * disable ChromeVox everywhere. | |
| 632 * forNextCompat: filters out tabs that have been listed for next compat (i.e. | |
| 633 * should retain content script). | |
| 634 */ | 608 */ |
| 635 disableClassicChromeVox_: function(params) { | 609 disableClassicChromeVox_: function(opt_tabs) { |
| 636 var disableChromeVoxCommand = { | 610 var disableChromeVoxCommand = { |
| 637 message: 'SYSTEM_COMMAND', | 611 message: 'SYSTEM_COMMAND', |
| 638 command: 'killChromeVox' | 612 command: 'killChromeVox' |
| 639 }; | 613 }; |
| 640 | 614 |
| 641 if (params.forNextCompat) { | 615 if (opt_tabs) { |
| 642 var reStr = this.nextCompatRegExp_.toString(); | 616 for (var i = 0, tab; tab = opt_tabs[i]; i++) |
| 643 disableChromeVoxCommand['excludeUrlRegExp'] = | |
| 644 reStr.substring(1, reStr.length - 1); | |
| 645 } | |
| 646 | |
| 647 if (params.tabs) { | |
| 648 for (var i = 0, tab; tab = params.tabs[i]; i++) | |
| 649 chrome.tabs.sendMessage(tab.id, disableChromeVoxCommand); | 617 chrome.tabs.sendMessage(tab.id, disableChromeVoxCommand); |
| 650 } else { | 618 } else { |
| 651 // Send to all ChromeVox clients. | 619 // Send to all ChromeVox clients. |
| 652 cvox.ExtensionBridge.send(disableChromeVoxCommand); | 620 cvox.ExtensionBridge.send(disableChromeVoxCommand); |
| 653 } | 621 } |
| 654 }, | 622 }, |
| 655 | 623 |
| 656 /** | 624 /** |
| 657 * @param {!Spannable} text | 625 * @param {!Spannable} text |
| 658 * @param {number} position | 626 * @param {number} position |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 850 return new RegExp('^(' + globs.map(function(glob) { | 818 return new RegExp('^(' + globs.map(function(glob) { |
| 851 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') | 819 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') |
| 852 .replace(/\*/g, '.*') | 820 .replace(/\*/g, '.*') |
| 853 .replace(/\?/g, '.'); | 821 .replace(/\?/g, '.'); |
| 854 }).join('|') + ')$'); | 822 }).join('|') + ')$'); |
| 855 }; | 823 }; |
| 856 | 824 |
| 857 new Background(); | 825 new Background(); |
| 858 | 826 |
| 859 }); // goog.scope | 827 }); // goog.scope |
| OLD | NEW |