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

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: 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},
170 * omitted: {msgId: string, earconId: string}}>}
171 * on: info used to describe a state that is set to true.
172 * off: info used to describe a state that is set to false.
173 * omitted: info used to describe a state that is undefined.
147 * @private 174 * @private
148 */ 175 */
149 Output.STATE_INFO_ = { 176 Output.STATE_INFO_ = {
150 checked: { 177 checked: {
151 on: { 178 on: {
152 earconId: 'CHECK_ON', 179 earconId: 'CHECK_ON',
153 msgId: 'checkbox_checked_state' 180 msgId: 'checkbox_checked_state'
154 }, 181 },
155 off: { 182 off: {
156 earconId: 'CHECK_OFF', 183 earconId: 'CHECK_OFF',
157 msgId: 'checkbox_unchecked_state' 184 msgId: 'checkbox_unchecked_state'
185 },
186 omitted: {
187 earconId: 'CHECK_OFF',
188 msgId: 'checkbox_unchecked_state'
189 }
190 },
191 collapsed: {
192 on: {
193 msgId: 'aria_expanded_false'
194 },
195 off: {
196 msgId: 'aria_expanded_true'
197 }
198 },
199 expanded: {
200 on: {
201 msgId: 'aria_expanded_true'
202 },
203 off: {
204 msgId: 'aria_expanded_false'
158 } 205 }
159 } 206 }
160 }; 207 };
161 208
162 /** 209 /**
163 * Rules specifying format of AutomationNodes for output. 210 * Rules specifying format of AutomationNodes for output.
164 * @type {!Object<string, Object<string, Object<string, string>>>} 211 * @type {!Object<string, Object<string, Object<string, string>>>}
165 */ 212 */
166 Output.RULES = { 213 Output.RULES = {
167 navigate: { 214 navigate: {
(...skipping 20 matching lines...) Expand all
188 }, 235 },
189 inlineTextBox: { 236 inlineTextBox: {
190 speak: '$value=' 237 speak: '$value='
191 }, 238 },
192 link: { 239 link: {
193 enter: '$name $visited $role', 240 enter: '$name $visited $role',
194 stay: '$name= $visited $role', 241 stay: '$name= $visited $role',
195 speak: '$name= $visited $role' 242 speak: '$name= $visited $role'
196 }, 243 },
197 list: { 244 list: {
198 enter: '@aria_role_list @list_with_items($parentChildCount)' 245 enter: '$role @list_with_items($countChildren(listItem))'
246 },
247 listBox: {
248 enter: '$name $role @list_with_items($countChildren(listBoxOption))'
249 },
250 listBoxOption: {
251 speak: '$name $role @describe_index($indexInParent, $parentChildCount)'
199 }, 252 },
200 listItem: { 253 listItem: {
201 enter: '$role' 254 enter: '$role'
202 }, 255 },
256 menu: {
257 enter: '$name $role @list_with_items($countChildren(menuItem))'
258 },
203 menuItem: { 259 menuItem: {
204 speak: '$if($haspopup, @describe_menu_item_with_submenu($name), ' + 260 speak: '$if($haspopup, @describe_menu_item_with_submenu($name), ' +
205 '@describe_menu_item($name)) ' + 261 '@describe_menu_item($name)) ' +
206 '@describe_index($indexInParent, $parentChildCount)' 262 '@describe_index($indexInParent, $parentChildCount)'
207 }, 263 },
208 menuListOption: { 264 menuListOption: {
209 speak: '$name $value @aria_role_menuitem ' + 265 speak: '$name $value @aria_role_menuitem ' +
210 '@describe_index($indexInParent, $parentChildCount)' 266 '@describe_index($indexInParent, $parentChildCount)'
211 }, 267 },
212 paragraph: { 268 paragraph: {
(...skipping 22 matching lines...) Expand all
235 '$earcon(EDITABLE_TEXT)', 291 '$earcon(EDITABLE_TEXT)',
236 braille: '' 292 braille: ''
237 }, 293 },
238 time: { 294 time: {
239 enter: '$name $role', 295 enter: '$name $role',
240 leave: '@exited_container($role)' 296 leave: '@exited_container($role)'
241 }, 297 },
242 toolbar: { 298 toolbar: {
243 enter: '$name $role' 299 enter: '$name $role'
244 }, 300 },
301 tree: {
302 enter: '$name $role @list_with_items($countChildren(treeItem))'
303 },
304 treeItem: {
305 enter: '$role $expanded $collapsed ' +
306 '@describe_index($indexInParent, $parentChildCount) ' +
307 '@describe_depth($hierarchicalLevel)'
308 },
245 window: { 309 window: {
246 enter: '$name', 310 enter: '$name',
247 speak: '@describe_window($name) $earcon(OBJECT_OPEN)' 311 speak: '@describe_window($name) $earcon(OBJECT_OPEN)'
248 } 312 }
249 }, 313 },
250 menuStart: { 314 menuStart: {
251 'default': { 315 'default': {
252 speak: '@chrome_menu_opened($name) $earcon(OBJECT_OPEN)' 316 speak: '@chrome_menu_opened($name) $earcon(OBJECT_OPEN)'
253 } 317 }
254 }, 318 },
(...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after
621 msg = cvox.ChromeVox.msgs.getMsg(info.msgId); 685 msg = cvox.ChromeVox.msgs.getMsg(info.msgId);
622 earconId = info.earcon; 686 earconId = info.earcon;
623 } else { 687 } else {
624 console.error('Missing role info for ' + node.role); 688 console.error('Missing role info for ' + node.role);
625 } 689 }
626 if (earconId) 690 if (earconId)
627 options.annotation.push(new Output.EarconAction(earconId)); 691 options.annotation.push(new Output.EarconAction(earconId));
628 this.append_(buff, msg, options); 692 this.append_(buff, msg, options);
629 } else if (node.attributes[token] !== undefined) { 693 } else if (node.attributes[token] !== undefined) {
630 options.annotation.push(token); 694 options.annotation.push(token);
631 this.append_(buff, node.attributes[token], options); 695 var value = node.attributes[token];
696 if (typeof value == 'number')
697 value = String(value);
698 this.append_(buff, value, options);
632 } else if (Output.STATE_INFO_[token]) { 699 } else if (Output.STATE_INFO_[token]) {
633 options.annotation.push('state'); 700 options.annotation.push('state');
634 var stateInfo = Output.STATE_INFO_[token]; 701 var stateInfo = Output.STATE_INFO_[token];
635 var resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off; 702 var resolvedInfo = {};
636 options.annotation.push( 703 if (node.state[token] === undefined)
637 new Output.EarconAction(resolvedInfo.earconId)); 704 resolvedInfo = stateInfo.omitted;
705 else
706 resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off;
707 if (!resolvedInfo)
708 return;
709 if (resolvedInfo.earconId) {
710 options.annotation.push(
711 new Output.EarconAction(resolvedInfo.earconId));
712 }
638 var msgId = 713 var msgId =
639 this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' : 714 this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' :
640 resolvedInfo.msgId; 715 resolvedInfo.msgId;
641 var msg = cvox.ChromeVox.msgs.getMsg(msgId); 716 var msg = cvox.ChromeVox.msgs.getMsg(msgId);
642 this.append_(buff, msg, options); 717 this.append_(buff, msg, options);
643 } else if (tree.firstChild) { 718 } else if (tree.firstChild) {
644 // Custom functions. 719 // Custom functions.
645 if (token == 'if') { 720 if (token == 'if') {
646 var cond = tree.firstChild; 721 var cond = tree.firstChild;
647 var attrib = cond.value.slice(1); 722 var attrib = cond.value.slice(1);
648 if (node.attributes[attrib] || node.state[attrib]) 723 if (node.attributes[attrib] || node.state[attrib])
649 this.format_(node, cond.nextSibling, buff); 724 this.format_(node, cond.nextSibling, buff);
650 else 725 else
651 this.format_(node, cond.nextSibling.nextSibling, buff); 726 this.format_(node, cond.nextSibling.nextSibling, buff);
652 } else if (token == 'earcon') { 727 } else if (token == 'earcon') {
653 // Assumes there's existing output in our buffer. 728 // Assumes there's existing output in our buffer.
654 var lastBuff = buff[buff.length - 1]; 729 var lastBuff = buff[buff.length - 1];
655 if (!lastBuff) 730 if (!lastBuff)
656 return; 731 return;
657 732
658 lastBuff.setSpan( 733 lastBuff.setSpan(
659 new Output.EarconAction(tree.firstChild.value), 0, 0); 734 new Output.EarconAction(tree.firstChild.value), 0, 0);
735 } else if (token == 'countChildren') {
736 var role = tree.firstChild.value;
737 var count = node.children.filter(function(e) {
738 return e.role == role;
739 }).length;
740 this.append_(buff, String(count));
660 } 741 }
661 } 742 }
662 } else if (prefix == '@') { 743 } else if (prefix == '@') {
663 // Tokens can have substitutions. 744 // Tokens can have substitutions.
664 var pieces = token.split('+'); 745 var pieces = token.split('+');
665 token = pieces.reduce(function(prev, cur) { 746 token = pieces.reduce(function(prev, cur) {
666 var lookup = cur; 747 var lookup = cur;
667 if (cur[0] == '$') 748 if (cur[0] == '$')
668 lookup = node.attributes[cur.slice(1)]; 749 lookup = node.attributes[cur.slice(1)];
669 return prev + lookup; 750 return prev + lookup;
670 }.bind(this), ''); 751 }.bind(this), '');
671 var msgId = token; 752 var msgId = token;
672 var msgArgs = []; 753 var msgArgs = [];
673 var curMsg = tree.firstChild; 754 var curMsg = tree.firstChild;
674 755
675 while (curMsg) { 756 while (curMsg) {
676 var arg = curMsg.value; 757 var arg = curMsg.value;
677 if (arg[0] != '$') { 758 if (arg[0] != '$') {
678 console.error('Unexpected value: ' + arg); 759 console.error('Unexpected value: ' + arg);
679 return; 760 return;
680 } 761 }
681 var msgBuff = []; 762 var msgBuff = [];
682 this.format_(node, arg, msgBuff); 763 this.format_(node, curMsg, msgBuff);
683 msgArgs = msgArgs.concat(msgBuff); 764 msgArgs = msgArgs.concat(msgBuff);
684 curMsg = curMsg.nextSibling; 765 curMsg = curMsg.nextSibling;
685 } 766 }
686 var msg = cvox.ChromeVox.msgs.getMsg(msgId, msgArgs); 767 var msg = cvox.ChromeVox.msgs.getMsg(msgId, msgArgs);
687 try { 768 try {
688 if (this.formatOptions_.braille) 769 if (this.formatOptions_.braille)
689 msg = cvox.ChromeVox.msgs.getMsg(msgId + '_brl', msgArgs) || msg; 770 msg = cvox.ChromeVox.msgs.getMsg(msgId + '_brl', msgArgs) || msg;
690 } catch(e) {} 771 } catch(e) {}
691 772
692 if (msg) { 773 if (msg) {
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
904 } 985 }
905 986
906 if (currentNode != root) 987 if (currentNode != root)
907 throw 'Unbalanced parenthesis.'; 988 throw 'Unbalanced parenthesis.';
908 989
909 return root; 990 return root;
910 } 991 }
911 }; 992 };
912 993
913 }); // goog.scope 994 }); // 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