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 |