Chromium Code Reviews| Index: chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js |
| diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js |
| index f38f7071b783b22e7fb106e627359b517d0ec1ec..8d9bf04ccbf8871f375aeb163d0f3ebbf3d6a1ca 100644 |
| --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js |
| +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js |
| @@ -75,6 +75,21 @@ Background = function() { |
| */ |
| this.mode_ = ChromeVoxMode.COMPAT; |
| + /** |
| + * The time the last live region event was output. |
| + * @type {Date} |
| + * @private |
| + */ |
| + this.lastLiveRegionTime_ = new Date(0); |
| + |
| + /** |
| + * 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.
|
| + * |this.lastLiveRegionTime_|, to prevent duplicate announcements. |
| + * @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.
|
| + * @private |
| + */ |
| + this.liveRegionNodeSet_ = new WeakSet(); |
| + |
| // Manually bind all functions to |this|. |
| for (var func in this) { |
| if (typeof(this[func]) == 'function') |
| @@ -125,9 +140,18 @@ Background = function() { |
| // Classic keymap. |
| cvox.ChromeVoxKbHandler.handlerKeyMap = cvox.KeyMap.fromDefaults(); |
| + chrome.automation.setTreeChangeObserverMask('liveRegionTreeChanges'); |
| chrome.automation.addTreeChangeObserver(this.onTreeChange); |
| }; |
| +/** |
| + * Live region events received in fewer than this many milliseconds will |
| + * queue, otherwise they'll be output with a category flush. |
| + * @type {number} |
| + * @const |
| + */ |
| +Background.LIVE_REGION_QUEUE_TIME_MS = 500; |
| + |
| Background.prototype = { |
| /** Forces ChromeVox Next to be active for all tabs. */ |
| forceChromeVoxNextActive: function() { |
| @@ -424,6 +448,8 @@ Background.prototype = { |
| evt.preventDefault(); |
| evt.stopPropagation(); |
| } |
| + |
| + Output.flushNextSpeechUtterance(); |
| }, |
| /** |
| @@ -498,22 +524,33 @@ Background.prototype = { |
| * @param {chrome.automation.TreeChange} treeChange |
| */ |
| onTreeChange: function(treeChange) { |
| - if (this.mode_ === ChromeVoxMode.CLASSIC || !cvox.ChromeVox.isActive) |
| - return; |
| - |
| var node = treeChange.target; |
| if (!node.containerLiveStatus) |
| return; |
| - if (node.containerLiveRelevant.indexOf('additions') >= 0 && |
| - treeChange.type == 'nodeCreated') |
| - this.outputLiveRegionChange_(node, null); |
| - if (node.containerLiveRelevant.indexOf('text') >= 0 && |
| - treeChange.type == 'nodeChanged') |
| - this.outputLiveRegionChange_(node, null); |
| - if (node.containerLiveRelevant.indexOf('removals') >= 0 && |
| - treeChange.type == 'nodeRemoved') |
| - this.outputLiveRegionChange_(node, '@live_regions_removed'); |
| + 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.
|
| + |
| + if (this.mode_ === ChromeVoxMode.CLASSIC || !cvox.ChromeVox.isActive) { |
| + skip = true; |
| + } else if (!this.currentRange_) { |
| + skip = true; |
| + } else if (!AutomationUtil.isInSameWebpage( |
| + node, this.currentRange_.start.node)) { |
| + skip = true; |
| + } |
| + |
| + if (!skip) { |
| + var type = treeChange.type; |
| + var relevant = node.containerLiveRelevant; |
| + 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
|
| + (type == 'nodeCreated' || type == 'subtreeCreated')) { |
| + this.outputLiveRegionChange_(node, null); |
| + } |
| + if (relevant.indexOf('text') >= 0 && type == 'nodeChanged') |
| + this.outputLiveRegionChange_(node, null); |
| + if (relevant.indexOf('removals') >= 0 && type == 'nodeRemoved') |
| + this.outputLiveRegionChange_(node, '@live_regions_removed'); |
| + } |
| }, |
| /** |
| @@ -526,13 +563,51 @@ Background.prototype = { |
| * @private |
| */ |
| outputLiveRegionChange_: function(node, opt_prependFormatStr) { |
| + if (node.containerLiveBusy) |
| + return; |
| + |
| + if (node.containerLiveAtomic && !node.liveAtomic) { |
| + if (node.parent) |
| + this.outputLiveRegionChange_(node.parent, opt_prependFormatStr); |
| + return; |
| + } |
| + |
| var range = cursors.Range.fromNode(node); |
| var output = new Output(); |
| if (opt_prependFormatStr) { |
| output.format(opt_prependFormatStr); |
| } |
| - output.withSpeech(range, null, Output.EventType.NAVIGATE); |
| + output.withSpeech(range, range, Output.EventType.NAVIGATE); |
| + |
| + if (output.empty && node.liveAtomic) { |
| + output.format('$descendants', node); |
| + } |
| + |
| output.withSpeechCategory(cvox.TtsCategory.LIVE); |
| + |
| + if (output.empty) |
| + return; |
| + |
| + // Enqueue live region updates that were received at approximately |
| + // the same time, otherwise flush previous live region updates. |
| + var currentTime = new Date(); |
| + var queueTime = Background.LIVE_REGION_QUEUE_TIME_MS; |
| + if (currentTime - this.lastLiveRegionTime_ > queueTime) { |
| + this.liveRegionNodeSet_ = new WeakSet(); |
| + output.withQueueMode(cvox.QueueMode.CATEGORY_FLUSH); |
| + this.lastLiveRegionTime_ = currentTime; |
| + } else { |
| + output.withQueueMode(cvox.QueueMode.QUEUE); |
| + } |
| + |
| + var parent = node; |
| + while (parent) { |
| + if (this.liveRegionNodeSet_.has(parent)) |
| + return; |
| + parent = parent.parent; |
| + } |
| + |
| + this.liveRegionNodeSet_.add(node); |
| output.go(); |
| }, |