OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |