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

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

Issue 1457683009: Complete live region support in ChromeVox Next. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 1 month 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 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
68 */ 68 */
69 this.currentRange_ = null; 69 this.currentRange_ = null;
70 70
71 /** 71 /**
72 * Which variant of ChromeVox is active. 72 * Which variant of ChromeVox is active.
73 * @type {ChromeVoxMode} 73 * @type {ChromeVoxMode}
74 * @private 74 * @private
75 */ 75 */
76 this.mode_ = ChromeVoxMode.COMPAT; 76 this.mode_ = ChromeVoxMode.COMPAT;
77 77
78 /**
79 * The time the last live region event was output.
80 * @type {Date}
81 * @private
82 */
83 this.lastLiveRegionTime_ = new Date(0);
84
85 /**
86 * Ids of nodes that have been announced as part of a live region since
Peter Lundblad 2015/11/20 13:42:58 nit: there are no ids stored in the set.
dmazzoni 2015/11/23 20:16:50 Done.
87 * |this.lastLiveRegionTime_|, to prevent duplicate announcements.
88 * @type {WeakSet}
Peter Lundblad 2015/11/20 13:42:58 {!WeakSet} Does the compiler support {!WeakSet<Aut
dmazzoni 2015/11/23 20:16:50 It worked! Thanks.
89 * @private
90 */
91 this.liveRegionNodeSet_ = new WeakSet();
92
78 // Manually bind all functions to |this|. 93 // Manually bind all functions to |this|.
79 for (var func in this) { 94 for (var func in this) {
80 if (typeof(this[func]) == 'function') 95 if (typeof(this[func]) == 'function')
81 this[func] = this[func].bind(this); 96 this[func] = this[func].bind(this);
82 } 97 }
83 98
84 /** @type {!cvox.AbstractEarcons} @private */ 99 /** @type {!cvox.AbstractEarcons} @private */
85 this.classicEarcons_ = cvox.ChromeVox.earcons || new cvox.ClassicEarcons(); 100 this.classicEarcons_ = cvox.ChromeVox.earcons || new cvox.ClassicEarcons();
86 101
87 /** @type {!cvox.AbstractEarcons} @private */ 102 /** @type {!cvox.AbstractEarcons} @private */
(...skipping 30 matching lines...) Expand all
118 }); 133 });
119 134
120 cvox.ExtensionBridge.addMessageListener(this.onMessage_); 135 cvox.ExtensionBridge.addMessageListener(this.onMessage_);
121 136
122 document.addEventListener('keydown', this.onKeyDown.bind(this), true); 137 document.addEventListener('keydown', this.onKeyDown.bind(this), true);
123 cvox.ChromeVoxKbHandler.commandHandler = this.onGotCommand.bind(this); 138 cvox.ChromeVoxKbHandler.commandHandler = this.onGotCommand.bind(this);
124 139
125 // Classic keymap. 140 // Classic keymap.
126 cvox.ChromeVoxKbHandler.handlerKeyMap = cvox.KeyMap.fromDefaults(); 141 cvox.ChromeVoxKbHandler.handlerKeyMap = cvox.KeyMap.fromDefaults();
127 142
143 chrome.automation.setTreeChangeObserverMask('liveRegionTreeChanges');
128 chrome.automation.addTreeChangeObserver(this.onTreeChange); 144 chrome.automation.addTreeChangeObserver(this.onTreeChange);
129 }; 145 };
130 146
147 /**
148 * Live region events received in fewer than this many milliseconds will
149 * queue, otherwise they'll be output with a category flush.
150 * @type {number}
151 * @const
152 */
153 Background.LIVE_REGION_QUEUE_TIME_MS = 500;
154
131 Background.prototype = { 155 Background.prototype = {
132 /** Forces ChromeVox Next to be active for all tabs. */ 156 /** Forces ChromeVox Next to be active for all tabs. */
133 forceChromeVoxNextActive: function() { 157 forceChromeVoxNextActive: function() {
134 this.setChromeVoxMode(ChromeVoxMode.FORCE_NEXT); 158 this.setChromeVoxMode(ChromeVoxMode.FORCE_NEXT);
135 }, 159 },
136 160
137 /** @type {ChromeVoxMode} */ 161 /** @type {ChromeVoxMode} */
138 get mode() { 162 get mode() {
139 return this.mode_; 163 return this.mode_;
140 }, 164 },
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 * Handles key down events. 441 * Handles key down events.
418 * @param {Event} evt The key down event to process. 442 * @param {Event} evt The key down event to process.
419 * @return {boolean} True if the default action should be performed. 443 * @return {boolean} True if the default action should be performed.
420 */ 444 */
421 onKeyDown: function(evt) { 445 onKeyDown: function(evt) {
422 if (this.mode_ != ChromeVoxMode.CLASSIC && 446 if (this.mode_ != ChromeVoxMode.CLASSIC &&
423 !cvox.ChromeVoxKbHandler.basicKeyDownActionsListener(evt)) { 447 !cvox.ChromeVoxKbHandler.basicKeyDownActionsListener(evt)) {
424 evt.preventDefault(); 448 evt.preventDefault();
425 evt.stopPropagation(); 449 evt.stopPropagation();
426 } 450 }
451
452 Output.flushNextSpeechUtterance();
427 }, 453 },
428 454
429 /** 455 /**
430 * Open the options page in a new tab. 456 * Open the options page in a new tab.
431 */ 457 */
432 showOptionsPage: function() { 458 showOptionsPage: function() {
433 var optionsPage = {url: 'chromevox/background/options.html'}; 459 var optionsPage = {url: 'chromevox/background/options.html'};
434 chrome.tabs.create(optionsPage); 460 chrome.tabs.create(optionsPage);
435 }, 461 },
436 462
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
491 } 517 }
492 518
493 this.setChromeVoxMode(mode); 519 this.setChromeVoxMode(mode);
494 }, 520 },
495 521
496 /** 522 /**
497 * Called when the automation tree is changed. 523 * Called when the automation tree is changed.
498 * @param {chrome.automation.TreeChange} treeChange 524 * @param {chrome.automation.TreeChange} treeChange
499 */ 525 */
500 onTreeChange: function(treeChange) { 526 onTreeChange: function(treeChange) {
501 if (this.mode_ === ChromeVoxMode.CLASSIC || !cvox.ChromeVox.isActive)
502 return;
503
504 var node = treeChange.target; 527 var node = treeChange.target;
505 if (!node.containerLiveStatus) 528 if (!node.containerLiveStatus)
506 return; 529 return;
507 530
508 if (node.containerLiveRelevant.indexOf('additions') >= 0 && 531 var skip = false;
Peter Lundblad 2015/11/20 13:42:58 Get rid of this and return early instead?
dmazzoni 2015/11/23 20:16:50 Done.
509 treeChange.type == 'nodeCreated') 532
510 this.outputLiveRegionChange_(node, null); 533 if (this.mode_ === ChromeVoxMode.CLASSIC || !cvox.ChromeVox.isActive) {
511 if (node.containerLiveRelevant.indexOf('text') >= 0 && 534 skip = true;
512 treeChange.type == 'nodeChanged') 535 } else if (!this.currentRange_) {
513 this.outputLiveRegionChange_(node, null); 536 skip = true;
514 if (node.containerLiveRelevant.indexOf('removals') >= 0 && 537 } else if (!AutomationUtil.isInSameWebpage(
515 treeChange.type == 'nodeRemoved') 538 node, this.currentRange_.start.node)) {
516 this.outputLiveRegionChange_(node, '@live_regions_removed'); 539 skip = true;
540 }
541
542 if (!skip) {
543 var type = treeChange.type;
544 var relevant = node.containerLiveRelevant;
545 if (relevant.indexOf('additions') >= 0 &&
David Tseng 2015/11/24 18:46:30 Where do relevant strings come from? This looks pr
dmazzoni 2015/11/30 22:02:47 This is from the ARIA spec, I'm not sure we should
546 (type == 'nodeCreated' || type == 'subtreeCreated')) {
547 this.outputLiveRegionChange_(node, null);
548 }
549 if (relevant.indexOf('text') >= 0 && type == 'nodeChanged')
550 this.outputLiveRegionChange_(node, null);
551 if (relevant.indexOf('removals') >= 0 && type == 'nodeRemoved')
552 this.outputLiveRegionChange_(node, '@live_regions_removed');
553 }
517 }, 554 },
518 555
519 /** 556 /**
520 * Given a node that needs to be spoken as part of a live region 557 * Given a node that needs to be spoken as part of a live region
521 * change and an additional optional format string, output the 558 * change and an additional optional format string, output the
522 * live region description. 559 * live region description.
523 * @param {!chrome.automation.AutomationNode} node The changed node. 560 * @param {!chrome.automation.AutomationNode} node The changed node.
524 * @param {?string} opt_prependFormatStr If set, a format string for 561 * @param {?string} opt_prependFormatStr If set, a format string for
525 * cvox2.Output to prepend to the output. 562 * cvox2.Output to prepend to the output.
526 * @private 563 * @private
527 */ 564 */
528 outputLiveRegionChange_: function(node, opt_prependFormatStr) { 565 outputLiveRegionChange_: function(node, opt_prependFormatStr) {
566 if (node.containerLiveBusy)
567 return;
568
569 if (node.containerLiveAtomic && !node.liveAtomic) {
570 if (node.parent)
571 this.outputLiveRegionChange_(node.parent, opt_prependFormatStr);
572 return;
573 }
574
529 var range = cursors.Range.fromNode(node); 575 var range = cursors.Range.fromNode(node);
530 var output = new Output(); 576 var output = new Output();
531 if (opt_prependFormatStr) { 577 if (opt_prependFormatStr) {
532 output.format(opt_prependFormatStr); 578 output.format(opt_prependFormatStr);
533 } 579 }
534 output.withSpeech(range, null, Output.EventType.NAVIGATE); 580 output.withSpeech(range, range, Output.EventType.NAVIGATE);
581
582 if (output.empty && node.liveAtomic) {
583 output.format('$descendants', node);
584 }
585
535 output.withSpeechCategory(cvox.TtsCategory.LIVE); 586 output.withSpeechCategory(cvox.TtsCategory.LIVE);
587
588 if (output.empty)
589 return;
590
591 // Enqueue live region updates that were received at approximately
592 // the same time, otherwise flush previous live region updates.
593 var currentTime = new Date();
594 var queueTime = Background.LIVE_REGION_QUEUE_TIME_MS;
595 if (currentTime - this.lastLiveRegionTime_ > queueTime) {
596 this.liveRegionNodeSet_ = new WeakSet();
597 output.withQueueMode(cvox.QueueMode.CATEGORY_FLUSH);
598 this.lastLiveRegionTime_ = currentTime;
599 } else {
600 output.withQueueMode(cvox.QueueMode.QUEUE);
601 }
602
603 var parent = node;
604 while (parent) {
605 if (this.liveRegionNodeSet_.has(parent))
606 return;
607 parent = parent.parent;
608 }
609
610 this.liveRegionNodeSet_.add(node);
536 output.go(); 611 output.go();
537 }, 612 },
538 613
539 /** 614 /**
540 * Returns true if the url should have Classic running. 615 * Returns true if the url should have Classic running.
541 * @return {boolean} 616 * @return {boolean}
542 * @private 617 * @private
543 */ 618 */
544 shouldEnableClassicForUrl_: function(url) { 619 shouldEnableClassicForUrl_: function(url) {
545 return this.mode_ != ChromeVoxMode.FORCE_NEXT && 620 return this.mode_ != ChromeVoxMode.FORCE_NEXT &&
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
686 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&') 761 return glob.replace(/[.+^$(){}|[\]\\]/g, '\\$&')
687 .replace(/\*/g, '.*') 762 .replace(/\*/g, '.*')
688 .replace(/\?/g, '.'); 763 .replace(/\?/g, '.');
689 }).join('|') + ')$'); 764 }).join('|') + ')$');
690 }; 765 };
691 766
692 /** @type {Background} */ 767 /** @type {Background} */
693 global.backgroundObj = new Background(); 768 global.backgroundObj = new Background();
694 769
695 }); // goog.scope 770 }); // goog.scope
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698