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

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

Issue 2387413002: Improve speech contextual announcements and startCallback synchronization (Closed)
Patch Set: Disable test; it lonly works if you have Google tts installed locally. Created 4 years, 2 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 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 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 * The speech queue mode for the generated speech utterance. 85 * The speech queue mode for the generated speech utterance.
86 * @type {cvox.QueueMode} 86 * @type {cvox.QueueMode}
87 * @private 87 * @private
88 */ 88 */
89 this.queueMode_ = cvox.QueueMode.QUEUE; 89 this.queueMode_ = cvox.QueueMode.QUEUE;
90 90
91 /** 91 /**
92 * @type {boolean} 92 * @type {boolean}
93 * @private 93 * @private
94 */ 94 */
95 this.outputContextFirst_ = localStorage['outputContextFirst'] == 'true'; 95 this.outputContextFirst_ = false;
96 }; 96 };
97 97
98 /** 98 /**
99 * Delimiter to use between output values. 99 * Delimiter to use between output values.
100 * @type {string} 100 * @type {string}
101 */ 101 */
102 Output.SPACE = ' '; 102 Output.SPACE = ' ';
103 103
104 /** 104 /**
105 * Metadata about supported automation roles. 105 * Metadata about supported automation roles.
106 * @const {Object<{msgId: string, 106 * @const {Object<{msgId: string,
107 * earconId: (string|undefined), 107 * earconId: (string|undefined),
108 * inherits: (string|undefined), 108 * inherits: (string|undefined),
109 * ignoreAncestry: (boolean|undefined)}>} 109 * outputContextFirst: (boolean|undefined)}>}
110 * msgId: the message id of the role. 110 * msgId: the message id of the role.
111 * earconId: an optional earcon to play when encountering the role. 111 * earconId: an optional earcon to play when encountering the role.
112 * inherits: inherits rules from this role. 112 * inherits: inherits rules from this role.
113 * ignoreAncestry: don't output ancestry changes when encountering this role. 113 * outputContextFirst: where to place the context output.
114 * @private 114 * @private
115 */ 115 */
116 Output.ROLE_INFO_ = { 116 Output.ROLE_INFO_ = {
117 alert: { 117 alert: {
118 msgId: 'role_alert', 118 msgId: 'role_alert',
119 earconId: 'ALERT_NONMODAL' 119 earconId: 'ALERT_NONMODAL'
120 }, 120 },
121 alertDialog: { 121 alertDialog: {
122 msgId: 'role_alertdialog' 122 msgId: 'role_alertdialog',
123 outputContextFirst: true
123 }, 124 },
124 article: { 125 article: {
125 msgId: 'role_article', 126 msgId: 'role_article',
126 inherits: 'abstractContainer' 127 inherits: 'abstractContainer'
127 }, 128 },
128 application: { 129 application: {
129 msgId: 'role_application', 130 msgId: 'role_application',
130 inherits: 'abstractContainer' 131 inherits: 'abstractContainer'
131 }, 132 },
132 banner: { 133 banner: {
133 msgId: 'role_banner', 134 msgId: 'role_banner',
134 inherits: 'abstractContainer' 135 inherits: 'abstractContainer'
135 }, 136 },
136 button: { 137 button: {
137 msgId: 'role_button', 138 msgId: 'role_button',
138 earconId: 'BUTTON' 139 earconId: 'BUTTON'
139 }, 140 },
140 buttonDropDown: { 141 buttonDropDown: {
141 msgId: 'role_button', 142 msgId: 'role_button',
142 earconId: 'BUTTON' 143 earconId: 'BUTTON'
143 }, 144 },
144 checkBox: { 145 checkBox: {
145 msgId: 'role_checkbox' 146 msgId: 'role_checkbox'
146 }, 147 },
147 columnHeader: { 148 columnHeader: {
148 msgId: 'role_columnheader', 149 msgId: 'role_columnheader',
149 inherits: 'cell' 150 inherits: 'cell'
150 }, 151 },
151 comboBox: { 152 comboBox: {
152 msgId: 'role_combobox' 153 msgId: 'role_combobox',
154 earconId: 'LISTBOX'
153 }, 155 },
154 complementary: { 156 complementary: {
155 msgId: 'role_complementary', 157 msgId: 'role_complementary',
156 inherits: 'abstractContainer' 158 inherits: 'abstractContainer'
157 }, 159 },
158 contentInfo: { 160 contentInfo: {
159 msgId: 'role_contentinfo', 161 msgId: 'role_contentinfo',
160 inherits: 'abstractContainer' 162 inherits: 'abstractContainer'
161 }, 163 },
162 date: { 164 date: {
163 msgId: 'input_type_date', 165 msgId: 'input_type_date',
164 inherits: 'abstractContainer' 166 inherits: 'abstractContainer'
165 }, 167 },
166 definition: { 168 definition: {
167 msgId: 'role_definition', 169 msgId: 'role_definition',
168 inherits: 'abstractContainer' 170 inherits: 'abstractContainer'
169 }, 171 },
170 dialog: { 172 dialog: {
171 msgId: 'role_dialog' 173 msgId: 'role_dialog',
174 outputContextFirst: true
172 }, 175 },
173 directory: { 176 directory: {
174 msgId: 'role_directory', 177 msgId: 'role_directory',
175 inherits: 'abstractContainer' 178 inherits: 'abstractContainer'
176 }, 179 },
177 document: { 180 document: {
178 msgId: 'role_document', 181 msgId: 'role_document',
179 inherits: 'abstractContainer' 182 inherits: 'abstractContainer'
180 }, 183 },
181 form: { 184 form: {
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 inherits: 'abstractContainer' 226 inherits: 'abstractContainer'
224 }, 227 },
225 marquee: { 228 marquee: {
226 msgId: 'role_marquee', 229 msgId: 'role_marquee',
227 }, 230 },
228 math: { 231 math: {
229 msgId: 'role_math', 232 msgId: 'role_math',
230 inherits: 'abstractContainer' 233 inherits: 'abstractContainer'
231 }, 234 },
232 menu: { 235 menu: {
233 msgId: 'role_menu' 236 msgId: 'role_menu',
237 outputContextFirst: true
234 }, 238 },
235 menuBar: { 239 menuBar: {
236 msgId: 'role_menubar', 240 msgId: 'role_menubar',
237 }, 241 },
238 menuItem: { 242 menuItem: {
239 msgId: 'role_menuitem' 243 msgId: 'role_menuitem'
240 }, 244 },
241 menuItemCheckBox: { 245 menuItemCheckBox: {
242 msgId: 'role_menuitemcheckbox' 246 msgId: 'role_menuitemcheckbox'
243 }, 247 },
(...skipping 17 matching lines...) Expand all
261 popUpButton: { 265 popUpButton: {
262 msgId: 'role_button', 266 msgId: 'role_button',
263 earconId: 'POP_UP_BUTTON' 267 earconId: 'POP_UP_BUTTON'
264 }, 268 },
265 radioButton: { 269 radioButton: {
266 msgId: 'role_radio' 270 msgId: 'role_radio'
267 }, 271 },
268 radioGroup: { 272 radioGroup: {
269 msgId: 'role_radiogroup', 273 msgId: 'role_radiogroup',
270 }, 274 },
275 rootWebArea: {
276 outputContextFirst: true
277 },
271 row: { 278 row: {
272 msgId: 'role_row', 279 msgId: 'role_row',
273 inherits: 'abstractContainer' 280 inherits: 'abstractContainer'
274 }, 281 },
275 rowHeader: { 282 rowHeader: {
276 msgId: 'role_rowheader', 283 msgId: 'role_rowheader',
277 inherits: 'cell' 284 inherits: 'cell'
278 }, 285 },
279 scrollBar: { 286 scrollBar: {
280 msgId: 'role_scrollbar', 287 msgId: 'role_scrollbar',
(...skipping 610 matching lines...) Expand 10 before | Expand all | Expand 10 after
891 callback(); 898 callback();
892 }.bind(this); 899 }.bind(this);
893 return this; 900 return this;
894 }, 901 },
895 902
896 /** 903 /**
897 * Executes all specified output. 904 * Executes all specified output.
898 */ 905 */
899 go: function() { 906 go: function() {
900 // Speech. 907 // Speech.
901 var queueMode = this.queueMode_; 908 var queueMode = cvox.QueueMode.FLUSH;
902 this.speechBuffer_.forEach(function(buff, i, a) { 909 if (Output.forceModeForNextSpeechUtterance_ !== undefined)
903 if (Output.forceModeForNextSpeechUtterance_ !== undefined && 910 queueMode = Output.forceModeForNextSpeechUtterance_;
904 buff.length > 0) { 911 else if (this.queueMode_ !== undefined)
905 queueMode = Output.forceModeForNextSpeechUtterance_; 912 queueMode = this.queueMode_;
906 Output.forceModeForNextSpeechUtterance_ = undefined;
907 }
908 913
909 var speechProps = {}; 914 if (this.speechBuffer_.length > 0)
915 Output.forceModeForNextSpeechUtterance_ = undefined;
916
917 for (var i = 0; i < this.speechBuffer_.length; i++) {
918 var buff = this.speechBuffer_[i];
919 var speechProps = /** @type {Object} */(
920 buff.getSpanInstanceOf(Output.SpeechProperties)) || {};
921
922 speechProps.category = this.speechCategory_;
923
910 (function() { 924 (function() {
911 var scopedBuff = buff; 925 var scopedBuff = buff;
912 speechProps =
913 scopedBuff.getSpanInstanceOf(Output.SpeechProperties) || {};
914 speechProps.category = this.speechCategory_;
915
916 speechProps['startCallback'] = function() { 926 speechProps['startCallback'] = function() {
917 var actions = scopedBuff.getSpansInstanceOf(Output.Action); 927 var actions = scopedBuff.getSpansInstanceOf(Output.Action);
918 if (actions) { 928 if (actions) {
919 actions.forEach(function(a) { 929 actions.forEach(function(a) {
920 a.run(); 930 a.run();
921 }); 931 });
922 } 932 }
923 }; 933 };
924 }.bind(this)()); 934 }());
925 935
926 if (this.speechEndCallback_ && i == a.length - 1) 936 if (i == this.speechBuffer_.length - 1)
927 speechProps['endCallback'] = this.speechEndCallback_; 937 speechProps['endCallback'] = this.speechEndCallback_;
928 else 938
929 speechProps['endCallback'] = null;
930 cvox.ChromeVox.tts.speak( 939 cvox.ChromeVox.tts.speak(
931 buff.toString(), queueMode, speechProps); 940 buff.toString(), queueMode, speechProps);
932 queueMode = cvox.QueueMode.QUEUE; 941 queueMode = cvox.QueueMode.QUEUE;
933 }.bind(this)); 942 }
934 943
935 // Braille. 944 // Braille.
936 if (this.brailleBuffer_.length) { 945 if (this.brailleBuffer_.length) {
937 var buff = this.createBrailleOutput_(); 946 var buff = this.createBrailleOutput_();
938 var selSpan = 947 var selSpan =
939 buff.getSpanInstanceOf(Output.SelectionSpan); 948 buff.getSpanInstanceOf(Output.SelectionSpan);
940 var startIndex = -1, endIndex = -1; 949 var startIndex = -1, endIndex = -1;
941 if (selSpan) { 950 if (selSpan) {
942 var valueStart = buff.getSpanStart(selSpan); 951 var valueStart = buff.getSpanStart(selSpan);
943 var valueEnd = buff.getSpanEnd(selSpan); 952 var valueEnd = buff.getSpanEnd(selSpan);
(...skipping 23 matching lines...) Expand all
967 * @param {!cursors.Range} range 976 * @param {!cursors.Range} range
968 * @param {cursors.Range} prevRange 977 * @param {cursors.Range} prevRange
969 * @param {EventType|Output.EventType} type 978 * @param {EventType|Output.EventType} type
970 * @param {!Array<Spannable>} buff Buffer to receive rendered output. 979 * @param {!Array<Spannable>} buff Buffer to receive rendered output.
971 * @private 980 * @private
972 */ 981 */
973 render_: function(range, prevRange, type, buff) { 982 render_: function(range, prevRange, type, buff) {
974 if (prevRange && !prevRange.isValid()) 983 if (prevRange && !prevRange.isValid())
975 prevRange = null; 984 prevRange = null;
976 985
986 // Scan ancestors to get the value of |outputContextFirst|.
987 var parent = range.start.node;
988 while (parent && parent.root && parent.root.role != RoleType.desktop) {
989 if (Output.ROLE_INFO_[parent.role] &&
990 Output.ROLE_INFO_[parent.role].outputContextFirst) {
991 this.outputContextFirst_ = true;
992 break;
993 }
994 parent = parent.parent;
995 }
996
977 if (range.isSubNode()) 997 if (range.isSubNode())
978 this.subNode_(range, prevRange, type, buff); 998 this.subNode_(range, prevRange, type, buff);
979 else 999 else
980 this.range_(range, prevRange, type, buff); 1000 this.range_(range, prevRange, type, buff);
981 }, 1001 },
982 1002
983 /** 1003 /**
984 * Format the node given the format specifier. 1004 * Format the node given the format specifier.
985 * @param {AutomationNode} node 1005 * @param {AutomationNode} node
986 * @param {string|!Object} format The output format either specified as an 1006 * @param {string|!Object} format The output format either specified as an
(...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after
1364 }, 1384 },
1365 1385
1366 /** 1386 /**
1367 * @param {!AutomationNode} node 1387 * @param {!AutomationNode} node
1368 * @param {!AutomationNode} prevNode 1388 * @param {!AutomationNode} prevNode
1369 * @param {EventType|Output.EventType} type 1389 * @param {EventType|Output.EventType} type
1370 * @param {!Array<Spannable>} buff 1390 * @param {!Array<Spannable>} buff
1371 * @private 1391 * @private
1372 */ 1392 */
1373 ancestry_: function(node, prevNode, type, buff) { 1393 ancestry_: function(node, prevNode, type, buff) {
1374 // Check to see if ancestry output is ignored.
1375 if (Output.ROLE_INFO_[node.role] &&
1376 Output.ROLE_INFO_[node.role].ignoreAncestry)
1377 return;
1378
1379 var prevUniqueAncestors = 1394 var prevUniqueAncestors =
1380 AutomationUtil.getUniqueAncestors(node, prevNode); 1395 AutomationUtil.getUniqueAncestors(node, prevNode);
1381 var uniqueAncestors = AutomationUtil.getUniqueAncestors(prevNode, node); 1396 var uniqueAncestors = AutomationUtil.getUniqueAncestors(prevNode, node);
1382 1397
1383 // First, look up the event type's format block. 1398 // First, look up the event type's format block.
1384 // Navigate is the default event. 1399 // Navigate is the default event.
1385 var eventBlock = Output.RULES[type] || Output.RULES['navigate']; 1400 var eventBlock = Output.RULES[type] || Output.RULES['navigate'];
1386 1401
1387 var getMergedRoleBlock = function(role) { 1402 var getMergedRoleBlock = function(role) {
1388 var parentRole = (Output.ROLE_INFO_[role] || {}).inherits; 1403 var parentRole = (Output.ROLE_INFO_[role] || {}).inherits;
(...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after
1715 /** 1730 /**
1716 * Gets the output buffer for braille. 1731 * Gets the output buffer for braille.
1717 * @return {!Spannable} 1732 * @return {!Spannable}
1718 */ 1733 */
1719 get brailleOutputForTest() { 1734 get brailleOutputForTest() {
1720 return this.createBrailleOutput_(); 1735 return this.createBrailleOutput_();
1721 } 1736 }
1722 }; 1737 };
1723 1738
1724 }); // goog.scope 1739 }); // goog.scope
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698