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

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

Issue 1109983003: Better support ChromeVox list and list-like output. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add more list-like roles support and tests. Created 5 years, 7 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
« no previous file with comments | « no previous file | chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs » ('j') | 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 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 dialog: { 99 dialog: {
100 msgId: 'dialog' 100 msgId: 'dialog'
101 }, 101 },
102 heading: { 102 heading: {
103 msgId: 'aria_role_heading', 103 msgId: 'aria_role_heading',
104 }, 104 },
105 link: { 105 link: {
106 msgId: 'tag_link', 106 msgId: 'tag_link',
107 earcon: 'LINK' 107 earcon: 'LINK'
108 }, 108 },
109 listBox: {
110 msgId: 'aria_role_listbox',
111 earcon: 'LISTBOX'
112 },
113 listBoxOption: {
114 msgId: 'aria_role_listitem',
115 earcon: 'LIST_ITEM'
116 },
109 listItem: { 117 listItem: {
110 msgId: 'ARIA_ROLE_LISTITEM', 118 msgId: 'ARIA_ROLE_LISTITEM',
111 earcon: 'list_item' 119 earcon: 'list_item'
112 }, 120 },
121 menu: {
122 msgId: 'aria_role_menu'
123 },
124 menuItem: {
125 msgId: 'aria_role_menuitem'
126 },
113 menuListOption: { 127 menuListOption: {
114 msgId: 'aria_role_menuitem' 128 msgId: 'aria_role_menuitem'
115 }, 129 },
130 menuListPopup: {
131 msgId: 'aria_role_menu'
132 },
116 popUpButton: { 133 popUpButton: {
117 msgId: 'tag_button' 134 msgId: 'tag_button'
118 }, 135 },
119 radioButton: { 136 radioButton: {
120 msgId: 'input_type_radio' 137 msgId: 'input_type_radio'
121 }, 138 },
122 spinButton: { 139 spinButton: {
123 msgId: 'aria_role_combobox', 140 msgId: 'aria_role_combobox',
124 earcon: 'LISTBOX' 141 earcon: 'LISTBOX'
125 }, 142 },
126 textBox: { 143 textBox: {
127 msgId: 'input_type_text', 144 msgId: 'input_type_text',
128 earcon: 'EDITABLE_TEXT' 145 earcon: 'EDITABLE_TEXT'
129 }, 146 },
130 textField: { 147 textField: {
131 msgId: 'input_type_text', 148 msgId: 'input_type_text',
132 earcon: 'EDITABLE_TEXT' 149 earcon: 'EDITABLE_TEXT'
133 }, 150 },
134 time: { 151 time: {
135 msgId: 'tag_time' 152 msgId: 'tag_time'
136 }, 153 },
137 toolbar: { 154 toolbar: {
138 msgId: 'aria_role_toolbar' 155 msgId: 'aria_role_toolbar'
156 },
157 tree: {
158 msgId: 'aria_role_tree'
159 },
160 treeItem: {
161 msgId: 'aria_role_treeitem'
139 } 162 }
140 }; 163 };
141 164
142 /** 165 /**
143 * Metadata about supported automation states. 166 * Metadata about supported automation states.
144 * @const {!Object<string, 167 * @const {!Object<string,
145 * {on: {msgId: string, earconId: string}, 168 * {on: {msgId: string, earconId: string},
146 * off: {msgId: string, earconId: string}}>} 169 * off: {msgId: string, earconId: string}}>}
147 * @private 170 * @private
148 */ 171 */
149 Output.STATE_INFO_ = { 172 Output.STATE_INFO_ = {
150 checked: { 173 checked: {
151 on: { 174 on: {
152 earconId: 'CHECK_ON', 175 earconId: 'CHECK_ON',
153 msgId: 'checkbox_checked_state' 176 msgId: 'checkbox_checked_state'
154 }, 177 },
155 off: { 178 off: {
156 earconId: 'CHECK_OFF', 179 earconId: 'CHECK_OFF',
157 msgId: 'checkbox_unchecked_state' 180 msgId: 'checkbox_unchecked_state'
181 },
182 omitted: {
183 earconId: 'CHECK_OFF',
184 msgId: 'checkbox_unchecked_state'
185 }
186 },
187 collapsed: {
188 on: {
189 msgId: 'aria_expanded_false'
190 },
191 off: {
192 msgId: 'aria_expanded_true'
193 }
194 },
195 expanded: {
196 on: {
197 msgId: 'aria_expanded_true'
198 },
199 off: {
200 msgId: 'aria_expanded_false'
158 } 201 }
159 } 202 }
160 }; 203 };
161 204
162 /** 205 /**
163 * Rules specifying format of AutomationNodes for output. 206 * Rules specifying format of AutomationNodes for output.
164 * @type {!Object<string, Object<string, Object<string, string>>>} 207 * @type {!Object<string, Object<string, Object<string, string>>>}
165 */ 208 */
166 Output.RULES = { 209 Output.RULES = {
167 navigate: { 210 navigate: {
(...skipping 20 matching lines...) Expand all
188 }, 231 },
189 inlineTextBox: { 232 inlineTextBox: {
190 speak: '$value=' 233 speak: '$value='
191 }, 234 },
192 link: { 235 link: {
193 enter: '$name $visited $role', 236 enter: '$name $visited $role',
194 stay: '$name= $visited $role', 237 stay: '$name= $visited $role',
195 speak: '$name= $visited $role' 238 speak: '$name= $visited $role'
196 }, 239 },
197 list: { 240 list: {
198 enter: '@aria_role_list @list_with_items($parentChildCount)' 241 enter: '$role @list_with_items($countChildren(listItem))'
242 },
243 listBox: {
244 enter: '$name $role @list_with_items($countChildren(listBoxOption))'
245 },
246 listBoxOption: {
247 speak: '$name $role @describe_index($indexInParent, $parentChildCount)'
199 }, 248 },
200 listItem: { 249 listItem: {
201 enter: '$role' 250 enter: '$role'
dmazzoni 2015/04/28 18:47:40 Should this one have describe_index too?
David Tseng 2015/04/28 19:06:33 No afaik because these are "normal" listitems (e.g
dmazzoni 2015/04/28 19:13:26 Maybe when we add support for extra verbose "help"
David Tseng 2015/04/28 20:02:51 Agreed on verbosity. I think most screen readers d
202 }, 251 },
252 menu: {
253 enter: '$name $role @list_with_items($countChildren(menuItem))'
254 },
203 menuItem: { 255 menuItem: {
204 speak: '$if($haspopup, @describe_menu_item_with_submenu($name), ' + 256 speak: '$if($haspopup, @describe_menu_item_with_submenu($name), ' +
205 '@describe_menu_item($name)) ' + 257 '@describe_menu_item($name)) ' +
206 '@describe_index($indexInParent, $parentChildCount)' 258 '@describe_index($indexInParent, $parentChildCount)'
207 }, 259 },
208 menuListOption: { 260 menuListOption: {
dmazzoni 2015/04/28 18:47:40 Please add menuList also - it's <select> without m
209 speak: '$name $value @aria_role_menuitem ' + 261 speak: '$name $value @aria_role_menuitem ' +
210 '@describe_index($indexInParent, $parentChildCount)' 262 '@describe_index($indexInParent, $parentChildCount)'
211 }, 263 },
212 paragraph: { 264 paragraph: {
213 speak: '$value' 265 speak: '$value'
214 }, 266 },
215 popUpButton: { 267 popUpButton: {
216 speak: '$value $name $role @aria_has_popup ' + 268 speak: '$value $name $role @aria_has_popup ' +
217 '$if($collapsed, @aria_expanded_false, @aria_expanded_true)' 269 '$if($collapsed, @aria_expanded_false, @aria_expanded_true)'
218 }, 270 },
(...skipping 16 matching lines...) Expand all
235 '$earcon(EDITABLE_TEXT)', 287 '$earcon(EDITABLE_TEXT)',
236 braille: '' 288 braille: ''
237 }, 289 },
238 time: { 290 time: {
239 enter: '$name $role', 291 enter: '$name $role',
240 leave: '@exited_container($role)' 292 leave: '@exited_container($role)'
241 }, 293 },
242 toolbar: { 294 toolbar: {
243 enter: '$name $role' 295 enter: '$name $role'
244 }, 296 },
297 tree: {
298 enter: '$name $role @list_with_items($countChildren(treeItem))'
299 },
300 treeItem: {
301 enter: '$role $expanded $collapsed ' +
302 '@describe_index($indexInParent, $parentChildCount) ' +
303 '@describe_depth($hierarchicalLevel)'
304 },
245 window: { 305 window: {
246 enter: '$name', 306 enter: '$name',
247 speak: '@describe_window($name) $earcon(OBJECT_OPEN)' 307 speak: '@describe_window($name) $earcon(OBJECT_OPEN)'
248 } 308 }
249 }, 309 },
250 menuStart: { 310 menuStart: {
251 'default': { 311 'default': {
252 speak: '@chrome_menu_opened($name) $earcon(OBJECT_OPEN)' 312 speak: '@chrome_menu_opened($name) $earcon(OBJECT_OPEN)'
253 } 313 }
254 }, 314 },
(...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after
621 msg = cvox.ChromeVox.msgs.getMsg(info.msgId); 681 msg = cvox.ChromeVox.msgs.getMsg(info.msgId);
622 earconId = info.earcon; 682 earconId = info.earcon;
623 } else { 683 } else {
624 console.error('Missing role info for ' + node.role); 684 console.error('Missing role info for ' + node.role);
625 } 685 }
626 if (earconId) 686 if (earconId)
627 options.annotation.push(new Output.EarconAction(earconId)); 687 options.annotation.push(new Output.EarconAction(earconId));
628 this.append_(buff, msg, options); 688 this.append_(buff, msg, options);
629 } else if (node.attributes[token] !== undefined) { 689 } else if (node.attributes[token] !== undefined) {
630 options.annotation.push(token); 690 options.annotation.push(token);
631 this.append_(buff, node.attributes[token], options); 691 var value = node.attributes[token];
692 if (typeof value == 'number')
693 value = String(value);
694 this.append_(buff, value, options);
632 } else if (Output.STATE_INFO_[token]) { 695 } else if (Output.STATE_INFO_[token]) {
633 options.annotation.push('state'); 696 options.annotation.push('state');
634 var stateInfo = Output.STATE_INFO_[token]; 697 var stateInfo = Output.STATE_INFO_[token];
635 var resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off; 698 var resolvedInfo = {};
636 options.annotation.push( 699 if (node.state[token] === undefined)
637 new Output.EarconAction(resolvedInfo.earconId)); 700 resolvedInfo = stateInfo.omitted;
701 else
702 resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off;
703 if (!resolvedInfo)
704 return;
705 if (resolvedInfo.earconId) {
706 options.annotation.push(
707 new Output.EarconAction(resolvedInfo.earconId));
708 }
638 var msgId = 709 var msgId =
639 this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' : 710 this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' :
640 resolvedInfo.msgId; 711 resolvedInfo.msgId;
641 var msg = cvox.ChromeVox.msgs.getMsg(msgId); 712 var msg = cvox.ChromeVox.msgs.getMsg(msgId);
642 this.append_(buff, msg, options); 713 this.append_(buff, msg, options);
643 } else if (tree.firstChild) { 714 } else if (tree.firstChild) {
644 // Custom functions. 715 // Custom functions.
645 if (token == 'if') { 716 if (token == 'if') {
646 var cond = tree.firstChild; 717 var cond = tree.firstChild;
647 var attrib = cond.value.slice(1); 718 var attrib = cond.value.slice(1);
648 if (node.attributes[attrib] || node.state[attrib]) 719 if (node.attributes[attrib] || node.state[attrib])
649 this.format_(node, cond.nextSibling, buff); 720 this.format_(node, cond.nextSibling, buff);
650 else 721 else
651 this.format_(node, cond.nextSibling.nextSibling, buff); 722 this.format_(node, cond.nextSibling.nextSibling, buff);
652 } else if (token == 'earcon') { 723 } else if (token == 'earcon') {
653 // Assumes there's existing output in our buffer. 724 // Assumes there's existing output in our buffer.
654 var lastBuff = buff[buff.length - 1]; 725 var lastBuff = buff[buff.length - 1];
655 if (!lastBuff) 726 if (!lastBuff)
656 return; 727 return;
657 728
658 lastBuff.setSpan( 729 lastBuff.setSpan(
659 new Output.EarconAction(tree.firstChild.value), 0, 0); 730 new Output.EarconAction(tree.firstChild.value), 0, 0);
731 } else if (token == 'countChildren') {
732 var role = tree.firstChild.value;
733 var count = node.children.filter(function(e) {
734 return e.role == role;
735 }).length;
736 this.append_(buff, String(count));
660 } 737 }
661 } 738 }
662 } else if (prefix == '@') { 739 } else if (prefix == '@') {
663 // Tokens can have substitutions. 740 // Tokens can have substitutions.
664 var pieces = token.split('+'); 741 var pieces = token.split('+');
665 token = pieces.reduce(function(prev, cur) { 742 token = pieces.reduce(function(prev, cur) {
666 var lookup = cur; 743 var lookup = cur;
667 if (cur[0] == '$') 744 if (cur[0] == '$')
668 lookup = node.attributes[cur.slice(1)]; 745 lookup = node.attributes[cur.slice(1)];
669 return prev + lookup; 746 return prev + lookup;
670 }.bind(this), ''); 747 }.bind(this), '');
671 var msgId = token; 748 var msgId = token;
672 var msgArgs = []; 749 var msgArgs = [];
673 var curMsg = tree.firstChild; 750 var curMsg = tree.firstChild;
674 751
675 while (curMsg) { 752 while (curMsg) {
676 var arg = curMsg.value; 753 var arg = curMsg.value;
677 if (arg[0] != '$') { 754 if (arg[0] != '$') {
678 console.error('Unexpected value: ' + arg); 755 console.error('Unexpected value: ' + arg);
679 return; 756 return;
680 } 757 }
681 var msgBuff = []; 758 var msgBuff = [];
682 this.format_(node, arg, msgBuff); 759 this.format_(node, curMsg, msgBuff);
683 msgArgs = msgArgs.concat(msgBuff); 760 msgArgs = msgArgs.concat(msgBuff);
684 curMsg = curMsg.nextSibling; 761 curMsg = curMsg.nextSibling;
685 } 762 }
686 var msg = cvox.ChromeVox.msgs.getMsg(msgId, msgArgs); 763 var msg = cvox.ChromeVox.msgs.getMsg(msgId, msgArgs);
687 try { 764 try {
688 if (this.formatOptions_.braille) 765 if (this.formatOptions_.braille)
689 msg = cvox.ChromeVox.msgs.getMsg(msgId + '_brl', msgArgs) || msg; 766 msg = cvox.ChromeVox.msgs.getMsg(msgId + '_brl', msgArgs) || msg;
690 } catch(e) {} 767 } catch(e) {}
691 768
692 if (msg) { 769 if (msg) {
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
904 } 981 }
905 982
906 if (currentNode != root) 983 if (currentNode != root)
907 throw 'Unbalanced parenthesis.'; 984 throw 'Unbalanced parenthesis.';
908 985
909 return root; 986 return root;
910 } 987 }
911 }; 988 };
912 989
913 }); // goog.scope 990 }); // goog.scope
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698