| 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 | 12 |
| 12 goog.require('AutomationPredicate'); | 13 goog.require('AutomationPredicate'); |
| 13 goog.require('AutomationUtil'); | 14 goog.require('AutomationUtil'); |
| 14 goog.require('ChromeVoxState'); | 15 goog.require('ChromeVoxState'); |
| 15 goog.require('LiveRegions'); | 16 goog.require('LiveRegions'); |
| 16 goog.require('NextEarcons'); | 17 goog.require('NextEarcons'); |
| 17 goog.require('Notifications'); | 18 goog.require('Notifications'); |
| 18 goog.require('Output'); | 19 goog.require('Output'); |
| 19 goog.require('Output.EventType'); | 20 goog.require('Output.EventType'); |
| 20 goog.require('PanelCommand'); | 21 goog.require('PanelCommand'); |
| 21 goog.require('constants'); | 22 goog.require('constants'); |
| 22 goog.require('cursors.Cursor'); | 23 goog.require('cursors.Cursor'); |
| 23 goog.require('cvox.BrailleKeyCommand'); | 24 goog.require('cvox.BrailleKeyCommand'); |
| 24 goog.require('cvox.ChromeVoxBackground'); | |
| 25 goog.require('cvox.ChromeVoxEditableTextBase'); | 25 goog.require('cvox.ChromeVoxEditableTextBase'); |
| 26 goog.require('cvox.ChromeVoxKbHandler'); | 26 goog.require('cvox.ChromeVoxKbHandler'); |
| 27 goog.require('cvox.ClassicEarcons'); | 27 goog.require('cvox.ClassicEarcons'); |
| 28 goog.require('cvox.ExtensionBridge'); | 28 goog.require('cvox.ExtensionBridge'); |
| 29 goog.require('cvox.NavBraille'); | 29 goog.require('cvox.NavBraille'); |
| 30 | 30 |
| 31 goog.scope(function() { | 31 goog.scope(function() { |
| 32 var AutomationNode = chrome.automation.AutomationNode; | 32 var AutomationNode = chrome.automation.AutomationNode; |
| 33 var Dir = constants.Dir; | 33 var Dir = constants.Dir; |
| 34 var EventType = chrome.automation.EventType; | 34 var EventType = chrome.automation.EventType; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 * @private | 71 * @private |
| 72 */ | 72 */ |
| 73 this.currentRange_ = null; | 73 this.currentRange_ = null; |
| 74 | 74 |
| 75 /** | 75 /** |
| 76 * @type {cursors.Range} | 76 * @type {cursors.Range} |
| 77 * @private | 77 * @private |
| 78 */ | 78 */ |
| 79 this.savedRange_ = null; | 79 this.savedRange_ = null; |
| 80 | 80 |
| 81 /** |
| 82 * Which variant of ChromeVox is active. |
| 83 * @type {ChromeVoxMode} |
| 84 * @private |
| 85 */ |
| 86 this.mode_ = ChromeVoxMode.COMPAT; |
| 87 |
| 81 // Manually bind all functions to |this|. | 88 // Manually bind all functions to |this|. |
| 82 for (var func in this) { | 89 for (var func in this) { |
| 83 if (typeof(this[func]) == 'function') | 90 if (typeof(this[func]) == 'function') |
| 84 this[func] = this[func].bind(this); | 91 this[func] = this[func].bind(this); |
| 85 } | 92 } |
| 86 | 93 |
| 87 /** @type {!cvox.AbstractEarcons} @private */ | 94 /** @type {!cvox.AbstractEarcons} @private */ |
| 88 this.classicEarcons_ = cvox.ChromeVox.earcons || new cvox.ClassicEarcons(); | 95 this.classicEarcons_ = cvox.ChromeVox.earcons || new cvox.ClassicEarcons(); |
| 89 | 96 |
| 90 /** @type {!cvox.AbstractEarcons} @private */ | 97 /** @type {!cvox.AbstractEarcons} @private */ |
| 91 this.nextEarcons_ = new NextEarcons(); | 98 this.nextEarcons_ = new NextEarcons(); |
| 92 | 99 |
| 93 // Turn cvox.ChromeVox.earcons into a getter that returns either the | 100 // Turn cvox.ChromeVox.earcons into a getter that returns either the |
| 94 // Next earcons or the Classic earcons depending on the current mode. | 101 // Next earcons or the Classic earcons depending on the current mode. |
| 95 Object.defineProperty(cvox.ChromeVox, 'earcons', { | 102 Object.defineProperty(cvox.ChromeVox, 'earcons', { |
| 96 get: (function() { | 103 get: (function() { |
| 97 if (this.mode === ChromeVoxMode.FORCE_NEXT || | 104 if (this.mode_ === ChromeVoxMode.FORCE_NEXT || |
| 98 this.mode === ChromeVoxMode.NEXT) { | 105 this.mode_ === ChromeVoxMode.NEXT) { |
| 99 return this.nextEarcons_; | 106 return this.nextEarcons_; |
| 100 } else { | 107 } else { |
| 101 return this.classicEarcons_; | 108 return this.classicEarcons_; |
| 102 } | 109 } |
| 103 }).bind(this) | 110 }).bind(this) |
| 104 }); | 111 }); |
| 105 | 112 |
| 106 if (cvox.ChromeVox.isChromeOS) { | 113 if (cvox.ChromeVox.isChromeOS) { |
| 107 Object.defineProperty(cvox.ChromeVox, 'modKeyStr', { | 114 Object.defineProperty(cvox.ChromeVox, 'modKeyStr', { |
| 108 get: function() { | 115 get: function() { |
| 109 return (this.mode == ChromeVoxMode.CLASSIC || this.mode == | 116 return (this.mode_ == ChromeVoxMode.CLASSIC || this.mode_ == |
| 110 ChromeVoxMode.COMPAT) ? | 117 ChromeVoxMode.COMPAT) ? |
| 111 'Search+Shift' : 'Search'; | 118 'Search+Shift' : 'Search'; |
| 112 }.bind(this) | 119 }.bind(this) |
| 113 }); | 120 }); |
| 114 } | 121 } |
| 115 | 122 |
| 116 Object.defineProperty(cvox.ChromeVox, 'isActive', { | 123 Object.defineProperty(cvox.ChromeVox, 'isActive', { |
| 117 get: function() { | 124 get: function() { |
| 118 return localStorage['active'] !== 'false'; | 125 return localStorage['active'] !== 'false'; |
| 119 }, | 126 }, |
| (...skipping 13 matching lines...) Expand all Loading... |
| 133 | 140 |
| 134 // Live region handler. | 141 // Live region handler. |
| 135 this.liveRegions_ = new LiveRegions(this); | 142 this.liveRegions_ = new LiveRegions(this); |
| 136 | 143 |
| 137 /** @type {number} @private */ | 144 /** @type {number} @private */ |
| 138 this.passThroughKeyUpCount_ = 0; | 145 this.passThroughKeyUpCount_ = 0; |
| 139 | 146 |
| 140 /** @type {boolean} @private */ | 147 /** @type {boolean} @private */ |
| 141 this.inExcursion_ = false; | 148 this.inExcursion_ = false; |
| 142 | 149 |
| 143 /** | |
| 144 * Stores the mode as computed the last time a current range was set. | |
| 145 * @type {?ChromeVoxMode} | |
| 146 */ | |
| 147 this.mode_ = null; | |
| 148 | |
| 149 if (!chrome.accessibilityPrivate.setKeyboardListener) | 150 if (!chrome.accessibilityPrivate.setKeyboardListener) |
| 150 chrome.accessibilityPrivate.setKeyboardListener = function() {}; | 151 chrome.accessibilityPrivate.setKeyboardListener = function() {}; |
| 151 | 152 |
| 152 if (cvox.ChromeVox.isChromeOS) { | 153 if (cvox.ChromeVox.isChromeOS) { |
| 153 chrome.accessibilityPrivate.onAccessibilityGesture.addListener( | 154 chrome.accessibilityPrivate.onAccessibilityGesture.addListener( |
| 154 this.onAccessibilityGesture_); | 155 this.onAccessibilityGesture_); |
| 155 | 156 |
| 156 Notifications.onStartup(); | 157 Notifications.onStartup(); |
| 157 }}; | 158 }}; |
| 158 | 159 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 196 'swipeDown2': 'readFromHere', | 197 'swipeDown2': 'readFromHere', |
| 197 }; | 198 }; |
| 198 | 199 |
| 199 Background.prototype = { | 200 Background.prototype = { |
| 200 __proto__: ChromeVoxState.prototype, | 201 __proto__: ChromeVoxState.prototype, |
| 201 | 202 |
| 202 /** | 203 /** |
| 203 * @override | 204 * @override |
| 204 */ | 205 */ |
| 205 getMode: function() { | 206 getMode: function() { |
| 206 if (localStorage['useNext'] == 'true') | 207 return this.mode_; |
| 207 return ChromeVoxMode.FORCE_NEXT; | |
| 208 | |
| 209 var target; | |
| 210 if (!this.getCurrentRange()) { | |
| 211 chrome.automation.getFocus(function(focus) { | |
| 212 target = focus; | |
| 213 }); | |
| 214 } else { | |
| 215 target = this.getCurrentRange().start.node; | |
| 216 } | |
| 217 | |
| 218 if (!target) | |
| 219 return ChromeVoxMode.CLASSIC; | |
| 220 | |
| 221 // Closure complains, but clearly, |target| is not null. | |
| 222 var root = | |
| 223 AutomationUtil.getTopLevelRoot(/** @type {!AutomationNode} */(target)); | |
| 224 if (!root) | |
| 225 return ChromeVoxMode.COMPAT; | |
| 226 if (this.isWhitelistedForCompat_(root.docUrl)) | |
| 227 return ChromeVoxMode.COMPAT; | |
| 228 else if (this.isWhitelistedForNext_(root.docUrl)) | |
| 229 return ChromeVoxMode.NEXT; | |
| 230 else | |
| 231 return ChromeVoxMode.CLASSIC; | |
| 232 }, | 208 }, |
| 233 | 209 |
| 234 /** | 210 /** |
| 235 * Handles a mode change. | 211 * @override |
| 236 * @param {ChromeVoxMode} newMode | |
| 237 * @param {?ChromeVoxMode} oldMode Can be null at startup when no range was | |
| 238 * previously set. | |
| 239 * @private | |
| 240 */ | 212 */ |
| 241 onModeChanged_: function(newMode, oldMode) { | 213 setMode: function(mode, opt_injectClassic) { |
| 242 // Switching key maps potentially affects the key codes that involve | 214 // Switching key maps potentially affects the key codes that involve |
| 243 // sequencing. Without resetting this list, potentially stale key codes | 215 // sequencing. Without resetting this list, potentially stale key codes |
| 244 // remain. The key codes themselves get pushed in | 216 // remain. The key codes themselves get pushed in |
| 245 // cvox.KeySequence.deserialize which gets called by cvox.KeyMap. | 217 // cvox.KeySequence.deserialize which gets called by cvox.KeyMap. |
| 246 cvox.ChromeVox.sequenceSwitchKeyCodes = []; | 218 cvox.ChromeVox.sequenceSwitchKeyCodes = []; |
| 219 if (mode === ChromeVoxMode.CLASSIC || mode === ChromeVoxMode.COMPAT) |
| 220 window['prefs'].switchToKeyMap('keymap_classic'); |
| 221 else |
| 222 window['prefs'].switchToKeyMap('keymap_next'); |
| 247 | 223 |
| 248 var selectedKeyMap = | 224 if (mode == ChromeVoxMode.CLASSIC) { |
| 249 newMode == ChromeVoxMode.CLASSIC || newMode == ChromeVoxMode.COMPAT ? | |
| 250 'keymap_classic' : 'keymap_next'; | |
| 251 window['prefs'].switchToKeyMap(selectedKeyMap); | |
| 252 | |
| 253 if (newMode == ChromeVoxMode.CLASSIC) { | |
| 254 if (chrome.commands && | 225 if (chrome.commands && |
| 255 chrome.commands.onCommand.hasListener(this.onGotCommand)) | 226 chrome.commands.onCommand.hasListener(this.onGotCommand)) |
| 256 chrome.commands.onCommand.removeListener(this.onGotCommand); | 227 chrome.commands.onCommand.removeListener(this.onGotCommand); |
| 257 chrome.accessibilityPrivate.setKeyboardListener(false, false); | 228 chrome.accessibilityPrivate.setKeyboardListener(false, false); |
| 258 | |
| 259 if (cvox.ChromeVox.isChromeOS) | |
| 260 chrome.accessibilityPrivate.setFocusRing([]); | |
| 261 } else { | 229 } else { |
| 262 if (chrome.commands && | 230 if (chrome.commands && |
| 263 !chrome.commands.onCommand.hasListener(this.onGotCommand)) | 231 !chrome.commands.onCommand.hasListener(this.onGotCommand)) |
| 264 chrome.commands.onCommand.addListener(this.onGotCommand); | 232 chrome.commands.onCommand.addListener(this.onGotCommand); |
| 265 chrome.accessibilityPrivate.setKeyboardListener( | 233 chrome.accessibilityPrivate.setKeyboardListener( |
| 266 true, cvox.ChromeVox.isStickyPrefOn); | 234 true, cvox.ChromeVox.isStickyPrefOn); |
| 267 } | 235 } |
| 268 | 236 |
| 269 // note that |this.currentRange_| can *change* because the request is | 237 // note that |this.currentRange_| can *change* because the request is |
| 270 // async. Save it to ensure we're looking at the currentRange at this moment | 238 // async. Save it to ensure we're looking at the currentRange at this moment |
| 271 // in time. | 239 // in time. |
| 272 var cur = this.currentRange_; | 240 var cur = this.currentRange_; |
| 273 chrome.tabs.query({active: true, | 241 chrome.tabs.query({active: true, |
| 274 lastFocusedWindow: true}, function(tabs) { | 242 lastFocusedWindow: true}, function(tabs) { |
| 275 if (newMode === ChromeVoxMode.CLASSIC) { | 243 if (mode === ChromeVoxMode.CLASSIC) { |
| 276 // Generally, we don't want to inject classic content scripts as it is | 244 // Generally, we don't want to inject classic content scripts as it is |
| 277 // done by the extension system at document load. The exception is when | 245 // done by the extension system at document load. The exception is when |
| 278 // we toggle classic on manually as part of a user command. | 246 // we toggle classic on manually as part of a user command. |
| 279 if (oldMode == ChromeVoxMode.FORCE_NEXT) | 247 if (opt_injectClassic) |
| 280 cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); | 248 cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); |
| 281 } else if (newMode === ChromeVoxMode.FORCE_NEXT) { | 249 } else if (mode === ChromeVoxMode.FORCE_NEXT) { |
| 282 // Disable ChromeVox everywhere. | 250 // Disable ChromeVox everywhere. |
| 283 this.disableClassicChromeVox_(); | 251 this.disableClassicChromeVox_(); |
| 284 } else { | 252 } else { |
| 285 // If we're focused in the desktop tree, do nothing. | 253 // If we're focused in the desktop tree, do nothing. |
| 286 if (cur && !cur.isWebRange()) | 254 if (cur && !cur.isWebRange()) |
| 287 return; | 255 return; |
| 288 | 256 |
| 289 // If we're entering compat mode or next mode for just one tab, | 257 // If we're entering compat mode or next mode for just one tab, |
| 290 // disable Classic for that tab only. | 258 // disable Classic for that tab only. |
| 291 this.disableClassicChromeVox_(tabs); | 259 this.disableClassicChromeVox_(tabs); |
| 292 } | 260 } |
| 293 }.bind(this)); | 261 }.bind(this)); |
| 294 | 262 |
| 295 // If switching out of a ChromeVox Next mode, make sure we cancel | 263 // If switching out of a ChromeVox Next mode, make sure we cancel |
| 296 // the progress loading sound just in case. | 264 // the progress loading sound just in case. |
| 297 if (oldMode === ChromeVoxMode.NEXT || | 265 if ((this.mode_ === ChromeVoxMode.NEXT || |
| 298 oldMode === ChromeVoxMode.FORCE_NEXT) | 266 this.mode_ === ChromeVoxMode.FORCE_NEXT) && |
| 267 this.mode_ != mode) { |
| 299 cvox.ChromeVox.earcons.cancelEarcon(cvox.Earcon.PAGE_START_LOADING); | 268 cvox.ChromeVox.earcons.cancelEarcon(cvox.Earcon.PAGE_START_LOADING); |
| 269 } |
| 300 | 270 |
| 301 if (newMode === ChromeVoxMode.NEXT || | 271 if (mode === ChromeVoxMode.NEXT || |
| 302 newMode === ChromeVoxMode.FORCE_NEXT) { | 272 mode === ChromeVoxMode.FORCE_NEXT) { |
| 303 (new PanelCommand(PanelCommandType.ENABLE_MENUS)).send(); | 273 (new PanelCommand(PanelCommandType.ENABLE_MENUS)).send(); |
| 304 if (cvox.TabsApiHandler) | 274 if (cvox.TabsApiHandler) |
| 305 cvox.TabsApiHandler.shouldOutputSpeechAndBraille = false; | 275 cvox.TabsApiHandler.shouldOutputSpeechAndBraille = false; |
| 306 } else { | 276 } else { |
| 307 (new PanelCommand(PanelCommandType.DISABLE_MENUS)).send(); | 277 (new PanelCommand(PanelCommandType.DISABLE_MENUS)).send(); |
| 308 if (cvox.TabsApiHandler) | 278 if (cvox.TabsApiHandler) |
| 309 cvox.TabsApiHandler.shouldOutputSpeechAndBraille = true; | 279 cvox.TabsApiHandler.shouldOutputSpeechAndBraille = true; |
| 310 } | 280 } |
| 281 |
| 282 // If switching to Classic from any automation-API-based mode, |
| 283 // clear the focus ring. |
| 284 if (mode === ChromeVoxMode.CLASSIC && mode != this.mode_) { |
| 285 if (cvox.ChromeVox.isChromeOS) |
| 286 chrome.accessibilityPrivate.setFocusRing([]); |
| 287 } |
| 288 |
| 289 // If switching away from Classic to any automation-API-based mode, |
| 290 // update the range based on what's focused. |
| 291 if (this.mode_ === ChromeVoxMode.CLASSIC && mode != this.mode_) { |
| 292 chrome.automation.getFocus((function(focus) { |
| 293 if (focus) |
| 294 this.setCurrentRange(cursors.Range.fromNode(focus)); |
| 295 }).bind(this)); |
| 296 } |
| 297 |
| 298 this.mode_ = mode; |
| 311 }, | 299 }, |
| 312 | 300 |
| 313 /** | 301 /** |
| 314 * Toggles between force next and classic/compat modes. | 302 * Mode refreshes takes into account both |url| and the current ChromeVox |
| 315 * This toggle automatically handles deciding between classic/compat based on | 303 * range. The latter gets used to decide if the user is or isn't in web |
| 316 * the start of the current range. | 304 * content. The focused state also needs to be set for this info to be |
| 317 * @param {boolean=} opt_setValue Directly set to force next (true) or | 305 * reliable. |
| 318 * classic/compat (false). | 306 * @override |
| 319 * @return {boolean} True to announce current position. | |
| 320 */ | 307 */ |
| 321 toggleNext: function(opt_setValue) { | 308 refreshMode: function(node) { |
| 322 var useNext; | 309 // Mode changes are based upon the top level web root. |
| 323 if (opt_setValue !== undefined) | 310 var root = node.root; |
| 324 useNext = opt_setValue; | 311 while (root && |
| 325 else | 312 root.parent && |
| 326 useNext = localStorage['useNext'] != 'true'; | 313 root.parent.root && |
| 314 root.parent.root.role != RoleType.desktop) { |
| 315 root = root.parent.root; |
| 316 } |
| 327 | 317 |
| 328 localStorage['useNext'] = useNext; | 318 var url = ''; |
| 329 if (useNext) | 319 if (root && root.role == RoleType.rootWebArea) |
| 330 this.setCurrentRangeToFocus_(); | 320 url = root.docUrl; |
| 331 else | |
| 332 this.setCurrentRange(null); | |
| 333 | 321 |
| 334 var announce = Msgs.getMsg(useNext ? | 322 var mode = this.mode_; |
| 335 'switch_to_next' : 'switch_to_classic'); | 323 if (mode != ChromeVoxMode.FORCE_NEXT) { |
| 336 cvox.ChromeVox.tts.speak( | 324 if (this.isWhitelistedForNext_(url)) { |
| 337 announce, cvox.QueueMode.FLUSH, {doNotInterrupt: true}); | 325 mode = ChromeVoxMode.NEXT; |
| 338 Notifications.onModeChange(); | 326 } else if (this.isBlacklistedForClassic_(url) || (this.currentRange_ && |
| 327 !this.currentRange_.isWebRange() && |
| 328 this.currentRange_.start.node.state.focused)) { |
| 329 mode = ChromeVoxMode.COMPAT; |
| 330 } else { |
| 331 mode = ChromeVoxMode.CLASSIC; |
| 332 } |
| 333 } |
| 339 | 334 |
| 340 // If the new mode is Classic, return false now so we don't announce | 335 this.setMode(mode); |
| 341 // anything more. | |
| 342 return useNext; | |
| 343 }, | 336 }, |
| 344 | 337 |
| 345 /** | 338 /** |
| 346 * @override | 339 * @override |
| 347 */ | 340 */ |
| 348 getCurrentRange: function() { | 341 getCurrentRange: function() { |
| 349 if (this.currentRange_ && this.currentRange_.isValid()) | 342 if (this.currentRange_ && this.currentRange_.isValid()) |
| 350 return this.currentRange_; | 343 return this.currentRange_; |
| 351 return null; | 344 return null; |
| 352 }, | 345 }, |
| 353 | 346 |
| 354 /** | 347 /** |
| 355 * @override | 348 * @override |
| 356 */ | 349 */ |
| 357 setCurrentRange: function(newRange) { | 350 setCurrentRange: function(newRange) { |
| 358 if (!this.inExcursion_ && newRange) | 351 if (!newRange) |
| 352 return; |
| 353 |
| 354 // Is the range invalid? |
| 355 if (newRange.start.node.role === undefined || |
| 356 newRange.end.node.role === undefined) { |
| 357 // Restore range to the focused location. |
| 358 chrome.automation.getFocus(function(f) { |
| 359 newRange = cursors.Range.fromNode(f); |
| 360 }); |
| 361 } |
| 362 |
| 363 if (!this.inExcursion_) |
| 359 this.savedRange_ = new cursors.Range(newRange.start, newRange.end); | 364 this.savedRange_ = new cursors.Range(newRange.start, newRange.end); |
| 360 | 365 |
| 361 this.currentRange_ = newRange; | 366 this.currentRange_ = newRange; |
| 362 var oldMode = this.mode_; | |
| 363 var newMode = this.getMode(); | |
| 364 if (oldMode != newMode) { | |
| 365 this.onModeChanged_(newMode, oldMode); | |
| 366 this.mode_ = newMode; | |
| 367 } | |
| 368 | 367 |
| 369 if (this.currentRange_) { | 368 if (this.currentRange_) { |
| 370 var start = this.currentRange_.start.node; | 369 var start = this.currentRange_.start.node; |
| 371 start.makeVisible(); | 370 start.makeVisible(); |
| 372 | 371 |
| 373 var root = start.root; | 372 var root = start.root; |
| 373 |
| 374 if (!root || root.role == RoleType.desktop) | 374 if (!root || root.role == RoleType.desktop) |
| 375 return; | 375 return; |
| 376 | 376 |
| 377 var position = {}; | 377 var position = {}; |
| 378 var loc = start.location; | 378 var loc = start.location; |
| 379 position.x = loc.left + loc.width / 2; | 379 position.x = loc.left + loc.width / 2; |
| 380 position.y = loc.top + loc.height / 2; | 380 position.y = loc.top + loc.height / 2; |
| 381 var url = root.docUrl; | 381 var url = root.docUrl; |
| 382 url = url.substring(0, url.indexOf('#')) || url; | 382 url = url.substring(0, url.indexOf('#')) || url; |
| 383 cvox.ChromeVox.position[url] = position; | 383 cvox.ChromeVox.position[url] = position; |
| 384 } | 384 } |
| 385 }, | 385 }, |
| 386 | 386 |
| 387 /** Forces ChromeVox Next to be active for all tabs. */ |
| 388 forceChromeVoxNextActive: function() { |
| 389 this.setMode(ChromeVoxMode.FORCE_NEXT); |
| 390 }, |
| 391 |
| 387 /** | 392 /** |
| 388 * Handles ChromeVox Next commands. | 393 * Handles ChromeVox Next commands. |
| 389 * @param {string} command | 394 * @param {string} command |
| 395 * @param {boolean=} opt_bypassModeCheck Always tries to execute the command |
| 396 * regardless of mode. |
| 390 * @return {boolean} True if the command should propagate. | 397 * @return {boolean} True if the command should propagate. |
| 391 */ | 398 */ |
| 392 onGotCommand: function(command) { | 399 onGotCommand: function(command, opt_bypassModeCheck) { |
| 393 // Check for loss of focus which results in us invalidating our current | 400 // Check for loss of focus which results in us invalidating our current |
| 394 // range. Note this call is synchronis. | 401 // range. Note this call is synchronis. |
| 395 chrome.automation.getFocus(function(focusedNode) { | 402 chrome.automation.getFocus(function(focusedNode) { |
| 396 if (this.currentRange_ && !this.currentRange_.isValid()) | 403 if (this.currentRange_ && !this.currentRange_.isValid()) |
| 397 this.currentRange_ = cursors.Range.fromNode(focusedNode); | 404 this.currentRange_ = cursors.Range.fromNode(focusedNode); |
| 398 if (!focusedNode) | 405 if (!focusedNode) |
| 399 this.currentRange_ = null; | 406 this.currentRange_ = null; |
| 400 }.bind(this)); | 407 }.bind(this)); |
| 401 | 408 |
| 402 // These commands don't require a current range and work in all modes. | |
| 403 switch (command) { | |
| 404 case 'toggleChromeVoxVersion': | |
| 405 if (!this.toggleNext()) | |
| 406 return false; | |
| 407 if (this.currentRange_) | |
| 408 this.navigateToRange(this.currentRange_); | |
| 409 break; | |
| 410 case 'showNextUpdatePage': | |
| 411 var nextUpdatePage = {url: 'cvox2/background/next_update.html'}; | |
| 412 chrome.tabs.create(nextUpdatePage); | |
| 413 return false; | |
| 414 default: | |
| 415 break; | |
| 416 } | |
| 417 | |
| 418 // Require a current range. | |
| 419 if (!this.currentRange_) | 409 if (!this.currentRange_) |
| 420 return true; | 410 return true; |
| 421 | 411 |
| 422 // Next/compat commands hereafter. | 412 if (this.mode_ == ChromeVoxMode.CLASSIC && !opt_bypassModeCheck) |
| 423 if (this.mode == ChromeVoxMode.CLASSIC) | |
| 424 return true; | 413 return true; |
| 425 | 414 |
| 426 var current = this.currentRange_; | 415 var current = this.currentRange_; |
| 427 var dir = Dir.FORWARD; | 416 var dir = Dir.FORWARD; |
| 428 var pred = null; | 417 var pred = null; |
| 429 var predErrorMsg = undefined; | 418 var predErrorMsg = undefined; |
| 430 var speechProps = {}; | 419 var speechProps = {}; |
| 431 switch (command) { | 420 switch (command) { |
| 432 case 'nextCharacter': | 421 case 'nextCharacter': |
| 433 speechProps['phoneticCharacters'] = true; | 422 speechProps['phoneticCharacters'] = true; |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 569 if (this.currentRange_) { | 558 if (this.currentRange_) { |
| 570 var actionNode = this.currentRange_.start.node; | 559 var actionNode = this.currentRange_.start.node; |
| 571 if (actionNode.role == RoleType.inlineTextBox) | 560 if (actionNode.role == RoleType.inlineTextBox) |
| 572 actionNode = actionNode.parent; | 561 actionNode = actionNode.parent; |
| 573 actionNode.doDefault(); | 562 actionNode.doDefault(); |
| 574 } | 563 } |
| 575 // Skip all other processing; if focus changes, we should get an event | 564 // Skip all other processing; if focus changes, we should get an event |
| 576 // for that. | 565 // for that. |
| 577 return false; | 566 return false; |
| 578 case 'readFromHere': | 567 case 'readFromHere': |
| 579 ChromeVoxState.isReadingContinuously = true; | 568 global.isReadingContinuously = true; |
| 580 var continueReading = function() { | 569 var continueReading = function() { |
| 581 if (!ChromeVoxState.isReadingContinuously || !this.currentRange_) | 570 if (!global.isReadingContinuously || !this.currentRange_) |
| 582 return; | 571 return; |
| 583 | 572 |
| 584 var prevRange = this.currentRange_; | 573 var prevRange = this.currentRange_; |
| 585 var newRange = | 574 var newRange = |
| 586 this.currentRange_.move(cursors.Unit.DOM_NODE, Dir.FORWARD); | 575 this.currentRange_.move(cursors.Unit.DOM_NODE, Dir.FORWARD); |
| 587 | 576 |
| 588 // Stop if we've wrapped back to the document. | 577 // Stop if we've wrapped back to the document. |
| 589 var maybeDoc = newRange.start.node; | 578 var maybeDoc = newRange.start.node; |
| 590 if (maybeDoc.role == RoleType.rootWebArea && | 579 if (maybeDoc.role == RoleType.rootWebArea && |
| 591 maybeDoc.parent.root.role == RoleType.desktop) { | 580 maybeDoc.parent.root.role == RoleType.desktop) { |
| 592 ChromeVoxState.isReadingContinuously = false; | 581 global.isReadingContinuously = false; |
| 593 return; | 582 return; |
| 594 } | 583 } |
| 595 | 584 |
| 596 this.setCurrentRange(newRange); | 585 this.setCurrentRange(newRange); |
| 597 | 586 |
| 598 new Output().withRichSpeechAndBraille( | 587 new Output().withRichSpeechAndBraille( |
| 599 this.currentRange_, prevRange, Output.EventType.NAVIGATE) | 588 this.currentRange_, prevRange, Output.EventType.NAVIGATE) |
| 600 .onSpeechEnd(continueReading) | 589 .onSpeechEnd(continueReading) |
| 601 .go(); | 590 .go(); |
| 602 }.bind(this); | 591 }.bind(this); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 623 if (cvox.ChromeVox.isChromeOS) | 612 if (cvox.ChromeVox.isChromeOS) |
| 624 return false; | 613 return false; |
| 625 | 614 |
| 626 cvox.ChromeVox.isActive = !cvox.ChromeVox.isActive; | 615 cvox.ChromeVox.isActive = !cvox.ChromeVox.isActive; |
| 627 if (!cvox.ChromeVox.isActive) { | 616 if (!cvox.ChromeVox.isActive) { |
| 628 var msg = Msgs.getMsg('chromevox_inactive'); | 617 var msg = Msgs.getMsg('chromevox_inactive'); |
| 629 cvox.ChromeVox.tts.speak(msg, cvox.QueueMode.FLUSH); | 618 cvox.ChromeVox.tts.speak(msg, cvox.QueueMode.FLUSH); |
| 630 return false; | 619 return false; |
| 631 } | 620 } |
| 632 break; | 621 break; |
| 622 case 'toggleChromeVoxVersion': |
| 623 var newMode; |
| 624 if (this.mode_ == ChromeVoxMode.FORCE_NEXT) { |
| 625 var inWeb = current.isWebRange(); |
| 626 newMode = inWeb ? ChromeVoxMode.CLASSIC : ChromeVoxMode.COMPAT; |
| 627 } else { |
| 628 newMode = ChromeVoxMode.FORCE_NEXT; |
| 629 } |
| 630 this.setMode(newMode, true); |
| 631 |
| 632 var isClassic = |
| 633 newMode == ChromeVoxMode.CLASSIC || newMode == ChromeVoxMode.COMPAT; |
| 634 |
| 635 // Leaving unlocalized as 'next' isn't an official name. |
| 636 cvox.ChromeVox.tts.speak(isClassic ? |
| 637 'classic' : 'next', cvox.QueueMode.FLUSH, {doNotInterrupt: true}); |
| 638 |
| 639 // If the new mode is Classic, return now so we don't announce |
| 640 // anything more. |
| 641 if (newMode == ChromeVoxMode.CLASSIC) |
| 642 return false; |
| 643 break; |
| 633 case 'toggleStickyMode': | 644 case 'toggleStickyMode': |
| 634 cvox.ChromeVoxBackground.setPref('sticky', | 645 cvox.ChromeVoxBackground.setPref('sticky', |
| 635 !cvox.ChromeVox.isStickyPrefOn, | 646 !cvox.ChromeVox.isStickyPrefOn, |
| 636 true); | 647 true); |
| 637 | 648 |
| 638 if (cvox.ChromeVox.isStickyPrefOn) | 649 if (cvox.ChromeVox.isStickyPrefOn) |
| 639 chrome.accessibilityPrivate.setKeyboardListener(true, true); | 650 chrome.accessibilityPrivate.setKeyboardListener(true, true); |
| 640 else | 651 else |
| 641 chrome.accessibilityPrivate.setKeyboardListener(true, false); | 652 chrome.accessibilityPrivate.setKeyboardListener(true, false); |
| 642 return false; | 653 return false; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 670 (new PanelCommand(PanelCommandType.OPEN_MENUS, 'table_strategy')) | 681 (new PanelCommand(PanelCommandType.OPEN_MENUS, 'table_strategy')) |
| 671 .send(); | 682 .send(); |
| 672 return false; | 683 return false; |
| 673 case 'toggleSearchWidget': | 684 case 'toggleSearchWidget': |
| 674 (new PanelCommand(PanelCommandType.SEARCH)).send(); | 685 (new PanelCommand(PanelCommandType.SEARCH)).send(); |
| 675 return false; | 686 return false; |
| 676 case 'showKbExplorerPage': | 687 case 'showKbExplorerPage': |
| 677 var explorerPage = {url: 'chromevox/background/kbexplorer.html'}; | 688 var explorerPage = {url: 'chromevox/background/kbexplorer.html'}; |
| 678 chrome.tabs.create(explorerPage); | 689 chrome.tabs.create(explorerPage); |
| 679 break; | 690 break; |
| 691 case 'showNextUpdatePage': |
| 692 var nextUpdatePage = {url: 'cvox2/background/next_update.html'}; |
| 693 chrome.tabs.create(nextUpdatePage); |
| 694 break; |
| 680 case 'decreaseTtsRate': | 695 case 'decreaseTtsRate': |
| 681 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.RATE, false); | 696 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.RATE, false); |
| 682 return false; | 697 return false; |
| 683 case 'increaseTtsRate': | 698 case 'increaseTtsRate': |
| 684 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.RATE, true); | 699 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.RATE, true); |
| 685 return false; | 700 return false; |
| 686 case 'decreaseTtsPitch': | 701 case 'decreaseTtsPitch': |
| 687 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.PITCH, false); | 702 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.PITCH, false); |
| 688 return false; | 703 return false; |
| 689 case 'increaseTtsPitch': | 704 case 'increaseTtsPitch': |
| 690 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.PITCH, true); | 705 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.PITCH, true); |
| 691 return false; | 706 return false; |
| 692 case 'decreaseTtsVolume': | 707 case 'decreaseTtsVolume': |
| 693 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.VOLUME, false); | 708 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.VOLUME, false); |
| 694 return false; | 709 return false; |
| 695 case 'increaseTtsVolume': | 710 case 'increaseTtsVolume': |
| 696 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.VOLUME, true); | 711 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.VOLUME, true); |
| 697 return false; | 712 return false; |
| 698 case 'stopSpeech': | 713 case 'stopSpeech': |
| 699 cvox.ChromeVox.tts.stop(); | 714 cvox.ChromeVox.tts.stop(); |
| 700 ChromeVoxState.isReadingContinuously = false; | 715 global.isReadingContinuously = false; |
| 701 return false; | 716 return false; |
| 702 case 'toggleEarcons': | 717 case 'toggleEarcons': |
| 703 cvox.AbstractEarcons.enabled = !cvox.AbstractEarcons.enabled; | 718 cvox.AbstractEarcons.enabled = !cvox.AbstractEarcons.enabled; |
| 704 var announce = cvox.AbstractEarcons.enabled ? | 719 var announce = cvox.AbstractEarcons.enabled ? |
| 705 Msgs.getMsg('earcons_on') : | 720 Msgs.getMsg('earcons_on') : |
| 706 Msgs.getMsg('earcons_off'); | 721 Msgs.getMsg('earcons_off'); |
| 707 cvox.ChromeVox.tts.speak( | 722 cvox.ChromeVox.tts.speak( |
| 708 announce, cvox.QueueMode.FLUSH, | 723 announce, cvox.QueueMode.FLUSH, |
| 709 cvox.AbstractTts.PERSONALITY_ANNOTATION); | 724 cvox.AbstractTts.PERSONALITY_ANNOTATION); |
| 710 return false; | 725 return false; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 725 case cvox.TypingEcho.NONE: | 740 case cvox.TypingEcho.NONE: |
| 726 announce = Msgs.getMsg('none_echo'); | 741 announce = Msgs.getMsg('none_echo'); |
| 727 break; | 742 break; |
| 728 } | 743 } |
| 729 cvox.ChromeVox.tts.speak( | 744 cvox.ChromeVox.tts.speak( |
| 730 announce, cvox.QueueMode.FLUSH, | 745 announce, cvox.QueueMode.FLUSH, |
| 731 cvox.AbstractTts.PERSONALITY_ANNOTATION); | 746 cvox.AbstractTts.PERSONALITY_ANNOTATION); |
| 732 return false; | 747 return false; |
| 733 case 'cyclePunctuationEcho': | 748 case 'cyclePunctuationEcho': |
| 734 cvox.ChromeVox.tts.speak(Msgs.getMsg( | 749 cvox.ChromeVox.tts.speak(Msgs.getMsg( |
| 735 ChromeVoxState.backgroundTts.cyclePunctuationEcho()), | 750 global.backgroundTts.cyclePunctuationEcho()), |
| 736 cvox.QueueMode.FLUSH); | 751 cvox.QueueMode.FLUSH); |
| 737 return false; | 752 return false; |
| 738 case 'speakTimeAndDate': | 753 case 'speakTimeAndDate': |
| 739 chrome.automation.getDesktop(function(d) { | 754 chrome.automation.getDesktop(function(d) { |
| 740 // First, try speaking the on-screen time. | 755 // First, try speaking the on-screen time. |
| 741 var allTime = d.findAll({role: RoleType.time}); | 756 var allTime = d.findAll({role: RoleType.time}); |
| 742 allTime.filter(function(t) { | 757 allTime.filter(function(t) { |
| 743 return t.root.role == RoleType.desktop; | 758 return t.root.role == RoleType.desktop; |
| 744 }); | 759 }); |
| 745 | 760 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 778 output.go(); | 793 output.go(); |
| 779 return false; | 794 return false; |
| 780 case 'readCurrentURL': | 795 case 'readCurrentURL': |
| 781 var output = new Output(); | 796 var output = new Output(); |
| 782 var target = this.currentRange_.start.node.root; | 797 var target = this.currentRange_.start.node.root; |
| 783 output.withString(target.docUrl || '').go(); | 798 output.withString(target.docUrl || '').go(); |
| 784 return false; | 799 return false; |
| 785 case 'reportIssue': | 800 case 'reportIssue': |
| 786 var url = Background.ISSUE_URL; | 801 var url = Background.ISSUE_URL; |
| 787 var description = {}; | 802 var description = {}; |
| 788 description['Mode'] = this.mode; | 803 description['Mode'] = this.mode_; |
| 789 description['Version'] = chrome.app.getDetails().version; | 804 description['Version'] = chrome.app.getDetails().version; |
| 790 description['Reproduction Steps'] = '%0a1.%0a2.%0a3.'; | 805 description['Reproduction Steps'] = '%0a1.%0a2.%0a3.'; |
| 791 for (var key in description) | 806 for (var key in description) |
| 792 url += key + ':%20' + description[key] + '%0a'; | 807 url += key + ':%20' + description[key] + '%0a'; |
| 793 chrome.tabs.create({url: url}); | 808 chrome.tabs.create({url: url}); |
| 794 return false; | 809 return false; |
| 795 case 'toggleBrailleCaptions': | 810 case 'toggleBrailleCaptions': |
| 796 cvox.BrailleCaptionsBackground.setActive( | 811 cvox.BrailleCaptionsBackground.setActive( |
| 797 !cvox.BrailleCaptionsBackground.isEnabled()); | 812 !cvox.BrailleCaptionsBackground.isEnabled()); |
| 798 return false; | 813 return false; |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 899 /** | 914 /** |
| 900 * Handles key down events. | 915 * Handles key down events. |
| 901 * @param {Event} evt The key down event to process. | 916 * @param {Event} evt The key down event to process. |
| 902 * @return {boolean} True if the default action should be performed. | 917 * @return {boolean} True if the default action should be performed. |
| 903 */ | 918 */ |
| 904 onKeyDown: function(evt) { | 919 onKeyDown: function(evt) { |
| 905 evt.stickyMode = cvox.ChromeVox.isStickyModeOn() && cvox.ChromeVox.isActive; | 920 evt.stickyMode = cvox.ChromeVox.isStickyModeOn() && cvox.ChromeVox.isActive; |
| 906 if (cvox.ChromeVox.passThroughMode) | 921 if (cvox.ChromeVox.passThroughMode) |
| 907 return false; | 922 return false; |
| 908 | 923 |
| 909 if (this.mode != ChromeVoxMode.CLASSIC && | 924 if (this.mode_ != ChromeVoxMode.CLASSIC && |
| 910 !cvox.ChromeVoxKbHandler.basicKeyDownActionsListener(evt)) { | 925 !cvox.ChromeVoxKbHandler.basicKeyDownActionsListener(evt)) { |
| 911 evt.preventDefault(); | 926 evt.preventDefault(); |
| 912 evt.stopPropagation(); | 927 evt.stopPropagation(); |
| 913 } | 928 } |
| 914 Output.flushNextSpeechUtterance(); | 929 Output.flushNextSpeechUtterance(); |
| 915 return false; | 930 return false; |
| 916 }, | 931 }, |
| 917 | 932 |
| 918 /** | 933 /** |
| 919 * Handles key up events. | 934 * Handles key up events. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 942 chrome.tabs.create(optionsPage); | 957 chrome.tabs.create(optionsPage); |
| 943 }, | 958 }, |
| 944 | 959 |
| 945 /** | 960 /** |
| 946 * Handles a braille command. | 961 * Handles a braille command. |
| 947 * @param {!cvox.BrailleKeyEvent} evt | 962 * @param {!cvox.BrailleKeyEvent} evt |
| 948 * @param {!cvox.NavBraille} content | 963 * @param {!cvox.NavBraille} content |
| 949 * @return {boolean} True if evt was processed. | 964 * @return {boolean} True if evt was processed. |
| 950 */ | 965 */ |
| 951 onBrailleKeyEvent: function(evt, content) { | 966 onBrailleKeyEvent: function(evt, content) { |
| 952 if (this.mode === ChromeVoxMode.CLASSIC) | 967 if (this.mode_ === ChromeVoxMode.CLASSIC) |
| 953 return false; | 968 return false; |
| 954 | 969 |
| 955 switch (evt.command) { | 970 switch (evt.command) { |
| 956 case cvox.BrailleKeyCommand.PAN_LEFT: | 971 case cvox.BrailleKeyCommand.PAN_LEFT: |
| 957 this.onGotCommand('previousObject'); | 972 this.onGotCommand('previousObject'); |
| 958 break; | 973 break; |
| 959 case cvox.BrailleKeyCommand.PAN_RIGHT: | 974 case cvox.BrailleKeyCommand.PAN_RIGHT: |
| 960 this.onGotCommand('nextObject'); | 975 this.onGotCommand('nextObject'); |
| 961 break; | 976 break; |
| 962 case cvox.BrailleKeyCommand.LINE_UP: | 977 case cvox.BrailleKeyCommand.LINE_UP: |
| (...skipping 19 matching lines...) Expand all Loading... |
| 982 } | 997 } |
| 983 return true; | 998 return true; |
| 984 }, | 999 }, |
| 985 | 1000 |
| 986 /** | 1001 /** |
| 987 * Returns true if the url should have Classic running. | 1002 * Returns true if the url should have Classic running. |
| 988 * @return {boolean} | 1003 * @return {boolean} |
| 989 * @private | 1004 * @private |
| 990 */ | 1005 */ |
| 991 shouldEnableClassicForUrl_: function(url) { | 1006 shouldEnableClassicForUrl_: function(url) { |
| 992 return this.mode != ChromeVoxMode.FORCE_NEXT && | 1007 return this.mode_ != ChromeVoxMode.FORCE_NEXT && |
| 993 !this.isBlacklistedForClassic_(url) && | 1008 !this.isBlacklistedForClassic_(url) && |
| 994 !this.isWhitelistedForNext_(url); | 1009 !this.isWhitelistedForNext_(url); |
| 995 }, | 1010 }, |
| 996 | 1011 |
| 997 /** | 1012 /** |
| 998 * Compat mode is on if any of the following are true: | |
| 999 * 1. a url is blacklisted for Classic. | |
| 1000 * 2. the current range is not within web content. | |
| 1001 * @param {string} url | |
| 1002 */ | |
| 1003 isWhitelistedForCompat_: function(url) { | |
| 1004 return this.isBlacklistedForClassic_(url) || (this.getCurrentRange() && | |
| 1005 !this.getCurrentRange().isWebRange() && | |
| 1006 this.getCurrentRange().start.node.state.focused); | |
| 1007 }, | |
| 1008 | |
| 1009 /** | |
| 1010 * @param {string} url | 1013 * @param {string} url |
| 1011 * @return {boolean} | 1014 * @return {boolean} |
| 1012 * @private | 1015 * @private |
| 1013 */ | 1016 */ |
| 1014 isBlacklistedForClassic_: function(url) { | 1017 isBlacklistedForClassic_: function(url) { |
| 1015 if (this.classicBlacklistRegExp_.test(url)) | 1018 if (this.classicBlacklistRegExp_.test(url)) |
| 1016 return true; | 1019 return true; |
| 1017 url = url.substring(0, url.indexOf('#')) || url; | 1020 url = url.substring(0, url.indexOf('#')) || url; |
| 1018 return this.classicBlacklist_.has(url); | 1021 return this.classicBlacklist_.has(url); |
| 1019 }, | 1022 }, |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1095 var url = msg['url']; | 1098 var url = msg['url']; |
| 1096 var isClassicEnabled = this.shouldEnableClassicForUrl_(url); | 1099 var isClassicEnabled = this.shouldEnableClassicForUrl_(url); |
| 1097 port.postMessage({ | 1100 port.postMessage({ |
| 1098 target: 'next', | 1101 target: 'next', |
| 1099 isClassicEnabled: isClassicEnabled | 1102 isClassicEnabled: isClassicEnabled |
| 1100 }); | 1103 }); |
| 1101 } else if (action == 'enableCompatForUrl') { | 1104 } else if (action == 'enableCompatForUrl') { |
| 1102 var url = msg['url']; | 1105 var url = msg['url']; |
| 1103 this.classicBlacklist_.add(url); | 1106 this.classicBlacklist_.add(url); |
| 1104 if (this.currentRange_ && this.currentRange_.start.node) | 1107 if (this.currentRange_ && this.currentRange_.start.node) |
| 1105 this.setCurrentRange(this.currentRange_); | 1108 this.refreshMode(this.currentRange_.start.node); |
| 1106 } else if (action == 'onCommand') { | 1109 } else if (action == 'onCommand') { |
| 1107 this.onGotCommand(msg['command']); | 1110 this.onGotCommand(msg['command']); |
| 1108 } else if (action == 'flushNextUtterance') { | 1111 } else if (action == 'flushNextUtterance') { |
| 1109 Output.flushNextSpeechUtterance(); | 1112 Output.flushNextSpeechUtterance(); |
| 1110 } | 1113 } |
| 1111 break; | 1114 break; |
| 1112 } | 1115 } |
| 1113 }, | 1116 }, |
| 1114 | 1117 |
| 1115 /** | 1118 /** |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1171 /** | 1174 /** |
| 1172 * Handles accessibility gestures from the touch screen. | 1175 * Handles accessibility gestures from the touch screen. |
| 1173 * @param {string} gesture The gesture to handle, based on the AXGesture enum | 1176 * @param {string} gesture The gesture to handle, based on the AXGesture enum |
| 1174 * defined in ui/accessibility/ax_enums.idl | 1177 * defined in ui/accessibility/ax_enums.idl |
| 1175 * @private | 1178 * @private |
| 1176 */ | 1179 */ |
| 1177 onAccessibilityGesture_: function(gesture) { | 1180 onAccessibilityGesture_: function(gesture) { |
| 1178 // If we're in classic mode, some gestures need to be handled by the | 1181 // If we're in classic mode, some gestures need to be handled by the |
| 1179 // content script. Other gestures are universal and will be handled in | 1182 // content script. Other gestures are universal and will be handled in |
| 1180 // this function. | 1183 // this function. |
| 1181 if (this.mode == ChromeVoxMode.CLASSIC) { | 1184 if (this.mode_ == ChromeVoxMode.CLASSIC) { |
| 1182 if (this.handleClassicGesture_(gesture)) | 1185 if (this.handleClassicGesture_(gesture)) |
| 1183 return; | 1186 return; |
| 1184 } | 1187 } |
| 1185 | 1188 |
| 1186 var command = Background.GESTURE_NEXT_COMMAND_MAP[gesture]; | 1189 var command = Background.GESTURE_NEXT_COMMAND_MAP[gesture]; |
| 1187 if (command) | 1190 if (command) |
| 1188 this.onGotCommand(command); | 1191 this.onGotCommand(command); |
| 1189 }, | 1192 }, |
| 1190 | 1193 |
| 1191 /** | 1194 /** |
| 1192 * Handles accessibility gestures from the touch screen when in CLASSIC | 1195 * Handles accessibility gestures from the touch screen when in CLASSIC |
| 1193 * mode, by forwarding a command to the content script. | 1196 * mode, by forwarding a command to the content script. |
| 1194 * @param {string} gesture The gesture to handle, based on the AXGesture enum | 1197 * @param {string} gesture The gesture to handle, based on the AXGesture enum |
| 1195 * defined in ui/accessibility/ax_enums.idl | 1198 * defined in ui/accessibility/ax_enums.idl |
| 1196 * @return {boolean} True if this gesture was handled. | 1199 * @return {boolean} True if this gesture was handled. |
| 1197 * @private | 1200 * @private |
| 1198 */ | 1201 */ |
| 1199 handleClassicGesture_: function(gesture) { | 1202 handleClassicGesture_: function(gesture) { |
| 1200 var command = Background.GESTURE_CLASSIC_COMMAND_MAP[gesture]; | 1203 var command = Background.GESTURE_CLASSIC_COMMAND_MAP[gesture]; |
| 1201 if (!command) | 1204 if (!command) |
| 1202 return false; | 1205 return false; |
| 1203 | 1206 |
| 1204 var msg = { | 1207 var msg = { |
| 1205 'message': 'USER_COMMAND', | 1208 'message': 'USER_COMMAND', |
| 1206 'command': command | 1209 'command': command |
| 1207 }; | 1210 }; |
| 1208 cvox.ExtensionBridge.send(msg); | 1211 cvox.ExtensionBridge.send(msg); |
| 1209 return true; | 1212 return true; |
| 1210 }, | 1213 }, |
| 1211 | |
| 1212 /** @private */ | |
| 1213 setCurrentRangeToFocus_: function() { | |
| 1214 chrome.automation.getFocus(function(focus) { | |
| 1215 if (focus) | |
| 1216 this.setCurrentRange(cursors.Range.fromNode(focus)); | |
| 1217 else | |
| 1218 this.setCurrentRange(null); | |
| 1219 }.bind(this)); | |
| 1220 }, | |
| 1221 }; | 1214 }; |
| 1222 | 1215 |
| 1223 /** | 1216 /** |
| 1224 * Converts a list of globs, as used in the extension manifest, to a regular | 1217 * Converts a list of globs, as used in the extension manifest, to a regular |
| 1225 * expression that matches if and only if any of the globs in the list matches. | 1218 * expression that matches if and only if any of the globs in the list matches. |
| 1226 * @param {!Array<string>} globs | 1219 * @param {!Array<string>} globs |
| 1227 * @return {!RegExp} | 1220 * @return {!RegExp} |
| 1228 * @private | 1221 * @private |
| 1229 */ | 1222 */ |
| 1230 Background.globsToRegExp_ = function(globs) { | 1223 Background.globsToRegExp_ = function(globs) { |
| 1231 return new RegExp('^(' + globs.map(function(glob) { | 1224 return new RegExp('^(' + globs.map(function(glob) { |
| 1232 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') | 1225 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') |
| 1233 .replace(/\*/g, '.*') | 1226 .replace(/\*/g, '.*') |
| 1234 .replace(/\?/g, '.'); | 1227 .replace(/\?/g, '.'); |
| 1235 }).join('|') + ')$'); | 1228 }).join('|') + ')$'); |
| 1236 }; | 1229 }; |
| 1237 | 1230 |
| 1238 new Background(); | 1231 /** @type {Background} */ |
| 1232 global.backgroundObj = new Background(); |
| 1239 | 1233 |
| 1240 }); // goog.scope | 1234 }); // goog.scope |
| OLD | NEW |