| 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 | 11 |
| 12 goog.require('AutomationPredicate'); | 12 goog.require('AutomationPredicate'); |
| 13 goog.require('AutomationUtil'); | 13 goog.require('AutomationUtil'); |
| 14 goog.require('BackgroundKeyboardHandler'); |
| 14 goog.require('ChromeVoxState'); | 15 goog.require('ChromeVoxState'); |
| 16 goog.require('CommandHandler'); |
| 15 goog.require('LiveRegions'); | 17 goog.require('LiveRegions'); |
| 16 goog.require('NextEarcons'); | 18 goog.require('NextEarcons'); |
| 17 goog.require('Notifications'); | 19 goog.require('Notifications'); |
| 18 goog.require('Output'); | 20 goog.require('Output'); |
| 19 goog.require('Output.EventType'); | 21 goog.require('Output.EventType'); |
| 20 goog.require('PanelCommand'); | 22 goog.require('PanelCommand'); |
| 21 goog.require('Stubs'); | 23 goog.require('Stubs'); |
| 22 goog.require('constants'); | 24 goog.require('constants'); |
| 23 goog.require('cursors.Cursor'); | 25 goog.require('cursors.Cursor'); |
| 24 goog.require('cvox.BrailleKeyCommand'); | 26 goog.require('cvox.BrailleKeyCommand'); |
| 25 goog.require('cvox.ChromeVoxBackground'); | 27 goog.require('cvox.ChromeVoxBackground'); |
| 26 goog.require('cvox.ChromeVoxEditableTextBase'); | 28 goog.require('cvox.ChromeVoxEditableTextBase'); |
| 27 goog.require('cvox.ChromeVoxKbHandler'); | |
| 28 goog.require('cvox.ClassicEarcons'); | 29 goog.require('cvox.ClassicEarcons'); |
| 29 goog.require('cvox.ExtensionBridge'); | 30 goog.require('cvox.ExtensionBridge'); |
| 30 goog.require('cvox.NavBraille'); | 31 goog.require('cvox.NavBraille'); |
| 31 | 32 |
| 32 goog.scope(function() { | 33 goog.scope(function() { |
| 33 var AutomationNode = chrome.automation.AutomationNode; | 34 var AutomationNode = chrome.automation.AutomationNode; |
| 34 var Dir = constants.Dir; | 35 var Dir = constants.Dir; |
| 35 var EventType = chrome.automation.EventType; | 36 var EventType = chrome.automation.EventType; |
| 36 var RoleType = chrome.automation.RoleType; | 37 var RoleType = chrome.automation.RoleType; |
| 37 | 38 |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 get: function() { | 119 get: function() { |
| 119 return localStorage['active'] !== 'false'; | 120 return localStorage['active'] !== 'false'; |
| 120 }, | 121 }, |
| 121 set: function(value) { | 122 set: function(value) { |
| 122 localStorage['active'] = value; | 123 localStorage['active'] = value; |
| 123 } | 124 } |
| 124 }); | 125 }); |
| 125 | 126 |
| 126 cvox.ExtensionBridge.addMessageListener(this.onMessage_); | 127 cvox.ExtensionBridge.addMessageListener(this.onMessage_); |
| 127 | 128 |
| 128 document.addEventListener('keydown', this.onKeyDown.bind(this), false); | 129 /** @type {!BackgroundKeyboardHandler} */ |
| 129 document.addEventListener('keyup', this.onKeyUp.bind(this), false); | 130 this.keyboardHandler_ = new BackgroundKeyboardHandler(); |
| 130 cvox.ChromeVoxKbHandler.commandHandler = this.onGotCommand.bind(this); | |
| 131 | 131 |
| 132 // Classic keymap. | 132 /** @type {!LiveRegions} */ |
| 133 cvox.ChromeVoxKbHandler.handlerKeyMap = cvox.KeyMap.fromDefaults(); | |
| 134 | |
| 135 // Live region handler. | |
| 136 this.liveRegions_ = new LiveRegions(this); | 133 this.liveRegions_ = new LiveRegions(this); |
| 137 | 134 |
| 138 /** @type {number} @private */ | |
| 139 this.passThroughKeyUpCount_ = 0; | |
| 140 | |
| 141 /** @type {boolean} @private */ | 135 /** @type {boolean} @private */ |
| 142 this.inExcursion_ = false; | 136 this.inExcursion_ = false; |
| 143 | 137 |
| 144 /** | 138 /** |
| 145 * Stores the mode as computed the last time a current range was set. | 139 * Stores the mode as computed the last time a current range was set. |
| 146 * @type {?ChromeVoxMode} | 140 * @type {?ChromeVoxMode} |
| 147 */ | 141 */ |
| 148 this.mode_ = null; | 142 this.mode_ = null; |
| 149 | 143 |
| 150 chrome.accessibilityPrivate.onAccessibilityGesture.addListener( | 144 chrome.accessibilityPrivate.onAccessibilityGesture.addListener( |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 231 }, | 225 }, |
| 232 | 226 |
| 233 /** | 227 /** |
| 234 * Handles a mode change. | 228 * Handles a mode change. |
| 235 * @param {ChromeVoxMode} newMode | 229 * @param {ChromeVoxMode} newMode |
| 236 * @param {?ChromeVoxMode} oldMode Can be null at startup when no range was | 230 * @param {?ChromeVoxMode} oldMode Can be null at startup when no range was |
| 237 * previously set. | 231 * previously set. |
| 238 * @private | 232 * @private |
| 239 */ | 233 */ |
| 240 onModeChanged_: function(newMode, oldMode) { | 234 onModeChanged_: function(newMode, oldMode) { |
| 241 if (newMode == ChromeVoxMode.CLASSIC) { | 235 this.keyboardHandler_.onModeChanged(newMode, oldMode); |
| 242 if (chrome.commands && | 236 CommandHandler.onModeChanged(newMode, oldMode); |
| 243 chrome.commands.onCommand.hasListener(this.onGotCommand)) | 237 |
| 244 chrome.commands.onCommand.removeListener(this.onGotCommand); | 238 if (newMode == ChromeVoxMode.CLASSIC) |
| 245 chrome.accessibilityPrivate.setKeyboardListener(false, false); | |
| 246 chrome.accessibilityPrivate.setFocusRing([]); | 239 chrome.accessibilityPrivate.setFocusRing([]); |
| 247 } else { | |
| 248 if (chrome.commands && | |
| 249 !chrome.commands.onCommand.hasListener(this.onGotCommand)) | |
| 250 chrome.commands.onCommand.addListener(this.onGotCommand); | |
| 251 chrome.accessibilityPrivate.setKeyboardListener( | |
| 252 true, cvox.ChromeVox.isStickyPrefOn); | |
| 253 } | |
| 254 | 240 |
| 255 // note that |this.currentRange_| can *change* because the request is | 241 // note that |this.currentRange_| can *change* because the request is |
| 256 // async. Save it to ensure we're looking at the currentRange at this moment | 242 // async. Save it to ensure we're looking at the currentRange at this moment |
| 257 // in time. | 243 // in time. |
| 258 var cur = this.currentRange_; | 244 var cur = this.currentRange_; |
| 259 chrome.tabs.query({active: true, | 245 chrome.tabs.query({active: true, |
| 260 lastFocusedWindow: true}, function(tabs) { | 246 lastFocusedWindow: true}, function(tabs) { |
| 261 if (newMode === ChromeVoxMode.CLASSIC) { | 247 if (newMode === ChromeVoxMode.CLASSIC) { |
| 262 // Generally, we don't want to inject classic content scripts as it is | 248 // Generally, we don't want to inject classic content scripts as it is |
| 263 // done by the extension system at document load. The exception is when | 249 // done by the extension system at document load. The exception is when |
| (...skipping 21 matching lines...) Expand all Loading... |
| 285 cvox.ChromeVox.earcons.cancelEarcon(cvox.Earcon.PAGE_START_LOADING); | 271 cvox.ChromeVox.earcons.cancelEarcon(cvox.Earcon.PAGE_START_LOADING); |
| 286 (new PanelCommand(PanelCommandType.DISABLE_MENUS)).send(); | 272 (new PanelCommand(PanelCommandType.DISABLE_MENUS)).send(); |
| 287 } | 273 } |
| 288 | 274 |
| 289 // Switching out of next, force next, or uninitialized (on startup). | 275 // Switching out of next, force next, or uninitialized (on startup). |
| 290 if (newMode === ChromeVoxMode.NEXT || | 276 if (newMode === ChromeVoxMode.NEXT || |
| 291 newMode === ChromeVoxMode.FORCE_NEXT) { | 277 newMode === ChromeVoxMode.FORCE_NEXT) { |
| 292 (new PanelCommand(PanelCommandType.ENABLE_MENUS)).send(); | 278 (new PanelCommand(PanelCommandType.ENABLE_MENUS)).send(); |
| 293 if (cvox.TabsApiHandler) | 279 if (cvox.TabsApiHandler) |
| 294 cvox.TabsApiHandler.shouldOutputSpeechAndBraille = false; | 280 cvox.TabsApiHandler.shouldOutputSpeechAndBraille = false; |
| 295 | |
| 296 window['prefs'].switchToKeyMap('keymap_next'); | |
| 297 } else { | 281 } else { |
| 298 // |newMode| is either classic or compat. | 282 // |newMode| is either classic or compat. |
| 299 if (cvox.TabsApiHandler) | 283 if (cvox.TabsApiHandler) |
| 300 cvox.TabsApiHandler.shouldOutputSpeechAndBraille = true; | 284 cvox.TabsApiHandler.shouldOutputSpeechAndBraille = true; |
| 301 | |
| 302 // Moving from next to classic/compat should be the only case where | |
| 303 // keymaps get reset. Note the classic <-> compat switches should preserve | |
| 304 // keymaps especially if a user selected a different one. | |
| 305 if (oldMode && | |
| 306 oldMode != ChromeVoxMode.CLASSIC && | |
| 307 oldMode != ChromeVoxMode.COMPAT) { | |
| 308 // The user's configured key map gets wiped here; this is consistent | |
| 309 // with previous behavior when switching keymaps. | |
| 310 window['prefs'].switchToKeyMap('keymap_next'); | |
| 311 } | |
| 312 } | 285 } |
| 313 }, | 286 }, |
| 314 | 287 |
| 315 /** | 288 /** |
| 316 * Toggles between force next and classic/compat modes. | 289 * Toggles between force next and classic/compat modes. |
| 317 * This toggle automatically handles deciding between classic/compat based on | 290 * This toggle automatically handles deciding between classic/compat based on |
| 318 * the start of the current range. | 291 * the start of the current range. |
| 319 * @param {boolean=} opt_setValue Directly set to force next (true) or | 292 * @param {boolean=} opt_setValue Directly set to force next (true) or |
| 320 * classic/compat (false). | 293 * classic/compat (false). |
| 321 * @return {boolean} True to announce current position. | 294 * @return {boolean} True to announce current position. |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 379 var loc = start.location; | 352 var loc = start.location; |
| 380 position.x = loc.left + loc.width / 2; | 353 position.x = loc.left + loc.width / 2; |
| 381 position.y = loc.top + loc.height / 2; | 354 position.y = loc.top + loc.height / 2; |
| 382 var url = root.docUrl; | 355 var url = root.docUrl; |
| 383 url = url.substring(0, url.indexOf('#')) || url; | 356 url = url.substring(0, url.indexOf('#')) || url; |
| 384 cvox.ChromeVox.position[url] = position; | 357 cvox.ChromeVox.position[url] = position; |
| 385 } | 358 } |
| 386 }, | 359 }, |
| 387 | 360 |
| 388 /** | 361 /** |
| 389 * Handles ChromeVox Next commands. | |
| 390 * @param {string} command | |
| 391 * @return {boolean} True if the command should propagate. | |
| 392 */ | |
| 393 onGotCommand: function(command) { | |
| 394 // Check for loss of focus which results in us invalidating our current | |
| 395 // range. Note this call is synchronis. | |
| 396 chrome.automation.getFocus(function(focusedNode) { | |
| 397 if (this.currentRange_ && !this.currentRange_.isValid()) | |
| 398 this.currentRange_ = cursors.Range.fromNode(focusedNode); | |
| 399 if (!focusedNode) | |
| 400 this.currentRange_ = null; | |
| 401 }.bind(this)); | |
| 402 | |
| 403 // These commands don't require a current range and work in all modes. | |
| 404 switch (command) { | |
| 405 case 'toggleChromeVoxVersion': | |
| 406 if (!this.toggleNext()) | |
| 407 return false; | |
| 408 if (this.currentRange_) | |
| 409 this.navigateToRange(this.currentRange_); | |
| 410 break; | |
| 411 case 'showNextUpdatePage': | |
| 412 (new PanelCommand(PanelCommandType.TUTORIAL)).send(); | |
| 413 return false; | |
| 414 default: | |
| 415 break; | |
| 416 } | |
| 417 | |
| 418 // Require a current range. | |
| 419 if (!this.currentRange_) | |
| 420 return true; | |
| 421 | |
| 422 // Next/compat commands hereafter. | |
| 423 if (this.mode == ChromeVoxMode.CLASSIC) | |
| 424 return true; | |
| 425 | |
| 426 var current = this.currentRange_; | |
| 427 var dir = Dir.FORWARD; | |
| 428 var pred = null; | |
| 429 var predErrorMsg = undefined; | |
| 430 var speechProps = {}; | |
| 431 switch (command) { | |
| 432 case 'nextCharacter': | |
| 433 speechProps['phoneticCharacters'] = true; | |
| 434 current = current.move(cursors.Unit.CHARACTER, Dir.FORWARD); | |
| 435 break; | |
| 436 case 'previousCharacter': | |
| 437 speechProps['phoneticCharacters'] = true; | |
| 438 current = current.move(cursors.Unit.CHARACTER, Dir.BACKWARD); | |
| 439 break; | |
| 440 case 'nextWord': | |
| 441 current = current.move(cursors.Unit.WORD, Dir.FORWARD); | |
| 442 break; | |
| 443 case 'previousWord': | |
| 444 current = current.move(cursors.Unit.WORD, Dir.BACKWARD); | |
| 445 break; | |
| 446 case 'forward': | |
| 447 case 'nextLine': | |
| 448 current = current.move(cursors.Unit.LINE, Dir.FORWARD); | |
| 449 break; | |
| 450 case 'backward': | |
| 451 case 'previousLine': | |
| 452 current = current.move(cursors.Unit.LINE, Dir.BACKWARD); | |
| 453 break; | |
| 454 case 'nextButton': | |
| 455 dir = Dir.FORWARD; | |
| 456 pred = AutomationPredicate.button; | |
| 457 predErrorMsg = 'no_next_button'; | |
| 458 break; | |
| 459 case 'previousButton': | |
| 460 dir = Dir.BACKWARD; | |
| 461 pred = AutomationPredicate.button; | |
| 462 predErrorMsg = 'no_previous_button'; | |
| 463 break; | |
| 464 case 'nextCheckbox': | |
| 465 dir = Dir.FORWARD; | |
| 466 pred = AutomationPredicate.checkBox; | |
| 467 predErrorMsg = 'no_next_checkbox'; | |
| 468 break; | |
| 469 case 'previousCheckbox': | |
| 470 dir = Dir.BACKWARD; | |
| 471 pred = AutomationPredicate.checkBox; | |
| 472 predErrorMsg = 'no_previous_checkbox'; | |
| 473 break; | |
| 474 case 'nextComboBox': | |
| 475 dir = Dir.FORWARD; | |
| 476 pred = AutomationPredicate.comboBox; | |
| 477 predErrorMsg = 'no_next_combo_box'; | |
| 478 break; | |
| 479 case 'previousComboBox': | |
| 480 dir = Dir.BACKWARD; | |
| 481 pred = AutomationPredicate.comboBox; | |
| 482 predErrorMsg = 'no_previous_combo_box'; | |
| 483 break; | |
| 484 case 'nextEditText': | |
| 485 dir = Dir.FORWARD; | |
| 486 pred = AutomationPredicate.editText; | |
| 487 predErrorMsg = 'no_next_edit_text'; | |
| 488 break; | |
| 489 case 'previousEditText': | |
| 490 dir = Dir.BACKWARD; | |
| 491 pred = AutomationPredicate.editText; | |
| 492 predErrorMsg = 'no_previous_edit_text'; | |
| 493 break; | |
| 494 case 'nextFormField': | |
| 495 dir = Dir.FORWARD; | |
| 496 pred = AutomationPredicate.formField; | |
| 497 predErrorMsg = 'no_next_form_field'; | |
| 498 break; | |
| 499 case 'previousFormField': | |
| 500 dir = Dir.BACKWARD; | |
| 501 pred = AutomationPredicate.formField; | |
| 502 predErrorMsg = 'no_previous_form_field'; | |
| 503 break; | |
| 504 case 'nextHeading': | |
| 505 dir = Dir.FORWARD; | |
| 506 pred = AutomationPredicate.heading; | |
| 507 predErrorMsg = 'no_next_heading'; | |
| 508 break; | |
| 509 case 'previousHeading': | |
| 510 dir = Dir.BACKWARD; | |
| 511 pred = AutomationPredicate.heading; | |
| 512 predErrorMsg = 'no_previous_heading'; | |
| 513 break; | |
| 514 case 'nextLink': | |
| 515 dir = Dir.FORWARD; | |
| 516 pred = AutomationPredicate.link; | |
| 517 predErrorMsg = 'no_next_link'; | |
| 518 break; | |
| 519 case 'previousLink': | |
| 520 dir = Dir.BACKWARD; | |
| 521 pred = AutomationPredicate.link; | |
| 522 predErrorMsg = 'no_previous_link'; | |
| 523 break; | |
| 524 case 'nextTable': | |
| 525 dir = Dir.FORWARD; | |
| 526 pred = AutomationPredicate.table; | |
| 527 predErrorMsg = 'no_next_table'; | |
| 528 break; | |
| 529 case 'previousTable': | |
| 530 dir = Dir.BACKWARD; | |
| 531 pred = AutomationPredicate.table; | |
| 532 predErrorMsg = 'no_previous_table'; | |
| 533 break; | |
| 534 case 'nextVisitedLink': | |
| 535 dir = Dir.FORWARD; | |
| 536 pred = AutomationPredicate.visitedLink; | |
| 537 predErrorMsg = 'no_next_visited_link'; | |
| 538 break; | |
| 539 case 'previousVisitedLink': | |
| 540 dir = Dir.BACKWARD; | |
| 541 pred = AutomationPredicate.visitedLink; | |
| 542 predErrorMsg = 'no_previous_visited_link'; | |
| 543 break; | |
| 544 case 'right': | |
| 545 case 'nextObject': | |
| 546 current = current.move(cursors.Unit.DOM_NODE, Dir.FORWARD); | |
| 547 break; | |
| 548 case 'left': | |
| 549 case 'previousObject': | |
| 550 current = current.move(cursors.Unit.DOM_NODE, Dir.BACKWARD); | |
| 551 break; | |
| 552 case 'jumpToTop': | |
| 553 var node = | |
| 554 AutomationUtil.findNodePost(current.start.node.root, | |
| 555 Dir.FORWARD, | |
| 556 AutomationPredicate.leaf); | |
| 557 if (node) | |
| 558 current = cursors.Range.fromNode(node); | |
| 559 break; | |
| 560 case 'jumpToBottom': | |
| 561 var node = | |
| 562 AutomationUtil.findNodePost(current.start.node.root, | |
| 563 Dir.BACKWARD, | |
| 564 AutomationPredicate.leaf); | |
| 565 if (node) | |
| 566 current = cursors.Range.fromNode(node); | |
| 567 break; | |
| 568 case 'forceClickOnCurrentItem': | |
| 569 if (this.currentRange_) { | |
| 570 var actionNode = this.currentRange_.start.node; | |
| 571 if (actionNode.role == RoleType.inlineTextBox) | |
| 572 actionNode = actionNode.parent; | |
| 573 actionNode.doDefault(); | |
| 574 } | |
| 575 // Skip all other processing; if focus changes, we should get an event | |
| 576 // for that. | |
| 577 return false; | |
| 578 case 'readFromHere': | |
| 579 ChromeVoxState.isReadingContinuously = true; | |
| 580 var continueReading = function() { | |
| 581 if (!ChromeVoxState.isReadingContinuously || !this.currentRange_) | |
| 582 return; | |
| 583 | |
| 584 var prevRange = this.currentRange_; | |
| 585 var newRange = | |
| 586 this.currentRange_.move(cursors.Unit.DOM_NODE, Dir.FORWARD); | |
| 587 | |
| 588 // Stop if we've wrapped back to the document. | |
| 589 var maybeDoc = newRange.start.node; | |
| 590 if (maybeDoc.role == RoleType.rootWebArea && | |
| 591 maybeDoc.parent.root.role == RoleType.desktop) { | |
| 592 ChromeVoxState.isReadingContinuously = false; | |
| 593 return; | |
| 594 } | |
| 595 | |
| 596 this.setCurrentRange(newRange); | |
| 597 | |
| 598 new Output().withRichSpeechAndBraille( | |
| 599 this.currentRange_, prevRange, Output.EventType.NAVIGATE) | |
| 600 .onSpeechEnd(continueReading) | |
| 601 .go(); | |
| 602 }.bind(this); | |
| 603 | |
| 604 new Output().withRichSpeechAndBraille( | |
| 605 this.currentRange_, null, Output.EventType.NAVIGATE) | |
| 606 .onSpeechEnd(continueReading) | |
| 607 .go(); | |
| 608 | |
| 609 return false; | |
| 610 case 'contextMenu': | |
| 611 if (this.currentRange_) { | |
| 612 var actionNode = this.currentRange_.start.node; | |
| 613 if (actionNode.role == RoleType.inlineTextBox) | |
| 614 actionNode = actionNode.parent; | |
| 615 actionNode.showContextMenu(); | |
| 616 return false; | |
| 617 } | |
| 618 break; | |
| 619 case 'showOptionsPage': | |
| 620 chrome.runtime.openOptionsPage(); | |
| 621 break; | |
| 622 case 'toggleChromeVox': | |
| 623 if (cvox.ChromeVox.isChromeOS) | |
| 624 return false; | |
| 625 | |
| 626 cvox.ChromeVox.isActive = !cvox.ChromeVox.isActive; | |
| 627 if (!cvox.ChromeVox.isActive) { | |
| 628 var msg = Msgs.getMsg('chromevox_inactive'); | |
| 629 cvox.ChromeVox.tts.speak(msg, cvox.QueueMode.FLUSH); | |
| 630 return false; | |
| 631 } | |
| 632 break; | |
| 633 case 'toggleStickyMode': | |
| 634 cvox.ChromeVoxBackground.setPref('sticky', | |
| 635 !cvox.ChromeVox.isStickyPrefOn, | |
| 636 true); | |
| 637 | |
| 638 if (cvox.ChromeVox.isStickyPrefOn) | |
| 639 chrome.accessibilityPrivate.setKeyboardListener(true, true); | |
| 640 else | |
| 641 chrome.accessibilityPrivate.setKeyboardListener(true, false); | |
| 642 return false; | |
| 643 case 'passThroughMode': | |
| 644 cvox.ChromeVox.passThroughMode = true; | |
| 645 cvox.ChromeVox.tts.speak( | |
| 646 Msgs.getMsg('pass_through_key'), cvox.QueueMode.QUEUE); | |
| 647 return true; | |
| 648 case 'toggleKeyboardHelp': | |
| 649 this.startExcursion(); | |
| 650 (new PanelCommand(PanelCommandType.OPEN_MENUS)).send(); | |
| 651 return false; | |
| 652 case 'showHeadingsList': | |
| 653 this.startExcursion(); | |
| 654 (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_heading')).send(); | |
| 655 return false; | |
| 656 case 'showFormsList': | |
| 657 this.startExcursion(); | |
| 658 (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_form')).send(); | |
| 659 return false; | |
| 660 case 'showLandmarksList': | |
| 661 this.startExcursion(); | |
| 662 (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_landmark')).send(); | |
| 663 return false; | |
| 664 case 'showLinksList': | |
| 665 this.startExcursion(); | |
| 666 (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_link')).send(); | |
| 667 return false; | |
| 668 case 'showTablesList': | |
| 669 this.startExcursion(); | |
| 670 (new PanelCommand(PanelCommandType.OPEN_MENUS, 'table_strategy')) | |
| 671 .send(); | |
| 672 return false; | |
| 673 case 'toggleSearchWidget': | |
| 674 (new PanelCommand(PanelCommandType.SEARCH)).send(); | |
| 675 return false; | |
| 676 case 'showKbExplorerPage': | |
| 677 var explorerPage = {url: 'chromevox/background/kbexplorer.html'}; | |
| 678 chrome.tabs.create(explorerPage); | |
| 679 break; | |
| 680 case 'decreaseTtsRate': | |
| 681 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.RATE, false); | |
| 682 return false; | |
| 683 case 'increaseTtsRate': | |
| 684 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.RATE, true); | |
| 685 return false; | |
| 686 case 'decreaseTtsPitch': | |
| 687 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.PITCH, false); | |
| 688 return false; | |
| 689 case 'increaseTtsPitch': | |
| 690 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.PITCH, true); | |
| 691 return false; | |
| 692 case 'decreaseTtsVolume': | |
| 693 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.VOLUME, false); | |
| 694 return false; | |
| 695 case 'increaseTtsVolume': | |
| 696 this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.VOLUME, true); | |
| 697 return false; | |
| 698 case 'stopSpeech': | |
| 699 cvox.ChromeVox.tts.stop(); | |
| 700 ChromeVoxState.isReadingContinuously = false; | |
| 701 return false; | |
| 702 case 'toggleEarcons': | |
| 703 cvox.AbstractEarcons.enabled = !cvox.AbstractEarcons.enabled; | |
| 704 var announce = cvox.AbstractEarcons.enabled ? | |
| 705 Msgs.getMsg('earcons_on') : | |
| 706 Msgs.getMsg('earcons_off'); | |
| 707 cvox.ChromeVox.tts.speak( | |
| 708 announce, cvox.QueueMode.FLUSH, | |
| 709 cvox.AbstractTts.PERSONALITY_ANNOTATION); | |
| 710 return false; | |
| 711 case 'cycleTypingEcho': | |
| 712 cvox.ChromeVox.typingEcho = | |
| 713 cvox.TypingEcho.cycle(cvox.ChromeVox.typingEcho); | |
| 714 var announce = ''; | |
| 715 switch (cvox.ChromeVox.typingEcho) { | |
| 716 case cvox.TypingEcho.CHARACTER: | |
| 717 announce = Msgs.getMsg('character_echo'); | |
| 718 break; | |
| 719 case cvox.TypingEcho.WORD: | |
| 720 announce = Msgs.getMsg('word_echo'); | |
| 721 break; | |
| 722 case cvox.TypingEcho.CHARACTER_AND_WORD: | |
| 723 announce = Msgs.getMsg('character_and_word_echo'); | |
| 724 break; | |
| 725 case cvox.TypingEcho.NONE: | |
| 726 announce = Msgs.getMsg('none_echo'); | |
| 727 break; | |
| 728 } | |
| 729 cvox.ChromeVox.tts.speak( | |
| 730 announce, cvox.QueueMode.FLUSH, | |
| 731 cvox.AbstractTts.PERSONALITY_ANNOTATION); | |
| 732 return false; | |
| 733 case 'cyclePunctuationEcho': | |
| 734 cvox.ChromeVox.tts.speak(Msgs.getMsg( | |
| 735 ChromeVoxState.backgroundTts.cyclePunctuationEcho()), | |
| 736 cvox.QueueMode.FLUSH); | |
| 737 return false; | |
| 738 case 'speakTimeAndDate': | |
| 739 chrome.automation.getDesktop(function(d) { | |
| 740 // First, try speaking the on-screen time. | |
| 741 var allTime = d.findAll({role: RoleType.time}); | |
| 742 allTime.filter(function(t) { | |
| 743 return t.root.role == RoleType.desktop; | |
| 744 }); | |
| 745 | |
| 746 var timeString = ''; | |
| 747 allTime.forEach(function(t) { | |
| 748 if (t.name) | |
| 749 timeString = t.name; | |
| 750 }); | |
| 751 if (timeString) { | |
| 752 cvox.ChromeVox.tts.speak(timeString, cvox.QueueMode.FLUSH); | |
| 753 } else { | |
| 754 // Fallback to the old way of speaking time. | |
| 755 var output = new Output(); | |
| 756 var dateTime = new Date(); | |
| 757 output.withString( | |
| 758 dateTime.toLocaleTimeString() + | |
| 759 ', ' + dateTime.toLocaleDateString()).go(); | |
| 760 } | |
| 761 }); | |
| 762 return false; | |
| 763 case 'readCurrentTitle': | |
| 764 var target = this.currentRange_.start.node; | |
| 765 var output = new Output(); | |
| 766 | |
| 767 if (target.root.role == RoleType.rootWebArea) { | |
| 768 // Web. | |
| 769 target = target.root; | |
| 770 output.withString(target.name || target.docUrl); | |
| 771 } else { | |
| 772 // Views. | |
| 773 while (target.role != RoleType.window) | |
| 774 target = target.parent; | |
| 775 if (target) | |
| 776 output.withString(target.name || ''); | |
| 777 } | |
| 778 output.go(); | |
| 779 return false; | |
| 780 case 'readCurrentURL': | |
| 781 var output = new Output(); | |
| 782 var target = this.currentRange_.start.node.root; | |
| 783 output.withString(target.docUrl || '').go(); | |
| 784 return false; | |
| 785 case 'reportIssue': | |
| 786 var url = Background.ISSUE_URL; | |
| 787 var description = {}; | |
| 788 description['Mode'] = this.mode; | |
| 789 description['Version'] = chrome.app.getDetails().version; | |
| 790 description['Reproduction Steps'] = '%0a1.%0a2.%0a3.'; | |
| 791 for (var key in description) | |
| 792 url += key + ':%20' + description[key] + '%0a'; | |
| 793 chrome.tabs.create({url: url}); | |
| 794 return false; | |
| 795 case 'toggleBrailleCaptions': | |
| 796 cvox.BrailleCaptionsBackground.setActive( | |
| 797 !cvox.BrailleCaptionsBackground.isEnabled()); | |
| 798 return false; | |
| 799 case 'copy': | |
| 800 var textarea = document.createElement('textarea'); | |
| 801 document.body.appendChild(textarea); | |
| 802 textarea.focus(); | |
| 803 document.execCommand('paste'); | |
| 804 var clipboardContent = textarea.value; | |
| 805 textarea.remove(); | |
| 806 cvox.ChromeVox.tts.speak( | |
| 807 Msgs.getMsg('copy', [clipboardContent]), cvox.QueueMode.FLUSH); | |
| 808 this.pageSel_ = null; | |
| 809 return true; | |
| 810 case 'toggleSelection': | |
| 811 if (!this.pageSel_) { | |
| 812 this.pageSel_ = this.currentRange; | |
| 813 } else { | |
| 814 var root = this.currentRange_.start.node.root; | |
| 815 if (root && root.anchorObject && root.focusObject) { | |
| 816 var sel = new cursors.Range( | |
| 817 new cursors.Cursor(root.anchorObject, root.anchorOffset), | |
| 818 new cursors.Cursor(root.focusObject, root.focusOffset) | |
| 819 ); | |
| 820 var o = new Output() | |
| 821 .format('@end_selection') | |
| 822 .withSpeechAndBraille(sel, sel, Output.EventType.NAVIGATE) | |
| 823 .go(); | |
| 824 } | |
| 825 this.pageSel_ = null; | |
| 826 return false; | |
| 827 } | |
| 828 break; | |
| 829 default: | |
| 830 return true; | |
| 831 } | |
| 832 | |
| 833 if (pred) { | |
| 834 var bound = current.getBound(dir).node; | |
| 835 if (bound) { | |
| 836 var node = AutomationUtil.findNextNode( | |
| 837 bound, dir, pred, {skipInitialAncestry: true}); | |
| 838 | |
| 839 if (node) { | |
| 840 node = AutomationUtil.findNodePre( | |
| 841 node, Dir.FORWARD, AutomationPredicate.object) || node; | |
| 842 } | |
| 843 | |
| 844 if (node) { | |
| 845 current = cursors.Range.fromNode(node); | |
| 846 } else { | |
| 847 if (predErrorMsg) { | |
| 848 cvox.ChromeVox.tts.speak(Msgs.getMsg(predErrorMsg), | |
| 849 cvox.QueueMode.FLUSH); | |
| 850 } | |
| 851 return false; | |
| 852 } | |
| 853 } | |
| 854 } | |
| 855 | |
| 856 if (current) | |
| 857 this.navigateToRange(current, undefined, speechProps); | |
| 858 | |
| 859 return false; | |
| 860 }, | |
| 861 | |
| 862 /** | |
| 863 * Increase or decrease a speech property and make an announcement. | |
| 864 * @param {string} propertyName The name of the property to change. | |
| 865 * @param {boolean} increase If true, increases the property value by one | |
| 866 * step size, otherwise decreases. | |
| 867 */ | |
| 868 increaseOrDecreaseSpeechProperty_: function(propertyName, increase) { | |
| 869 cvox.ChromeVox.tts.increaseOrDecreaseProperty(propertyName, increase); | |
| 870 var announcement; | |
| 871 var valueAsPercent = Math.round( | |
| 872 cvox.ChromeVox.tts.propertyToPercentage(propertyName) * 100); | |
| 873 switch (propertyName) { | |
| 874 case cvox.AbstractTts.RATE: | |
| 875 announcement = Msgs.getMsg('announce_rate', [valueAsPercent]); | |
| 876 break; | |
| 877 case cvox.AbstractTts.PITCH: | |
| 878 announcement = Msgs.getMsg('announce_pitch', [valueAsPercent]); | |
| 879 break; | |
| 880 case cvox.AbstractTts.VOLUME: | |
| 881 announcement = Msgs.getMsg('announce_volume', [valueAsPercent]); | |
| 882 break; | |
| 883 } | |
| 884 if (announcement) { | |
| 885 cvox.ChromeVox.tts.speak( | |
| 886 announcement, cvox.QueueMode.FLUSH, | |
| 887 cvox.AbstractTts.PERSONALITY_ANNOTATION); | |
| 888 } | |
| 889 }, | |
| 890 | |
| 891 /** | |
| 892 * Navigate to the given range - it both sets the range and outputs it. | 362 * Navigate to the given range - it both sets the range and outputs it. |
| 893 * @param {!cursors.Range} range The new range. | 363 * @param {!cursors.Range} range The new range. |
| 894 * @param {boolean=} opt_focus Focus the range; defaults to true. | 364 * @param {boolean=} opt_focus Focus the range; defaults to true. |
| 895 * @param {Object=} opt_speechProps Speech properties. | 365 * @param {Object=} opt_speechProps Speech properties. |
| 896 * @private | 366 * @private |
| 897 */ | 367 */ |
| 898 navigateToRange: function(range, opt_focus, opt_speechProps) { | 368 navigateToRange: function(range, opt_focus, opt_speechProps) { |
| 899 opt_focus = opt_focus === undefined ? true : opt_focus; | 369 opt_focus = opt_focus === undefined ? true : opt_focus; |
| 900 opt_speechProps = opt_speechProps || {}; | 370 opt_speechProps = opt_speechProps || {}; |
| 901 | 371 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 972 if (msg) | 442 if (msg) |
| 973 o.format(msg); | 443 o.format(msg); |
| 974 | 444 |
| 975 for (var prop in opt_speechProps) | 445 for (var prop in opt_speechProps) |
| 976 o.format('!' + prop); | 446 o.format('!' + prop); |
| 977 | 447 |
| 978 o.go(); | 448 o.go(); |
| 979 }, | 449 }, |
| 980 | 450 |
| 981 /** | 451 /** |
| 982 * Handles key down events. | |
| 983 * @param {Event} evt The key down event to process. | |
| 984 * @return {boolean} True if the default action should be performed. | |
| 985 */ | |
| 986 onKeyDown: function(evt) { | |
| 987 evt.stickyMode = cvox.ChromeVox.isStickyModeOn() && cvox.ChromeVox.isActive; | |
| 988 if (cvox.ChromeVox.passThroughMode) | |
| 989 return false; | |
| 990 | |
| 991 if (this.mode != ChromeVoxMode.CLASSIC && | |
| 992 !cvox.ChromeVoxKbHandler.basicKeyDownActionsListener(evt)) { | |
| 993 evt.preventDefault(); | |
| 994 evt.stopPropagation(); | |
| 995 } | |
| 996 Output.flushNextSpeechUtterance(); | |
| 997 return false; | |
| 998 }, | |
| 999 | |
| 1000 /** | |
| 1001 * Handles key up events. | |
| 1002 * @param {Event} evt The key down event to process. | |
| 1003 * @return {boolean} True if the default action should be performed. | |
| 1004 */ | |
| 1005 onKeyUp: function(evt) { | |
| 1006 // Reset pass through mode once a keyup (not involving the pass through key) | |
| 1007 // is seen. The pass through command involves three keys. | |
| 1008 if (cvox.ChromeVox.passThroughMode) { | |
| 1009 if (this.passThroughKeyUpCount_ >= 3) { | |
| 1010 cvox.ChromeVox.passThroughMode = false; | |
| 1011 this.passThroughKeyUpCount_ = 0; | |
| 1012 } else { | |
| 1013 this.passThroughKeyUpCount_++; | |
| 1014 } | |
| 1015 } | |
| 1016 return false; | |
| 1017 }, | |
| 1018 | |
| 1019 /** | |
| 1020 * Open the options page in a new tab. | 452 * Open the options page in a new tab. |
| 1021 */ | 453 */ |
| 1022 showOptionsPage: function() { | 454 showOptionsPage: function() { |
| 1023 var optionsPage = {url: 'chromevox/background/options.html'}; | 455 var optionsPage = {url: 'chromevox/background/options.html'}; |
| 1024 chrome.tabs.create(optionsPage); | 456 chrome.tabs.create(optionsPage); |
| 1025 }, | 457 }, |
| 1026 | 458 |
| 1027 /** | 459 /** |
| 1028 * Handles a braille command. | 460 * Handles a braille command. |
| 1029 * @param {!cvox.BrailleKeyEvent} evt | 461 * @param {!cvox.BrailleKeyEvent} evt |
| 1030 * @param {!cvox.NavBraille} content | 462 * @param {!cvox.NavBraille} content |
| 1031 * @return {boolean} True if evt was processed. | 463 * @return {boolean} True if evt was processed. |
| 1032 */ | 464 */ |
| 1033 onBrailleKeyEvent: function(evt, content) { | 465 onBrailleKeyEvent: function(evt, content) { |
| 1034 if (this.mode === ChromeVoxMode.CLASSIC) | 466 if (this.mode === ChromeVoxMode.CLASSIC) |
| 1035 return false; | 467 return false; |
| 1036 | 468 |
| 1037 switch (evt.command) { | 469 switch (evt.command) { |
| 1038 case cvox.BrailleKeyCommand.PAN_LEFT: | 470 case cvox.BrailleKeyCommand.PAN_LEFT: |
| 1039 this.onGotCommand('previousObject'); | 471 CommandHandler.onCommand('previousObject'); |
| 1040 break; | 472 break; |
| 1041 case cvox.BrailleKeyCommand.PAN_RIGHT: | 473 case cvox.BrailleKeyCommand.PAN_RIGHT: |
| 1042 this.onGotCommand('nextObject'); | 474 CommandHandler.onCommand('nextObject'); |
| 1043 break; | 475 break; |
| 1044 case cvox.BrailleKeyCommand.LINE_UP: | 476 case cvox.BrailleKeyCommand.LINE_UP: |
| 1045 this.onGotCommand('previousLine'); | 477 CommandHandler.onCommand('previousLine'); |
| 1046 break; | 478 break; |
| 1047 case cvox.BrailleKeyCommand.LINE_DOWN: | 479 case cvox.BrailleKeyCommand.LINE_DOWN: |
| 1048 this.onGotCommand('nextLine'); | 480 CommandHandler.onCommand('nextLine'); |
| 1049 break; | 481 break; |
| 1050 case cvox.BrailleKeyCommand.TOP: | 482 case cvox.BrailleKeyCommand.TOP: |
| 1051 this.onGotCommand('jumpToTop'); | 483 CommandHandler.onCommand('jumpToTop'); |
| 1052 break; | 484 break; |
| 1053 case cvox.BrailleKeyCommand.BOTTOM: | 485 case cvox.BrailleKeyCommand.BOTTOM: |
| 1054 this.onGotCommand('jumpToBottom'); | 486 CommandHandler.onCommand('jumpToBottom'); |
| 1055 break; | 487 break; |
| 1056 case cvox.BrailleKeyCommand.ROUTING: | 488 case cvox.BrailleKeyCommand.ROUTING: |
| 1057 this.brailleRoutingCommand_( | 489 this.brailleRoutingCommand_( |
| 1058 content.text, | 490 content.text, |
| 1059 // Cast ok since displayPosition is always defined in this case. | 491 // Cast ok since displayPosition is always defined in this case. |
| 1060 /** @type {number} */ (evt.displayPosition)); | 492 /** @type {number} */ (evt.displayPosition)); |
| 1061 break; | 493 break; |
| 1062 default: | 494 default: |
| 1063 return false; | 495 return false; |
| 1064 } | 496 } |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1179 port.postMessage({ | 611 port.postMessage({ |
| 1180 target: 'next', | 612 target: 'next', |
| 1181 isClassicEnabled: isClassicEnabled | 613 isClassicEnabled: isClassicEnabled |
| 1182 }); | 614 }); |
| 1183 } else if (action == 'enableCompatForUrl') { | 615 } else if (action == 'enableCompatForUrl') { |
| 1184 var url = msg['url']; | 616 var url = msg['url']; |
| 1185 this.classicBlacklist_.add(url); | 617 this.classicBlacklist_.add(url); |
| 1186 if (this.currentRange_ && this.currentRange_.start.node) | 618 if (this.currentRange_ && this.currentRange_.start.node) |
| 1187 this.setCurrentRange(this.currentRange_); | 619 this.setCurrentRange(this.currentRange_); |
| 1188 } else if (action == 'onCommand') { | 620 } else if (action == 'onCommand') { |
| 1189 this.onGotCommand(msg['command']); | 621 CommandHandler.onCommand(msg['command']); |
| 1190 } else if (action == 'flushNextUtterance') { | 622 } else if (action == 'flushNextUtterance') { |
| 1191 Output.flushNextSpeechUtterance(); | 623 Output.flushNextSpeechUtterance(); |
| 1192 } | 624 } |
| 1193 break; | 625 break; |
| 1194 } | 626 } |
| 1195 }, | 627 }, |
| 1196 | 628 |
| 1197 /** | 629 /** |
| 1198 * Restore the range to the last range that was *not* in the ChromeVox | 630 * Restore the range to the last range that was *not* in the ChromeVox |
| 1199 * panel. This is used when the ChromeVox Panel closes. | 631 * panel. This is used when the ChromeVox Panel closes. |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1260 // If we're in classic mode, some gestures need to be handled by the | 692 // If we're in classic mode, some gestures need to be handled by the |
| 1261 // content script. Other gestures are universal and will be handled in | 693 // content script. Other gestures are universal and will be handled in |
| 1262 // this function. | 694 // this function. |
| 1263 if (this.mode == ChromeVoxMode.CLASSIC) { | 695 if (this.mode == ChromeVoxMode.CLASSIC) { |
| 1264 if (this.handleClassicGesture_(gesture)) | 696 if (this.handleClassicGesture_(gesture)) |
| 1265 return; | 697 return; |
| 1266 } | 698 } |
| 1267 | 699 |
| 1268 var command = Background.GESTURE_NEXT_COMMAND_MAP[gesture]; | 700 var command = Background.GESTURE_NEXT_COMMAND_MAP[gesture]; |
| 1269 if (command) | 701 if (command) |
| 1270 this.onGotCommand(command); | 702 CommandHandler.onCommand(command); |
| 1271 }, | 703 }, |
| 1272 | 704 |
| 1273 /** | 705 /** |
| 1274 * Handles accessibility gestures from the touch screen when in CLASSIC | 706 * Handles accessibility gestures from the touch screen when in CLASSIC |
| 1275 * mode, by forwarding a command to the content script. | 707 * mode, by forwarding a command to the content script. |
| 1276 * @param {string} gesture The gesture to handle, based on the AXGesture enum | 708 * @param {string} gesture The gesture to handle, based on the AXGesture enum |
| 1277 * defined in ui/accessibility/ax_enums.idl | 709 * defined in ui/accessibility/ax_enums.idl |
| 1278 * @return {boolean} True if this gesture was handled. | 710 * @return {boolean} True if this gesture was handled. |
| 1279 * @private | 711 * @private |
| 1280 */ | 712 */ |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1313 return new RegExp('^(' + globs.map(function(glob) { | 745 return new RegExp('^(' + globs.map(function(glob) { |
| 1314 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') | 746 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') |
| 1315 .replace(/\*/g, '.*') | 747 .replace(/\*/g, '.*') |
| 1316 .replace(/\?/g, '.'); | 748 .replace(/\?/g, '.'); |
| 1317 }).join('|') + ')$'); | 749 }).join('|') + ')$'); |
| 1318 }; | 750 }; |
| 1319 | 751 |
| 1320 new Background(); | 752 new Background(); |
| 1321 | 753 |
| 1322 }); // goog.scope | 754 }); // goog.scope |
| OLD | NEW |