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

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

Issue 1295213003: Make cvox2 output feedback more robust when focusing a text field. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@plundblad_fix
Patch Set: Created 5 years, 4 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');
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 /** 58 /**
59 * Which variant of ChromeVox is active. 59 * Which variant of ChromeVox is active.
60 * @type {ChromeVoxMode} 60 * @type {ChromeVoxMode}
61 * @private 61 * @private
62 */ 62 */
63 this.mode_ = ChromeVoxMode.CLASSIC; 63 this.mode_ = ChromeVoxMode.CLASSIC;
64 64
65 /** @type {!ClassicCompatibility} @private */ 65 /** @type {!ClassicCompatibility} @private */
66 this.compat_ = new ClassicCompatibility(); 66 this.compat_ = new ClassicCompatibility();
67 67
68 /** @type {Array<Object>} A queue of incoming events. @private */
69 this.eventQueue_ = [];
Peter Lundblad 2015/08/20 08:30:05 I suggest putting the event queue into its own cla
70
71 /**
72 * @type {?number}
73 * The window.setTimeout timer ID of the event queue timer.
74 * @private
75 */
76 this.eventQueueTimerId_ = null;
Peter Lundblad 2015/08/20 08:30:05 Since your code already relies on timer ids being
77
78 /**
79 * @type {number}
80 * How long to hold events in the event queue before executing them.
81 * @const
82 */
83 this.EVENT_QUEUE_DELAY_MS = 10;
David Tseng 2015/08/19 17:19:25 nit: make private.
Peter Lundblad 2015/08/20 08:30:05 Put constants outside of the constructor, directly
84
68 // Manually bind all functions to |this|. 85 // Manually bind all functions to |this|.
69 for (var func in this) { 86 for (var func in this) {
70 if (typeof(this[func]) == 'function') 87 if (typeof(this[func]) == 'function')
71 this[func] = this[func].bind(this); 88 this[func] = this[func].bind(this);
72 } 89 }
73 90
74 /** 91 /**
75 * Maps an automation event to its listener. 92 * Maps an automation event to its listener.
76 * @type {!Object<EventType, function(Object) : void>} 93 * @type {!Object<EventType, function(Object) : void>}
77 */ 94 */
78 this.listeners_ = { 95 this.listeners_ = {
79 alert: this.onEventDefault, 96 alert: this.onEventDefault,
80 focus: this.onFocus, 97 focus: this.onFocus,
81 hover: this.onEventDefault, 98 hover: this.onEventDefault,
82 loadComplete: this.onLoadComplete, 99 loadComplete: this.onLoadComplete,
83 menuStart: this.onEventDefault, 100 menuStart: this.onEventDefault,
84 menuEnd: this.onEventDefault, 101 menuEnd: this.onEventDefault,
85 menuListValueChanged: this.onEventDefault, 102 menuListValueChanged: this.onEventDefault,
86 textChanged: this.onTextOrTextSelectionChanged, 103 textChanged: this.onTextOrTextSelectionChanged,
87 textSelectionChanged: this.onTextOrTextSelectionChanged, 104 textSelectionChanged: this.onTextOrTextSelectionChanged,
88 valueChanged: this.onValueChanged 105 valueChanged: this.onValueChanged
89 }; 106 };
90 107
108 /**
109 * The object that speaks changes to an editable text field.
110 * @type {?cvox.ChromeVoxEditableTextBase}
Peter Lundblad 2015/08/20 08:30:05 Document when this is null.
111 */
112 this.editableTextHandler_ = null;
113
91 chrome.automation.getDesktop(this.onGotDesktop); 114 chrome.automation.getDesktop(this.onGotDesktop);
92 115
93 // Handle messages directed to the Next background page. 116 // Handle messages directed to the Next background page.
94 cvox.ExtensionBridge.addMessageListener(function(msg, port) { 117 cvox.ExtensionBridge.addMessageListener(function(msg, port) {
95 var target = msg['target']; 118 var target = msg['target'];
96 var action = msg['action']; 119 var action = msg['action'];
97 120
98 switch (target) { 121 switch (target) {
99 case 'next': 122 case 'next':
100 if (action == 'getIsClassicEnabled') { 123 if (action == 'getIsClassicEnabled') {
(...skipping 15 matching lines...) Expand all
116 this.setChromeVoxMode(ChromeVoxMode.FORCE_NEXT); 139 this.setChromeVoxMode(ChromeVoxMode.FORCE_NEXT);
117 }, 140 },
118 141
119 /** 142 /**
120 * Handles all setup once a new automation tree appears. 143 * Handles all setup once a new automation tree appears.
121 * @param {chrome.automation.AutomationNode} desktop 144 * @param {chrome.automation.AutomationNode} desktop
122 */ 145 */
123 onGotDesktop: function(desktop) { 146 onGotDesktop: function(desktop) {
124 // Register all automation event listeners. 147 // Register all automation event listeners.
125 for (var eventType in this.listeners_) 148 for (var eventType in this.listeners_)
126 desktop.addEventListener(eventType, this.listeners_[eventType], true); 149 desktop.addEventListener(eventType, this.addToEventQueue_, true);
127 150
128 // Register a tree change observer. 151 // Register a tree change observer.
129 chrome.automation.addTreeChangeObserver(this.onTreeChange); 152 chrome.automation.addTreeChangeObserver(this.onTreeChange);
130 153
131 // The focused state gets set on the containing webView node. 154 // The focused state gets set on the containing webView node.
132 var webView = desktop.find({role: chrome.automation.RoleType.webView, 155 var webView = desktop.find({role: chrome.automation.RoleType.webView,
133 state: {focused: true}}); 156 state: {focused: true}});
134 if (webView) { 157 if (webView) {
135 var root = webView.find({role: chrome.automation.RoleType.rootWebArea}); 158 var root = webView.find({role: chrome.automation.RoleType.rootWebArea});
136 if (root) { 159 if (root) {
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after
394 case cvox.BrailleKeyCommand.BOTTOM: 417 case cvox.BrailleKeyCommand.BOTTOM:
395 this.onGotCommand('goToEnd', true); 418 this.onGotCommand('goToEnd', true);
396 break; 419 break;
397 default: 420 default:
398 return false; 421 return false;
399 } 422 }
400 return true; 423 return true;
401 }, 424 },
402 425
403 /** 426 /**
427 * Add an event to the event queue so that we can avoid ignore
David Tseng 2015/08/19 17:19:25 avoid or ignore
428 * transient states - for example if focus moves to one node and
429 * then immediately another, or if a node gains focus and
430 * immediately changes its selection.
431 *
432 * @param {Object} evt
433 */
David Tseng 2015/08/19 17:19:25 nit: @private
434 addToEventQueue_: function(evt) {
435 evt.enqueueTime = new Date();
Peter Lundblad 2015/08/20 08:30:05 Should we extend the automation api with a time fi
436 this.eventQueue_.push(evt);
437 if (!this.eventQueueTimerId_) {
438 this.eventQueueTimerId_ = window.setTimeout(
439 this.executeNextEventFromQueue_, this.EVENT_QUEUE_DELAY_MS);
440 }
441 },
442
443 /**
444 * Filter duplicates from the event queue, then pop the first item
445 * off and if it's time to execute it, do so, otherwise set the timer
446 * to wake up again.
David Tseng 2015/08/19 17:19:25 nit: @private
447 */
448 executeNextEventFromQueue_: function() {
Peter Lundblad 2015/08/20 08:30:05 nit: s/execute/handle/ ?
449 this.eventQueueTimerId_ = null;
450
451 if (this.eventQueue_.length == 0) {
452 return;
453 }
David Tseng 2015/08/19 17:19:25 nit: remove braces.
454
455 // Assuming the queue is in increasing order of enqueue time, remove all exc ept the
456 // last item of each type.
457 var lastTimeForEventType = {};
Peter Lundblad 2015/08/20 08:30:05 Why not just store the object reference instead of
458 this.eventQueue_.forEach(function(evt) {
459 lastTimeForEventType[evt.type] = evt.enqueueTime;
460 });
461 this.eventQueue_ = this.eventQueue_.filter(function(evt) {
462 return evt.enqueueTime == lastTimeForEventType[evt.type];
David Tseng 2015/08/19 17:19:25 Any issues doing this generically? For example, do
463 });
464
465 while (this.eventQueue_.length) {
466 var now = new Date();
467 var delta = now - this.eventQueue_[0].enqueueTime;
468 if (delta < this.EVENT_QUEUE_DELAY_MS) {
469 this.eventQueueTimerId_ = window.setTimeout(
470 this.executeNextEventFromQueue_, this.EVENT_QUEUE_DELAY_MS - delta);
471 return;
472 }
473
474 var evt = this.eventQueue_.shift();
475 this.listeners_[evt.type](evt);
476 }
477 },
478
479 /**
404 * Provides all feedback once ChromeVox's focus changes. 480 * Provides all feedback once ChromeVox's focus changes.
405 * @param {Object} evt 481 * @param {Object} evt
406 */ 482 */
407 onEventDefault: function(evt) { 483 onEventDefault: function(evt) {
408 var node = evt.target; 484 var node = evt.target;
409 485
410 if (!node) 486 if (!node)
411 return; 487 return;
412 488
413 var prevRange = this.currentRange_; 489 var prevRange = this.currentRange_;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
447 onFocus: function(evt) { 523 onFocus: function(evt) {
448 var node = evt.target; 524 var node = evt.target;
449 525
450 // It doesn't make sense to focus the containing web area if a descendant 526 // It doesn't make sense to focus the containing web area if a descendant
451 // has focused state. 527 // has focused state.
452 if (node.role == 'rootWebArea') { 528 if (node.role == 'rootWebArea') {
453 node = AutomationUtil.findNodePost(node, 529 node = AutomationUtil.findNodePost(node,
454 Dir.FORWARD, 530 Dir.FORWARD,
455 AutomationPredicate.focused) || node; 531 AutomationPredicate.focused) || node;
456 } 532 }
533
534 if (evt.target.role == 'textField' || evt.target.role == 'textBox') {
David Tseng 2015/08/19 17:19:25 nit: please use RoleType.textField and RoleType.te
535 this.createEditableTextHandlerIfNeeded_(evt.target);
536 }
537
457 this.onEventDefault({target: node, type: 'focus'}); 538 this.onEventDefault({target: node, type: 'focus'});
458 }, 539 },
459 540
460 /** 541 /**
461 * Provides all feedback once a load complete event fires. 542 * Provides all feedback once a load complete event fires.
462 * @param {Object} evt 543 * @param {Object} evt
463 */ 544 */
464 onLoadComplete: function(evt) { 545 onLoadComplete: function(evt) {
465 this.setupChromeVoxVariants_(evt.target.docUrl); 546 this.setupChromeVoxVariants_(evt.target.docUrl);
466 547
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
504 return; 585 return;
505 586
506 if (!evt.target.state.focused) 587 if (!evt.target.state.focused)
507 return; 588 return;
508 589
509 if (!this.currentRange_) { 590 if (!this.currentRange_) {
510 this.onEventDefault(evt); 591 this.onEventDefault(evt);
511 this.currentRange_ = cursors.Range.fromNode(evt.target); 592 this.currentRange_ = cursors.Range.fromNode(evt.target);
512 } 593 }
513 594
595 this.createEditableTextHandlerIfNeeded_(evt.target);
514 var textChangeEvent = new cvox.TextChangeEvent( 596 var textChangeEvent = new cvox.TextChangeEvent(
515 evt.target.value, 597 evt.target.value,
516 evt.target.textSelStart, 598 evt.target.textSelStart,
517 evt.target.textSelEnd, 599 evt.target.textSelEnd,
518 true); // triggered by user 600 true); // triggered by user
519 if (!this.editableTextHandler || 601 this.editableTextHandler_.changed(textChangeEvent);
520 evt.target != this.currentRange_.start.node) {
521 this.editableTextHandler =
522 new cvox.ChromeVoxEditableTextBase(
523 textChangeEvent.value,
524 textChangeEvent.start,
525 textChangeEvent.end,
526 evt.target.state['protected'],
527 cvox.ChromeVox.tts);
528 }
529 602
530 this.editableTextHandler.changed(textChangeEvent);
531 new Output().withBraille( 603 new Output().withBraille(
532 this.currentRange_, null, evt.type) 604 this.currentRange_, null, evt.type)
533 .go(); 605 .go();
534 }, 606 },
535 607
536 /** 608 /**
537 * Provides all feedback once a value changed event fires. 609 * Provides all feedback once a value changed event fires.
538 * @param {Object} evt 610 * @param {Object} evt
539 */ 611 */
540 onValueChanged: function(evt) { 612 onValueChanged: function(evt) {
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
682 // When in compat mode, if the focus is within the desktop tree proper, 754 // When in compat mode, if the focus is within the desktop tree proper,
683 // then do not disable content scripts. 755 // then do not disable content scripts.
684 if (this.currentRange_.start.node.root.role == 'desktop') 756 if (this.currentRange_.start.node.root.role == 'desktop')
685 return; 757 return;
686 758
687 this.disableClassicChromeVox_(); 759 this.disableClassicChromeVox_();
688 } 760 }
689 }.bind(this)); 761 }.bind(this));
690 762
691 this.mode_ = mode; 763 this.mode_ = mode;
692 } 764 },
765
766 /**
767 * Create an editable text handler for the given node if needed.
768 * @param {Object} node
David Tseng 2015/08/19 17:19:25 s/Object/AutomationNode
769 */
David Tseng 2015/08/19 17:19:25 @private
770 createEditableTextHandlerIfNeeded_: function(node) {
771 if (!this.editableTextHandler_ || node != this.currentRange_.start.node) {
772 this.editableTextHandler_ =
773 new cvox.ChromeVoxEditableTextBase(
774 node.value,
775 node.textSelStart,
776 node.textSelEnd,
777 node.state['protected'],
778 cvox.ChromeVox.tts);
779 }
780 },
693 }; 781 };
694 782
695 /** @type {Background} */ 783 /** @type {Background} */
696 global.backgroundObj = new Background(); 784 global.backgroundObj = new Background();
697 785
698 }); // goog.scope 786 }); // goog.scope
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698