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

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

Issue 730053004: Add speech output rules for most of the remaining native views for ChromeVox Next. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@space_command
Patch Set: Rebase Created 6 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
« no previous file with comments | « chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_predicate.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 Provides output services for ChromeVox. 6 * @fileoverview Provides output services for ChromeVox.
7 */ 7 */
8 8
9 goog.provide('Output'); 9 goog.provide('Output');
10 goog.provide('Output.EventType'); 10 goog.provide('Output.EventType');
(...skipping 26 matching lines...) Expand all
37 * $text_sel_end'). 37 * $text_sel_end').
38 * = suffix: used to specify substitution only if not previously appended. 38 * = suffix: used to specify substitution only if not previously appended.
39 * For example, $name= would insert the name attribute only if no name 39 * For example, $name= would insert the name attribute only if no name
40 * attribute had been inserted previously. 40 * attribute had been inserted previously.
41 * @param {!cursors.Range} range 41 * @param {!cursors.Range} range
42 * @param {cursors.Range} prevRange 42 * @param {cursors.Range} prevRange
43 * @param {chrome.automation.EventType|Output.EventType} type 43 * @param {chrome.automation.EventType|Output.EventType} type
44 * @constructor 44 * @constructor
45 */ 45 */
46 Output = function(range, prevRange, type) { 46 Output = function(range, prevRange, type) {
47 // TODO(dtseng): Include brailleBuffer once we add rules for braille. 47 // TODO(dtseng): Include braille specific rules.
48 /** @type {!cvox.Spannable} */ 48 /** @type {!cvox.Spannable} */
49 this.buffer_ = new cvox.Spannable(); 49 this.buffer_ = new cvox.Spannable();
50 /** @type {!cvox.Spannable} */
51 this.brailleBuffer_ = new cvox.Spannable();
52 /** @type {!Array.<Object>} */
50 this.locations_ = []; 53 this.locations_ = [];
51 54
55 /**
56 * Current global options.
57 * @type {{speech: boolean, braille: boolean, location: boolean}}
58 */
59 this.formatOptions_ = {speech: true, braille: false, location: true};
60
52 this.render_(range, prevRange, type); 61 this.render_(range, prevRange, type);
53 this.handleSpeech(); 62 this.handleSpeech();
54 this.handleBraille(); 63 this.handleBraille();
55 this.handleDisplay(); 64 this.handleDisplay();
56 }; 65 };
57 66
58 /** 67 /**
59 * Delimiter to use between output values. 68 * Delimiter to use between output values.
60 * @type {string} 69 * @type {string}
61 */ 70 */
62 Output.SPACE = ' '; 71 Output.SPACE = ' ';
63 72
64 /** 73 /**
65 * Rules specifying format of AutomationNodes for output. 74 * Rules specifying format of AutomationNodes for output.
66 * @type {!Object.<string, Object.<string, Object.<string, string>>>} 75 * @type {!Object.<string, Object.<string, Object.<string, string>>>}
67 */ 76 */
68 Output.RULES = { 77 Output.RULES = {
69 navigate: { 78 navigate: {
70 'default': { 79 'default': {
71 speak: '$name $role $value', 80 speak: '$name $role $value',
72 braille: '' 81 braille: ''
73 }, 82 },
83 alert: {
84 speak: '@aria_role_alert $name @earcon(ALERT_NONMODAL)'
85 },
74 button: { 86 button: {
75 speak: '$name $earcon(BUTTON, @tag_button)' 87 speak: '$name $earcon(BUTTON, @tag_button)'
76 }, 88 },
77 checkBox: { 89 checkBox: {
78 speak: '$or($checked, @describe_checkbox_checked($name), ' + 90 speak: '$or($checked, @describe_checkbox_checked($name), ' +
79 '@describe_checkbox_unchecked($name)) ' + 91 '@describe_checkbox_unchecked($name)) ' +
80 '$or($checked, ' + 92 '$or($checked, ' +
81 '$earcon(CHECK_ON, @input_type_checkbox), ' + 93 '$earcon(CHECK_ON, @input_type_checkbox), ' +
82 '$earcon(CHECK_OFF, @input_type_checkbox))' 94 '$earcon(CHECK_OFF, @input_type_checkbox))'
83 }, 95 },
84 heading: { 96 heading: {
85 enter: '@aria_role_heading', 97 enter: '@aria_role_heading',
86 speak: '@aria_role_heading $name=' 98 speak: '@aria_role_heading $name='
87 }, 99 },
88 inlineTextBox: { 100 inlineTextBox: {
89 speak: '$value=' 101 speak: '$value='
90 }, 102 },
91 link: { 103 link: {
92 enter: '$name= $visited $earcon(LINK, @tag_link)=', 104 enter: '$name= $visited $earcon(LINK, @tag_link)=',
93 stay: '$name= $visited @tag_link', 105 stay: '$name= $visited @tag_link',
94 speak: '$name= $visited $earcon(LINK, @tag_link)=' 106 speak: '$name= $visited $earcon(LINK, @tag_link)='
95 }, 107 },
96 list: { 108 list: {
97 enter: '$role' 109 enter: '$role'
98 }, 110 },
99 listItem: { 111 listItem: {
100 enter: '$role' 112 enter: '$role'
101 }, 113 },
114 menuItem: {
115 speak: '$or($haspopup, @describe_menu_item_with_submenu($name), ' +
116 '@describe_menu_item($name))'
117 },
118 menuListOption: {
119 speak: '$name $value @aria_role_menuitem ' +
120 '@describe_index($indexInParent, $parentChildCount)'
121 },
102 paragraph: { 122 paragraph: {
103 speak: '$value' 123 speak: '$value'
104 }, 124 },
125 popUpButton: {
126 speak: '$value $name @tag_button @aria_has_popup $earcon(LISTBOX)'
127 },
128 radioButton: {
129 speak: '$or($checked, @describe_radio_selected($name), ' +
130 '@describe_radio_unselected($name)) ' +
131 '$or($checked, ' +
132 '$earcon(CHECK_ON, @input_type_radio), ' +
133 '$earcon(CHECK_OFF, @input_type_radio))'
134 },
135 slider: {
136 speak: '@describe_slider($value, $name)'
137 },
105 staticText: { 138 staticText: {
106 speak: '$value' 139 speak: '$value'
107 }, 140 },
108 textBox: { 141 textBox: {
109 speak: '$name $value $earcon(EDITABLE_TEXT, @input_type_text)' 142 speak: '$name $value $earcon(EDITABLE_TEXT, @input_type_text)'
110 }, 143 },
144 tab: {
145 speak: '@describe_tab($name)'
146 },
111 textField: { 147 textField: {
112 speak: '$name $value $earcon(EDITABLE_TEXT, @input_type_text)' 148 speak: '$name $value $earcon(EDITABLE_TEXT, @input_type_text)'
113 }, 149 },
114 window: { 150 window: {
115 enter: '$name $role= $earcon(OBJECT_OPEN)' 151 speak: '@describe_window($name) $earcon(OBJECT_OPEN)'
116 // TODO(dtseng): A leave event is not reliable because views does not fire
117 // focus events after closing windows.
118 } 152 }
119 }, 153 },
120 menuStart: { 154 menuStart: {
121 'default': { 155 'default': {
122 speak: '@chrome_menu_opened($name) $role $earcon(OBJECT_OPEN)' 156 speak: '@chrome_menu_opened($name) $role $earcon(OBJECT_OPEN)'
123 } 157 }
124 }, 158 },
125 menuEnd: { 159 menuEnd: {
126 'default': { 160 'default': {
127 speak: '$earcon(OBJECT_CLOSE)' 161 speak: '$earcon(OBJECT_CLOSE)'
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
188 a.run(); 222 a.run();
189 }); 223 });
190 } 224 }
191 }, 225 },
192 226
193 /** 227 /**
194 * Handles output to braille. 228 * Handles output to braille.
195 */ 229 */
196 handleBraille: function() { 230 handleBraille: function() {
197 cvox.ChromeVox.braille.write( 231 cvox.ChromeVox.braille.write(
198 cvox.NavBraille.fromText(this.buffer_.toString())); 232 cvox.NavBraille.fromText(this.brailleBuffer_.toString()));
199 }, 233 },
200 234
201 /** 235 /**
202 * Handles output to visual display. 236 * Handles output to visual display.
203 */ 237 */
204 handleDisplay: function() { 238 handleDisplay: function() {
205 chrome.accessibilityPrivate.setFocusRing(this.locations_); 239 chrome.accessibilityPrivate.setFocusRing(this.locations_);
206 }, 240 },
207 241
208 /** 242 /**
209 * Renders the given range using optional context previous range and event 243 * Renders the given range using optional context previous range and event
210 * type. 244 * type.
211 * @param {!cursors.Range} range 245 * @param {!cursors.Range} range
212 * @param {cursors.Range} prevRange 246 * @param {cursors.Range} prevRange
213 * @param {chrome.automation.EventType|string} type 247 * @param {chrome.automation.EventType|string} type
214 * @private 248 * @private
215 */ 249 */
216 render_: function(range, prevRange, type) { 250 render_: function(range, prevRange, type) {
217 var buff = new cvox.Spannable(); 251 var buff = new cvox.Spannable();
252 var brailleBuff = new cvox.Spannable();
218 if (range.isSubNode()) 253 if (range.isSubNode())
219 this.subNode_(range, prevRange, type, buff); 254 this.subNode_(range, prevRange, type, buff);
220 else 255 else
221 this.range_(range, prevRange, type, buff); 256 this.range_(range, prevRange, type, buff);
222 257
258 this.formatOptions_.braille = true;
259 this.formatOptions_.location = false;
260 if (range.isSubNode())
261 this.subNode_(range, prevRange, type, brailleBuff);
262 else
263 this.range_(range, prevRange, type, brailleBuff);
264
223 this.buffer_ = buff; 265 this.buffer_ = buff;
266 this.brailleBuffer_ = brailleBuff;
224 }, 267 },
225 268
226 /** 269 /**
227 * Format the node given the format specifier. 270 * Format the node given the format specifier.
228 * @param {!chrome.automation.AutomationNode} node 271 * @param {!chrome.automation.AutomationNode} node
229 * @param {string|!Object} format The output format either specified as an 272 * @param {string|!Object} format The output format either specified as an
230 * output template string or a parsed output format tree. 273 * output template string or a parsed output format tree.
231 * @param {!Object=} opt_exclude A set of attributes to exclude. 274 * @param {!Object=} opt_exclude A set of attributes to exclude.
232 * @private 275 * @private
233 */ 276 */
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
271 314
272 if (opt_exclude[token]) 315 if (opt_exclude[token])
273 return; 316 return;
274 317
275 // All possible tokens based on prefix. 318 // All possible tokens based on prefix.
276 if (prefix == '$') { 319 if (prefix == '$') {
277 options.annotation = token; 320 options.annotation = token;
278 if (token == 'role') { 321 if (token == 'role') {
279 // Non-localized role and state obtained by default. 322 // Non-localized role and state obtained by default.
280 this.addToSpannable_(buff, node.role, options); 323 this.addToSpannable_(buff, node.role, options);
324 } else if (token == 'indexInParent') {
325 this.addToSpannable_(buff, node.indexInParent + 1);
326 } else if (token == 'parentChildCount') {
327 if (node.parent())
328 this.addToSpannable_(buff, node.parent().children().length);
281 } else if (token == 'state') { 329 } else if (token == 'state') {
282 Object.getOwnPropertyNames(node.state).forEach(function(s) { 330 Object.getOwnPropertyNames(node.state).forEach(function(s) {
283 this.addToSpannable_(buff, s, options); 331 this.addToSpannable_(buff, s, options);
284 }.bind(this)); 332 }.bind(this));
285 } else if (node.attributes[token]) { 333 } else if (node.attributes[token]) {
286 this.addToSpannable_(buff, node.attributes[token], options); 334 this.addToSpannable_(buff, node.attributes[token], options);
287 } else if (node.state[token]) { 335 } else if (node.state[token]) {
288 this.addToSpannable_(buff, token, options); 336 this.addToSpannable_(buff, token, options);
289 } else if (tree.firstChild) { 337 } else if (tree.firstChild) {
290 // Custom functions. 338 // Custom functions.
(...skipping 19 matching lines...) Expand all
310 var msgId = token; 358 var msgId = token;
311 var msgArgs = []; 359 var msgArgs = [];
312 var curMsg = tree.firstChild; 360 var curMsg = tree.firstChild;
313 361
314 while (curMsg) { 362 while (curMsg) {
315 var arg = curMsg.value; 363 var arg = curMsg.value;
316 if (arg[0] != '$') { 364 if (arg[0] != '$') {
317 console.error('Unexpected value: ' + arg); 365 console.error('Unexpected value: ' + arg);
318 return; 366 return;
319 } 367 }
320 arg = arg.slice(1); 368 var msgBuff = new cvox.Spannable();
321 if (!node.attributes[arg]) 369 this.format_(node, arg, msgBuff);
322 msgArgs.push(''); 370 msgArgs.push(msgBuff.toString());
323 else
324 msgArgs.push(node.attributes[arg]);
325 curMsg = curMsg.nextSibling; 371 curMsg = curMsg.nextSibling;
326 } 372 }
373 var msg = cvox.ChromeVox.msgs.getMsg(msgId, msgArgs);
374 try {
375 if (this.formatOptions_.braille)
376 msg = cvox.ChromeVox.msgs.getMsg(msgId + '_brl', msgArgs) || msg;
377 } catch(e) {}
327 378
328 var msg = cvox.ChromeVox.msgs.getMsg(msgId, msgArgs);
329 if (msg) { 379 if (msg) {
330 this.addToSpannable_(buff, msg, options); 380 this.addToSpannable_(buff, msg, options);
331 } 381 }
332 } 382 }
333 }.bind(this)); 383 }.bind(this));
334 }, 384 },
335 385
336 /** 386 /**
337 * @param {!cursors.Range} range 387 * @param {!cursors.Range} range
338 * @param {cursors.Range} prevRange 388 * @param {cursors.Range} prevRange
339 * @param {chrome.automation.EventType|string} type 389 * @param {chrome.automation.EventType|string} type
340 * @param {!cvox.Spannable} rangeBuff 390 * @param {!cvox.Spannable} rangeBuff
341 * @private 391 * @private
342 */ 392 */
343 range_: function(range, prevRange, type, rangeBuff) { 393 range_: function(range, prevRange, type, rangeBuff) {
344 if (!prevRange) 394 if (!prevRange)
345 prevRange = range; 395 prevRange = range;
346 396
347 var cursor = range.getStart(); 397 var cursor = range.getStart();
348 var prevNode = prevRange.getStart().getNode(); 398 var prevNode = prevRange.getStart().getNode();
349 399
350 var formatNodeAndAncestors = function(node, prevNode) { 400 var formatNodeAndAncestors = function(node, prevNode) {
351 var buff = new cvox.Spannable(); 401 var buff = new cvox.Spannable();
352 this.ancestry_(node, prevNode, type, buff); 402 this.ancestry_(node, prevNode, type, buff);
353 this.node_(node, prevNode, type, buff); 403 this.node_(node, prevNode, type, buff);
354 this.locations_.push(node.location); 404 if (this.formatOptions_.location)
405 this.locations_.push(node.location);
355 return buff; 406 return buff;
356 }.bind(this); 407 }.bind(this);
357 408
358 while (cursor.getNode() != range.getEnd().getNode()) { 409 while (cursor.getNode() != range.getEnd().getNode()) {
359 var node = cursor.getNode(); 410 var node = cursor.getNode();
360 this.addToSpannable_( 411 this.addToSpannable_(
361 rangeBuff, formatNodeAndAncestors(node, prevNode)); 412 rangeBuff, formatNodeAndAncestors(node, prevNode));
362 prevNode = node; 413 prevNode = node;
363 cursor = cursor.move(cursors.Unit.NODE, 414 cursor = cursor.move(cursors.Unit.NODE,
364 cursors.Movement.DIRECTIONAL, 415 cursors.Movement.DIRECTIONAL,
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
510 } 561 }
511 562
512 if (currentNode != root) 563 if (currentNode != root)
513 throw 'Unbalanced parenthesis.'; 564 throw 'Unbalanced parenthesis.';
514 565
515 return root; 566 return root;
516 } 567 }
517 }; 568 };
518 569
519 }); // goog.scope 570 }); // goog.scope
OLDNEW
« no previous file with comments | « chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_predicate.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698