| 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 |