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 |