Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(477)

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js

Issue 2150623002: Refactor: Extract a KeyboardHandler and CommandHandler from Background (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix test. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698