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