| 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'); |
| 11 goog.provide('global'); | 11 goog.provide('global'); |
| 12 | 12 |
| 13 goog.require('AutomationPredicate'); | 13 goog.require('AutomationPredicate'); |
| 14 goog.require('AutomationUtil'); | 14 goog.require('AutomationUtil'); |
| 15 goog.require('Output'); | 15 goog.require('Output'); |
| 16 goog.require('Output.EventType'); | 16 goog.require('Output.EventType'); |
| 17 goog.require('cursors.Cursor'); | 17 goog.require('cursors.Cursor'); |
| 18 goog.require('cvox.ChromeVoxEditableTextBase'); | 18 goog.require('cvox.ChromeVoxEditableTextBase'); |
| 19 goog.require('cvox.TabsApiHandler'); | |
| 20 | 19 |
| 21 goog.scope(function() { | 20 goog.scope(function() { |
| 22 var AutomationNode = chrome.automation.AutomationNode; | 21 var AutomationNode = chrome.automation.AutomationNode; |
| 23 var Dir = AutomationUtil.Dir; | 22 var Dir = AutomationUtil.Dir; |
| 24 var EventType = chrome.automation.EventType; | 23 var EventType = chrome.automation.EventType; |
| 25 | 24 |
| 26 /** | 25 /** |
| 27 * ChromeVox2 background page. | 26 * ChromeVox2 background page. |
| 28 * @constructor | 27 * @constructor |
| 29 */ | 28 */ |
| 30 Background = function() { | 29 Background = function() { |
| 31 /** | 30 /** |
| 32 * A list of site substring patterns to use with ChromeVox next. Keep these | 31 * A list of site substring patterns to use with ChromeVox next. Keep these |
| 33 * strings relatively specific. | 32 * strings relatively specific. |
| 34 * @type {!Array.<string>} | 33 * @type {!Array.<string>} |
| 35 * @private | 34 * @private |
| 36 */ | 35 */ |
| 37 this.whitelist_ = ['chromevox_next_test']; | 36 this.whitelist_ = ['chromevox_next_test']; |
| 38 | 37 |
| 39 /** | 38 /** |
| 40 * @type {cvox.TabsApiHandler} | |
| 41 * @private | |
| 42 */ | |
| 43 this.tabsHandler_ = new cvox.TabsApiHandler(cvox.ChromeVox.tts, | |
| 44 cvox.ChromeVox.braille, | |
| 45 cvox.ChromeVox.earcons); | |
| 46 | |
| 47 /** | |
| 48 * @type {cursors.Range} | 39 * @type {cursors.Range} |
| 49 * @private | 40 * @private |
| 50 */ | 41 */ |
| 51 this.currentRange_ = null; | 42 this.currentRange_ = null; |
| 52 | 43 |
| 53 /** | 44 /** |
| 54 * Whether ChromeVox Next is active. | 45 * Whether ChromeVox Next is active. |
| 55 * @type {boolean} | 46 * @type {boolean} |
| 56 * @private | 47 * @private |
| 57 */ | 48 */ |
| (...skipping 17 matching lines...) Expand all Loading... |
| 75 menuEnd: this.onEventDefault, | 66 menuEnd: this.onEventDefault, |
| 76 menuListValueChanged: this.onEventDefault, | 67 menuListValueChanged: this.onEventDefault, |
| 77 loadComplete: this.onLoadComplete, | 68 loadComplete: this.onLoadComplete, |
| 78 textChanged: this.onTextOrTextSelectionChanged, | 69 textChanged: this.onTextOrTextSelectionChanged, |
| 79 textSelectionChanged: this.onTextOrTextSelectionChanged, | 70 textSelectionChanged: this.onTextOrTextSelectionChanged, |
| 80 valueChanged: this.onEventDefault | 71 valueChanged: this.onEventDefault |
| 81 }; | 72 }; |
| 82 | 73 |
| 83 // Register listeners for ... | 74 // Register listeners for ... |
| 84 // Desktop. | 75 // Desktop. |
| 85 chrome.automation.getDesktop(this.onGotTree); | 76 chrome.automation.getDesktop(this.onGotDesktop); |
| 86 | |
| 87 // Tabs. | |
| 88 chrome.tabs.onUpdated.addListener(this.onTabUpdated); | |
| 89 }; | 77 }; |
| 90 | 78 |
| 91 Background.prototype = { | 79 Background.prototype = { |
| 92 /** | 80 /** |
| 93 * Handles chrome.tabs.onUpdated. | 81 * Handles all setup once a new automation tree appears. |
| 94 * @param {number} tabId | 82 * @param {chrome.automation.AutomationNode} desktop |
| 95 * @param {Object} changeInfo | |
| 96 */ | 83 */ |
| 97 onTabUpdated: function(tabId, changeInfo) { | 84 onGotDesktop: function(desktop) { |
| 98 if (changeInfo.status != 'complete') | |
| 99 return; | |
| 100 chrome.tabs.get(tabId, function(tab) { | |
| 101 if (!tab.url) | |
| 102 return; | |
| 103 | |
| 104 var next = this.isWhitelisted_(tab.url); | |
| 105 | |
| 106 this.toggleChromeVoxVersion({next: next, classic: !next}); | |
| 107 }.bind(this)); | |
| 108 }, | |
| 109 | |
| 110 /** | |
| 111 * Handles all setup once a new automation tree appears. | |
| 112 * @param {chrome.automation.AutomationNode} root | |
| 113 */ | |
| 114 onGotTree: function(root) { | |
| 115 // Register all automation event listeners. | 85 // Register all automation event listeners. |
| 116 for (var eventType in this.listeners_) | 86 for (var eventType in this.listeners_) |
| 117 root.addEventListener(eventType, this.listeners_[eventType], true); | 87 desktop.addEventListener(eventType, this.listeners_[eventType], true); |
| 118 | 88 |
| 119 if (root.attributes.docLoaded) { | 89 // The focused state gets set on the containing webView node. |
| 120 this.onLoadComplete( | 90 var webView = desktop.find({role: chrome.automation.RoleType.webView, |
| 121 {target: root, type: chrome.automation.EventType.loadComplete}); | 91 state: {focused: true}}); |
| 92 if (webView) { |
| 93 var root = webView.find({role: chrome.automation.RoleType.rootWebArea}); |
| 94 if (root) { |
| 95 this.onLoadComplete( |
| 96 {target: root, |
| 97 type: chrome.automation.EventType.loadComplete}); |
| 98 } |
| 122 } | 99 } |
| 123 }, | 100 }, |
| 124 | 101 |
| 125 /** | 102 /** |
| 126 * Handles chrome.commands.onCommand. | 103 * Handles chrome.commands.onCommand. |
| 127 * @param {string} command | 104 * @param {string} command |
| 128 */ | 105 */ |
| 129 onGotCommand: function(command) { | 106 onGotCommand: function(command) { |
| 130 if (command == 'toggleChromeVoxVersion') { | 107 if (command == 'toggleChromeVoxVersion') { |
| 131 this.toggleChromeVoxVersion(); | 108 this.toggleChromeVoxVersion(); |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 263 new Output().withSpeechAndBraille( | 240 new Output().withSpeechAndBraille( |
| 264 this.currentRange_, prevRange, evt.type) | 241 this.currentRange_, prevRange, evt.type) |
| 265 .go(); | 242 .go(); |
| 266 }, | 243 }, |
| 267 | 244 |
| 268 /** | 245 /** |
| 269 * Provides all feedback once a load complete event fires. | 246 * Provides all feedback once a load complete event fires. |
| 270 * @param {Object} evt | 247 * @param {Object} evt |
| 271 */ | 248 */ |
| 272 onLoadComplete: function(evt) { | 249 onLoadComplete: function(evt) { |
| 250 var next = this.isWhitelisted_(evt.target.attributes.url); |
| 251 this.toggleChromeVoxVersion({next: next, classic: !next}); |
| 273 // Don't process nodes inside of web content if ChromeVox Next is inactive. | 252 // Don't process nodes inside of web content if ChromeVox Next is inactive. |
| 274 if (evt.target.root.role != chrome.automation.RoleType.desktop && | 253 if (evt.target.root.role != chrome.automation.RoleType.desktop && |
| 275 !this.active_) | 254 !this.active_) |
| 276 return; | 255 return; |
| 277 | 256 |
| 278 var node = AutomationUtil.findNodePost(evt.target, | 257 if (this.currentRange_) |
| 258 return; |
| 259 |
| 260 var root = evt.target; |
| 261 var webView = root; |
| 262 while (webView && webView.role != chrome.automation.RoleType.webView) |
| 263 webView = webView.parent; |
| 264 |
| 265 if (!webView || !webView.state.focused) |
| 266 return; |
| 267 |
| 268 var node = AutomationUtil.findNodePost(root, |
| 279 Dir.FORWARD, | 269 Dir.FORWARD, |
| 280 AutomationPredicate.leaf); | 270 AutomationPredicate.leaf); |
| 271 |
| 281 if (node) | 272 if (node) |
| 282 this.currentRange_ = cursors.Range.fromNode(node); | 273 this.currentRange_ = cursors.Range.fromNode(node); |
| 283 | 274 |
| 284 if (this.currentRange_) | 275 if (this.currentRange_) |
| 285 new Output().withSpeechAndBraille( | 276 new Output().withSpeechAndBraille( |
| 286 this.currentRange_, null, evt.type) | 277 this.currentRange_, null, evt.type) |
| 287 .go(); | 278 .go(); |
| 288 }, | 279 }, |
| 289 | 280 |
| 290 /** | 281 /** |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 355 */ | 346 */ |
| 356 toggleChromeVoxVersion: function(opt_options) { | 347 toggleChromeVoxVersion: function(opt_options) { |
| 357 if (!opt_options) { | 348 if (!opt_options) { |
| 358 opt_options = {}; | 349 opt_options = {}; |
| 359 opt_options.next = !this.active_; | 350 opt_options.next = !this.active_; |
| 360 opt_options.classic = !opt_options.next; | 351 opt_options.classic = !opt_options.next; |
| 361 } | 352 } |
| 362 | 353 |
| 363 if (opt_options.next) { | 354 if (opt_options.next) { |
| 364 if (!chrome.commands.onCommand.hasListener(this.onGotCommand)) | 355 if (!chrome.commands.onCommand.hasListener(this.onGotCommand)) |
| 365 chrome.commands.onCommand.addListener(this.onGotCommand); | 356 chrome.commands.onCommand.addListener(this.onGotCommand); |
| 366 | 357 this.active_ = true; |
| 367 if (!this.active_) | |
| 368 chrome.automation.getTree(this.onGotTree); | |
| 369 this.active_ = true; | |
| 370 } else { | 358 } else { |
| 371 if (chrome.commands.onCommand.hasListener(this.onGotCommand)) | 359 if (chrome.commands.onCommand.hasListener(this.onGotCommand)) |
| 372 chrome.commands.onCommand.removeListener(this.onGotCommand); | 360 chrome.commands.onCommand.removeListener(this.onGotCommand); |
| 373 | |
| 374 if (this.active_) { | |
| 375 for (var eventType in this.listeners_) { | |
| 376 this.currentRange_.getStart().getNode().root.removeEventListener( | |
| 377 eventType, this.listeners_[eventType], true); | |
| 378 } | |
| 379 } | |
| 380 this.active_ = false; | 361 this.active_ = false; |
| 381 } | 362 } |
| 382 | 363 |
| 383 chrome.tabs.query({active: true}, function(tabs) { | 364 chrome.tabs.query({active: true}, function(tabs) { |
| 384 if (opt_options.classic) { | 365 if (opt_options.classic) { |
| 385 // This case should do nothing because Classic gets injected by the | 366 // This case should do nothing because Classic gets injected by the |
| 386 // extension system via our manifest. Once ChromeVox Next is enabled | 367 // extension system via our manifest. Once ChromeVox Next is enabled |
| 387 // for tabs, re-enable. | 368 // for tabs, re-enable. |
| 388 // cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); | 369 // cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); |
| 389 } else { | 370 } else { |
| 390 tabs.forEach(function(tab) { | 371 tabs.forEach(function(tab) { |
| 391 this.disableClassicChromeVox_(tab.id); | 372 this.disableClassicChromeVox_(tab.id); |
| 392 }.bind(this)); | 373 }.bind(this)); |
| 393 } | 374 } |
| 394 }.bind(this)); | 375 }.bind(this)); |
| 395 } | 376 } |
| 396 }; | 377 }; |
| 397 | 378 |
| 398 /** @type {Background} */ | 379 /** @type {Background} */ |
| 399 global.backgroundObj = new Background(); | 380 global.backgroundObj = new Background(); |
| 400 | 381 |
| 401 }); // goog.scope | 382 }); // goog.scope |
| OLD | NEW |