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

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions.js

Issue 2939273002: DO NOT SUBMIT: what chrome/browser/resources/ could eventually look like with clang-format (Closed)
Patch Set: Created 3 years, 6 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 Keeps track of live regions on the page and speaks updates 6 * @fileoverview Keeps track of live regions on the page and speaks updates
7 * when they change. 7 * when they change.
8 * 8 *
9 */ 9 */
10 10
11 goog.provide('cvox.LiveRegions'); 11 goog.provide('cvox.LiveRegions');
12 12
13 goog.require('cvox.AriaUtil'); 13 goog.require('cvox.AriaUtil');
14 goog.require('cvox.ChromeVox'); 14 goog.require('cvox.ChromeVox');
15 goog.require('cvox.DescriptionUtil'); 15 goog.require('cvox.DescriptionUtil');
16 goog.require('cvox.DomUtil'); 16 goog.require('cvox.DomUtil');
17 goog.require('cvox.Interframe'); 17 goog.require('cvox.Interframe');
18 goog.require('cvox.NavDescription'); 18 goog.require('cvox.NavDescription');
19 goog.require('cvox.NavigationSpeaker'); 19 goog.require('cvox.NavigationSpeaker');
20 20
21 /** 21 /**
22 * @constructor 22 * @constructor
23 */ 23 */
24 cvox.LiveRegions = function() { 24 cvox.LiveRegions = function() {};
25 };
26 25
27 /** 26 /**
28 * @type {Date} 27 * @type {Date}
29 */ 28 */
30 cvox.LiveRegions.pageLoadTime = null; 29 cvox.LiveRegions.pageLoadTime = null;
31 30
32 /** 31 /**
33 * Time in milliseconds after initial page load to ignore live region 32 * Time in milliseconds after initial page load to ignore live region
34 * updates, to avoid announcing regions as they're initially created. 33 * updates, to avoid announcing regions as they're initially created.
35 * The exception is alerts, they're announced when a page is loaded. 34 * The exception is alerts, they're announced when a page is loaded.
(...skipping 19 matching lines...) Expand all
55 54
56 /** 55 /**
57 * Maximum time interval in which to discard duplicate live region announcement. 56 * Maximum time interval in which to discard duplicate live region announcement.
58 * @type {number} 57 * @type {number}
59 * @const 58 * @const
60 */ 59 */
61 cvox.LiveRegions.MAX_DISCARD_DUPS_MS = 2000; 60 cvox.LiveRegions.MAX_DISCARD_DUPS_MS = 2000;
62 61
63 /** 62 /**
64 * @type {Date} 63 * @type {Date}
65 */ 64 */
66 cvox.LiveRegions.lastAnnouncedTime = null; 65 cvox.LiveRegions.lastAnnouncedTime = null;
67 66
68 /** 67 /**
69 * Tracks nodes handled during mutation processing. 68 * Tracks nodes handled during mutation processing.
70 * @type {!Array<Node>} 69 * @type {!Array<Node>}
71 */ 70 */
72 cvox.LiveRegions.nodesAlreadyHandled = []; 71 cvox.LiveRegions.nodesAlreadyHandled = [];
73 72
74 /** 73 /**
75 * @param {Date} pageLoadTime The time the page was loaded. Live region 74 * @param {Date} pageLoadTime The time the page was loaded. Live region
76 * updates within the first INITIAL_SILENCE_MS milliseconds are ignored. 75 * updates within the first INITIAL_SILENCE_MS milliseconds are ignored.
77 * @param {cvox.QueueMode} queueMode Interrupt or flush. Polite live region 76 * @param {cvox.QueueMode} queueMode Interrupt or flush. Polite live region
78 * changes always queue. 77 * changes always queue.
79 * @param {boolean} disableSpeak true if change announcement should be disabled. 78 * @param {boolean} disableSpeak true if change announcement should be disabled.
80 * @return {boolean} true if any regions announced. 79 * @return {boolean} true if any regions announced.
81 */ 80 */
82 cvox.LiveRegions.init = function(pageLoadTime, queueMode, disableSpeak) { 81 cvox.LiveRegions.init = function(pageLoadTime, queueMode, disableSpeak) {
83 cvox.LiveRegions.pageLoadTime = pageLoadTime; 82 cvox.LiveRegions.pageLoadTime = pageLoadTime;
84 83
85 if (disableSpeak || !cvox.ChromeVox.documentHasFocus()) { 84 if (disableSpeak || !cvox.ChromeVox.documentHasFocus()) {
86 return false; 85 return false;
87 } 86 }
88 87
89 // Speak any live regions already on the page. The logic below will 88 // Speak any live regions already on the page. The logic below will
90 // make sure that only alerts are actually announced. 89 // make sure that only alerts are actually announced.
91 var anyRegionsAnnounced = false; 90 var anyRegionsAnnounced = false;
92 var regions = cvox.AriaUtil.getLiveRegions(document.body); 91 var regions = cvox.AriaUtil.getLiveRegions(document.body);
93 for (var i = 0; i < regions.length; i++) { 92 for (var i = 0; i < regions.length; i++) {
94 cvox.LiveRegions.handleOneChangedNode( 93 cvox.LiveRegions.handleOneChangedNode(
95 regions[i], 94 regions[i], regions[i], false, false,
96 regions[i],
97 false,
98 false,
99 function(assertive, navDescriptions) { 95 function(assertive, navDescriptions) {
100 if (!assertive && queueMode == cvox.QueueMode.FLUSH) { 96 if (!assertive && queueMode == cvox.QueueMode.FLUSH) {
101 queueMode = cvox.QueueMode.QUEUE; 97 queueMode = cvox.QueueMode.QUEUE;
102 } 98 }
103 var descSpeaker = new cvox.NavigationSpeaker(); 99 var descSpeaker = new cvox.NavigationSpeaker();
104 descSpeaker.speakDescriptionArray(navDescriptions, queueMode, null); 100 descSpeaker.speakDescriptionArray(navDescriptions, queueMode, null);
105 anyRegionsAnnounced = true; 101 anyRegionsAnnounced = true;
106 }); 102 });
107 } 103 }
108 104
109 cvox.Interframe.addListener(function(message) { 105 cvox.Interframe.addListener(function(message) {
110 if (message['command'] != 'speakLiveRegion') { 106 if (message['command'] != 'speakLiveRegion') {
111 return; 107 return;
112 } 108 }
113 var iframes = document.getElementsByTagName('iframe'); 109 var iframes = document.getElementsByTagName('iframe');
114 for (var i = 0, iframe; iframe = iframes[i]; i++) { 110 for (var i = 0, iframe; iframe = iframes[i]; i++) {
115 if (iframe.src == message['src']) { 111 if (iframe.src == message['src']) {
116 if (!cvox.DomUtil.isVisible(iframe)) { 112 if (!cvox.DomUtil.isVisible(iframe)) {
117 return; 113 return;
118 } 114 }
119 var structs = JSON.parse(message['content']); 115 var structs = JSON.parse(message['content']);
120 var descriptions = []; 116 var descriptions = [];
121 for (var j = 0, description; description = structs[j]; j++) { 117 for (var j = 0, description; description = structs[j]; j++) {
122 descriptions.push(new cvox.NavDescription(description)); 118 descriptions.push(new cvox.NavDescription(description));
123 } 119 }
124 new cvox.NavigationSpeaker() 120 new cvox.NavigationSpeaker().speakDescriptionArray(
125 .speakDescriptionArray(descriptions, message['queueMode'], null); 121 descriptions, message['queueMode'], null);
126 } 122 }
127 } 123 }
128 }); 124 });
129 125
130 return anyRegionsAnnounced; 126 return anyRegionsAnnounced;
131 }; 127 };
132 128
133 /** 129 /**
134 * See if any mutations pertain to a live region, and speak them if so. 130 * See if any mutations pertain to a live region, and speak them if so.
135 * 131 *
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after
303 299
304 /** 300 /**
305 * Announce one node within a live region. 301 * Announce one node within a live region.
306 * 302 *
307 * @param {Node} node A node in a live region. 303 * @param {Node} node A node in a live region.
308 * @param {Node} liveRoot The root of the live region this node is in. 304 * @param {Node} liveRoot The root of the live region this node is in.
309 * @param {boolean} isRemoval True if this node was removed. 305 * @param {boolean} isRemoval True if this node was removed.
310 * @param {function(boolean, Array<cvox.NavDescription>)} handler 306 * @param {function(boolean, Array<cvox.NavDescription>)} handler
311 * Callback function to be called for each live region found. 307 * Callback function to be called for each live region found.
312 */ 308 */
313 cvox.LiveRegions.announceChange = function( 309 cvox.LiveRegions.announceChange = function(node, liveRoot, isRemoval, handler) {
314 node, liveRoot, isRemoval, handler) {
315 // If this node is in an atomic container, announce the whole container. 310 // If this node is in an atomic container, announce the whole container.
316 // This includes aria-atomic, but also ARIA controls and other nodes 311 // This includes aria-atomic, but also ARIA controls and other nodes
317 // whose ARIA roles make them leaves. 312 // whose ARIA roles make them leaves.
318 if (node != liveRoot) { 313 if (node != liveRoot) {
319 var atomicContainer = node.parentElement; 314 var atomicContainer = node.parentElement;
320 while (atomicContainer) { 315 while (atomicContainer) {
321 if ((cvox.AriaUtil.getAriaAtomic(atomicContainer) || 316 if ((cvox.AriaUtil.getAriaAtomic(atomicContainer) ||
322 cvox.AriaUtil.isLeafElement(atomicContainer) || 317 cvox.AriaUtil.isLeafElement(atomicContainer) ||
323 cvox.AriaUtil.isControlWidget(atomicContainer)) && 318 cvox.AriaUtil.isControlWidget(atomicContainer)) &&
324 !cvox.AriaUtil.isCompositeControl(atomicContainer)) { 319 !cvox.AriaUtil.isCompositeControl(atomicContainer)) {
325 node = atomicContainer; 320 node = atomicContainer;
326 } 321 }
327 if (atomicContainer == liveRoot) { 322 if (atomicContainer == liveRoot) {
328 break; 323 break;
329 } 324 }
330 atomicContainer = atomicContainer.parentElement; 325 atomicContainer = atomicContainer.parentElement;
331 } 326 }
332 } 327 }
333 328
334 var navDescriptions = cvox.LiveRegions.getNavDescriptionsRecursive(node); 329 var navDescriptions = cvox.LiveRegions.getNavDescriptionsRecursive(node);
335 if (isRemoval) { 330 if (isRemoval) {
336 navDescriptions = [cvox.DescriptionUtil.getDescriptionFromAncestors( 331 navDescriptions = [cvox.DescriptionUtil.getDescriptionFromAncestors(
337 [node], true, cvox.ChromeVox.verbosity)]; 332 [node], true, cvox.ChromeVox.verbosity)];
338 navDescriptions = [new cvox.NavDescription({ 333 navDescriptions = [
339 context: Msgs.getMsg('live_regions_removed'), text: '' 334 new cvox.NavDescription(
340 })].concat(navDescriptions); 335 {context: Msgs.getMsg('live_regions_removed'), text: ''})
336 ].concat(navDescriptions);
341 } 337 }
342 338
343 if (navDescriptions.length == 0) { 339 if (navDescriptions.length == 0) {
344 return; 340 return;
345 } 341 }
346 342
347 // Don't announce alerts on page load if their text and values consist of 343 // Don't announce alerts on page load if their text and values consist of
348 // just whitespace. 344 // just whitespace.
349 var deltaTime = new Date() - cvox.LiveRegions.pageLoadTime; 345 var deltaTime = new Date() - cvox.LiveRegions.pageLoadTime;
350 if (cvox.AriaUtil.getRoleAttribute(liveRoot) == 'alert' && 346 if (cvox.AriaUtil.getRoleAttribute(liveRoot) == 'alert' &&
(...skipping 22 matching lines...) Expand all
373 return prev + '|' + navDescription.text; 369 return prev + '|' + navDescription.text;
374 }, ''); 370 }, '');
375 371
376 if (cvox.LiveRegions.lastAnnouncedMap[key]) { 372 if (cvox.LiveRegions.lastAnnouncedMap[key]) {
377 return; 373 return;
378 } 374 }
379 cvox.LiveRegions.lastAnnouncedMap[key] = now; 375 cvox.LiveRegions.lastAnnouncedMap[key] = now;
380 376
381 var assertive = cvox.AriaUtil.getAriaLive(liveRoot) == 'assertive'; 377 var assertive = cvox.AriaUtil.getAriaLive(liveRoot) == 'assertive';
382 if (cvox.Interframe.isIframe() && !cvox.ChromeVox.documentHasFocus()) { 378 if (cvox.Interframe.isIframe() && !cvox.ChromeVox.documentHasFocus()) {
383 cvox.Interframe.sendMessageToParentWindow( 379 cvox.Interframe.sendMessageToParentWindow({
384 {'command': 'speakLiveRegion', 380 'command': 'speakLiveRegion',
385 'content': JSON.stringify(navDescriptions), 381 'content': JSON.stringify(navDescriptions),
386 'queueMode': assertive ? 0 : 1, 382 'queueMode': assertive ? 0 : 1,
387 'src': window.location.href } 383 'src': window.location.href
388 ); 384 });
389 return; 385 return;
390 } 386 }
391 387
392 // Set a category on the NavDescriptions - that way live regions 388 // Set a category on the NavDescriptions - that way live regions
393 // interrupt other live regions but not anything else. 389 // interrupt other live regions but not anything else.
394 navDescriptions.forEach(function(desc) { 390 navDescriptions.forEach(function(desc) {
395 if (!desc.category) { 391 if (!desc.category) {
396 desc.category = cvox.TtsCategory.LIVE; 392 desc.category = cvox.TtsCategory.LIVE;
397 } 393 }
398 }); 394 });
399 395
400 // TODO(dmazzoni): http://crbug.com/415679 Temporary design decision; 396 // TODO(dmazzoni): http://crbug.com/415679 Temporary design decision;
401 // until we have a way to tell the speech queue to group the nav 397 // until we have a way to tell the speech queue to group the nav
402 // descriptions together, collapse them into one. 398 // descriptions together, collapse them into one.
403 // Otherwise, one nav description could be spoken, then something unrelated, 399 // Otherwise, one nav description could be spoken, then something unrelated,
404 // then the rest. 400 // then the rest.
405 if (navDescriptions.length > 1) { 401 if (navDescriptions.length > 1) {
406 var allStrings = []; 402 var allStrings = [];
407 navDescriptions.forEach(function(desc) { 403 navDescriptions.forEach(function(desc) {
408 if (desc.context) { 404 if (desc.context) {
409 allStrings.push(desc.context); 405 allStrings.push(desc.context);
410 } 406 }
411 if (desc.text) { 407 if (desc.text) {
412 allStrings.push(desc.text); 408 allStrings.push(desc.text);
413 } 409 }
414 if (desc.userValue) { 410 if (desc.userValue) {
415 allStrings.push(desc.userValue); 411 allStrings.push(desc.userValue);
416 } 412 }
417 }); 413 });
418 navDescriptions = [new cvox.NavDescription({ 414 navDescriptions = [new cvox.NavDescription(
419 text: allStrings.join(', '), 415 {text: allStrings.join(', '), category: cvox.TtsCategory.LIVE})];
420 category: cvox.TtsCategory.LIVE
421 })];
422 } 416 }
423 417
424 handler(assertive, navDescriptions); 418 handler(assertive, navDescriptions);
425 }; 419 };
426 420
427 /** 421 /**
428 * Recursively build up the value of a live region and return it as 422 * Recursively build up the value of a live region and return it as
429 * an array of NavDescriptions. Each atomic portion of the region gets a 423 * an array of NavDescriptions. Each atomic portion of the region gets a
430 * single string, otherwise each leaf node gets its own string. 424 * single string, otherwise each leaf node gets its own string.
431 * 425 *
432 * @param {Node} node A node in a live region. 426 * @param {Node} node A node in a live region.
433 * @return {Array<cvox.NavDescription>} An array of NavDescriptions 427 * @return {Array<cvox.NavDescription>} An array of NavDescriptions
434 * describing atomic nodes or leaf nodes in the subtree rooted 428 * describing atomic nodes or leaf nodes in the subtree rooted
435 * at this node. 429 * at this node.
436 */ 430 */
437 cvox.LiveRegions.getNavDescriptionsRecursive = function(node) { 431 cvox.LiveRegions.getNavDescriptionsRecursive = function(node) {
438 if (cvox.AriaUtil.getAriaAtomic(node) || 432 if (cvox.AriaUtil.getAriaAtomic(node) || cvox.DomUtil.isLeafNode(node)) {
439 cvox.DomUtil.isLeafNode(node)) {
440 var description = cvox.DescriptionUtil.getDescriptionFromAncestors( 433 var description = cvox.DescriptionUtil.getDescriptionFromAncestors(
441 [node], true, cvox.ChromeVox.verbosity); 434 [node], true, cvox.ChromeVox.verbosity);
442 if (!description.isEmpty()) { 435 if (!description.isEmpty()) {
443 return [description]; 436 return [description];
444 } else { 437 } else {
445 return []; 438 return [];
446 } 439 }
447 } 440 }
448 return cvox.DescriptionUtil.getFullDescriptionsFromChildren(null, 441 return cvox.DescriptionUtil.getFullDescriptionsFromChildren(
442 null,
449 /** @type {!Element} */ (node)); 443 /** @type {!Element} */ (node));
450 }; 444 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698