Wed, 31 Dec 2014 07:16:47 +0100
Revert simplistic fix pending revisit of Mozilla integration attempt.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | 'use strict'; |
michael@0 | 6 | |
michael@0 | 7 | const Cc = Components.classes; |
michael@0 | 8 | const Ci = Components.interfaces; |
michael@0 | 9 | const Cu = Components.utils; |
michael@0 | 10 | const Cr = Components.results; |
michael@0 | 11 | |
michael@0 | 12 | const INCLUDE_DESC = 0x01; |
michael@0 | 13 | const INCLUDE_NAME = 0x02; |
michael@0 | 14 | const INCLUDE_VALUE = 0x04; |
michael@0 | 15 | const INCLUDE_CUSTOM = 0x08; |
michael@0 | 16 | const NAME_FROM_SUBTREE_RULE = 0x10; |
michael@0 | 17 | const IGNORE_EXPLICIT_NAME = 0x20; |
michael@0 | 18 | |
michael@0 | 19 | const OUTPUT_DESC_FIRST = 0; |
michael@0 | 20 | const OUTPUT_DESC_LAST = 1; |
michael@0 | 21 | |
michael@0 | 22 | Cu.import('resource://gre/modules/XPCOMUtils.jsm'); |
michael@0 | 23 | XPCOMUtils.defineLazyModuleGetter(this, 'Utils', |
michael@0 | 24 | 'resource://gre/modules/accessibility/Utils.jsm'); |
michael@0 | 25 | XPCOMUtils.defineLazyModuleGetter(this, 'PrefCache', |
michael@0 | 26 | 'resource://gre/modules/accessibility/Utils.jsm'); |
michael@0 | 27 | XPCOMUtils.defineLazyModuleGetter(this, 'Logger', |
michael@0 | 28 | 'resource://gre/modules/accessibility/Utils.jsm'); |
michael@0 | 29 | XPCOMUtils.defineLazyModuleGetter(this, 'PluralForm', |
michael@0 | 30 | 'resource://gre/modules/PluralForm.jsm'); |
michael@0 | 31 | XPCOMUtils.defineLazyModuleGetter(this, 'Roles', |
michael@0 | 32 | 'resource://gre/modules/accessibility/Constants.jsm'); |
michael@0 | 33 | XPCOMUtils.defineLazyModuleGetter(this, 'States', |
michael@0 | 34 | 'resource://gre/modules/accessibility/Constants.jsm'); |
michael@0 | 35 | |
michael@0 | 36 | this.EXPORTED_SYMBOLS = ['UtteranceGenerator', 'BrailleGenerator']; |
michael@0 | 37 | |
michael@0 | 38 | this.OutputGenerator = { |
michael@0 | 39 | |
michael@0 | 40 | defaultOutputOrder: OUTPUT_DESC_LAST, |
michael@0 | 41 | |
michael@0 | 42 | /** |
michael@0 | 43 | * Generates output for a PivotContext. |
michael@0 | 44 | * @param {PivotContext} aContext object that generates and caches |
michael@0 | 45 | * context information for a given accessible and its relationship with |
michael@0 | 46 | * another accessible. |
michael@0 | 47 | * @return {Object} An object that neccessarily has an output property which |
michael@0 | 48 | * is an array of strings. Depending on the utterance order, |
michael@0 | 49 | * the strings describe the context for an accessible object either |
michael@0 | 50 | * starting from the accessible's ancestry or accessible's subtree. |
michael@0 | 51 | * The object may also have properties specific to the type of output |
michael@0 | 52 | * generated. |
michael@0 | 53 | */ |
michael@0 | 54 | genForContext: function genForContext(aContext) { |
michael@0 | 55 | let output = []; |
michael@0 | 56 | let self = this; |
michael@0 | 57 | let addOutput = function addOutput(aAccessible) { |
michael@0 | 58 | output.push.apply(output, self.genForObject(aAccessible, aContext)); |
michael@0 | 59 | }; |
michael@0 | 60 | let ignoreSubtree = function ignoreSubtree(aAccessible) { |
michael@0 | 61 | let roleString = Utils.AccRetrieval.getStringRole(aAccessible.role); |
michael@0 | 62 | let nameRule = self.roleRuleMap[roleString] || 0; |
michael@0 | 63 | // Ignore subtree if the name is explicit and the role's name rule is the |
michael@0 | 64 | // NAME_FROM_SUBTREE_RULE. |
michael@0 | 65 | return (((nameRule & INCLUDE_VALUE) && aAccessible.value) || |
michael@0 | 66 | ((nameRule & NAME_FROM_SUBTREE_RULE) && |
michael@0 | 67 | (Utils.getAttributes(aAccessible)['explicit-name'] === 'true' && |
michael@0 | 68 | !(nameRule & IGNORE_EXPLICIT_NAME)))); |
michael@0 | 69 | }; |
michael@0 | 70 | |
michael@0 | 71 | let contextStart = this._getContextStart(aContext); |
michael@0 | 72 | |
michael@0 | 73 | if (this.outputOrder === OUTPUT_DESC_FIRST) { |
michael@0 | 74 | contextStart.forEach(addOutput); |
michael@0 | 75 | addOutput(aContext.accessible); |
michael@0 | 76 | [addOutput(node) for |
michael@0 | 77 | (node of aContext.subtreeGenerator(true, ignoreSubtree))]; |
michael@0 | 78 | } else { |
michael@0 | 79 | [addOutput(node) for |
michael@0 | 80 | (node of aContext.subtreeGenerator(false, ignoreSubtree))]; |
michael@0 | 81 | addOutput(aContext.accessible); |
michael@0 | 82 | contextStart.reverse().forEach(addOutput); |
michael@0 | 83 | } |
michael@0 | 84 | |
michael@0 | 85 | // Clean up the white space. |
michael@0 | 86 | let trimmed; |
michael@0 | 87 | output = [trimmed for (word of output) if (trimmed = word.trim())]; |
michael@0 | 88 | return {output: output}; |
michael@0 | 89 | }, |
michael@0 | 90 | |
michael@0 | 91 | |
michael@0 | 92 | /** |
michael@0 | 93 | * Generates output for an object. |
michael@0 | 94 | * @param {nsIAccessible} aAccessible accessible object to generate output |
michael@0 | 95 | * for. |
michael@0 | 96 | * @param {PivotContext} aContext object that generates and caches |
michael@0 | 97 | * context information for a given accessible and its relationship with |
michael@0 | 98 | * another accessible. |
michael@0 | 99 | * @return {Array} Two string array. The first string describes the object |
michael@0 | 100 | * and its state. The second string is the object's name. Whether the |
michael@0 | 101 | * object's description or it's role is included is determined by |
michael@0 | 102 | * {@link roleRuleMap}. |
michael@0 | 103 | */ |
michael@0 | 104 | genForObject: function genForObject(aAccessible, aContext) { |
michael@0 | 105 | let roleString = Utils.AccRetrieval.getStringRole(aAccessible.role); |
michael@0 | 106 | let func = this.objectOutputFunctions[ |
michael@0 | 107 | OutputGenerator._getOutputName(roleString)] || |
michael@0 | 108 | this.objectOutputFunctions.defaultFunc; |
michael@0 | 109 | |
michael@0 | 110 | let flags = this.roleRuleMap[roleString] || 0; |
michael@0 | 111 | |
michael@0 | 112 | if (aAccessible.childCount == 0) |
michael@0 | 113 | flags |= INCLUDE_NAME; |
michael@0 | 114 | |
michael@0 | 115 | return func.apply(this, [aAccessible, roleString, |
michael@0 | 116 | Utils.getState(aAccessible), flags, aContext]); |
michael@0 | 117 | }, |
michael@0 | 118 | |
michael@0 | 119 | /** |
michael@0 | 120 | * Generates output for an action performed. |
michael@0 | 121 | * @param {nsIAccessible} aAccessible accessible object that the action was |
michael@0 | 122 | * invoked in. |
michael@0 | 123 | * @param {string} aActionName the name of the action, one of the keys in |
michael@0 | 124 | * {@link gActionMap}. |
michael@0 | 125 | * @return {Array} A one string array with the action. |
michael@0 | 126 | */ |
michael@0 | 127 | genForAction: function genForAction(aObject, aActionName) {}, |
michael@0 | 128 | |
michael@0 | 129 | /** |
michael@0 | 130 | * Generates output for an announcement. Basically attempts to localize |
michael@0 | 131 | * the announcement string. |
michael@0 | 132 | * @param {string} aAnnouncement unlocalized announcement. |
michael@0 | 133 | * @return {Array} A one string array with the announcement. |
michael@0 | 134 | */ |
michael@0 | 135 | genForAnnouncement: function genForAnnouncement(aAnnouncement) {}, |
michael@0 | 136 | |
michael@0 | 137 | /** |
michael@0 | 138 | * Generates output for a tab state change. |
michael@0 | 139 | * @param {nsIAccessible} aAccessible accessible object of the tab's attached |
michael@0 | 140 | * document. |
michael@0 | 141 | * @param {string} aTabState the tab state name, see |
michael@0 | 142 | * {@link Presenter.tabStateChanged}. |
michael@0 | 143 | * @return {Array} The tab state utterace. |
michael@0 | 144 | */ |
michael@0 | 145 | genForTabStateChange: function genForTabStateChange(aObject, aTabState) {}, |
michael@0 | 146 | |
michael@0 | 147 | /** |
michael@0 | 148 | * Generates output for announcing entering and leaving editing mode. |
michael@0 | 149 | * @param {aIsEditing} boolean true if we are in editing mode |
michael@0 | 150 | * @return {Array} The mode utterance |
michael@0 | 151 | */ |
michael@0 | 152 | genForEditingMode: function genForEditingMode(aIsEditing) {}, |
michael@0 | 153 | |
michael@0 | 154 | _getContextStart: function getContextStart(aContext) {}, |
michael@0 | 155 | |
michael@0 | 156 | _addName: function _addName(aOutput, aAccessible, aFlags) { |
michael@0 | 157 | let name; |
michael@0 | 158 | if ((Utils.getAttributes(aAccessible)['explicit-name'] === 'true' && |
michael@0 | 159 | !(aFlags & IGNORE_EXPLICIT_NAME)) || (aFlags & INCLUDE_NAME)) { |
michael@0 | 160 | name = aAccessible.name; |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | let description = aAccessible.description; |
michael@0 | 164 | if (description) { |
michael@0 | 165 | // Compare against the calculated name unconditionally, regardless of name rule, |
michael@0 | 166 | // so we can make sure we don't speak duplicated descriptions |
michael@0 | 167 | let tmpName = name || aAccessible.name; |
michael@0 | 168 | if (tmpName && (description !== tmpName)) { |
michael@0 | 169 | name = name || ''; |
michael@0 | 170 | name = this.outputOrder === OUTPUT_DESC_FIRST ? |
michael@0 | 171 | description + ' - ' + name : |
michael@0 | 172 | name + ' - ' + description; |
michael@0 | 173 | } |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | if (name) { |
michael@0 | 177 | aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? |
michael@0 | 178 | 'push' : 'unshift'](name); |
michael@0 | 179 | } |
michael@0 | 180 | }, |
michael@0 | 181 | |
michael@0 | 182 | /** |
michael@0 | 183 | * Adds a landmark role to the output if available. |
michael@0 | 184 | * @param {Array} aOutput Output array. |
michael@0 | 185 | * @param {nsIAccessible} aAccessible current accessible object. |
michael@0 | 186 | */ |
michael@0 | 187 | _addLandmark: function _addLandmark(aOutput, aAccessible) { |
michael@0 | 188 | let landmarkName = Utils.getLandmarkName(aAccessible); |
michael@0 | 189 | if (!landmarkName) { |
michael@0 | 190 | return; |
michael@0 | 191 | } |
michael@0 | 192 | |
michael@0 | 193 | let landmark = Utils.stringBundle.GetStringFromName(landmarkName); |
michael@0 | 194 | if (!landmark) { |
michael@0 | 195 | return; |
michael@0 | 196 | } |
michael@0 | 197 | |
michael@0 | 198 | aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? 'unshift' : 'push']( |
michael@0 | 199 | landmark); |
michael@0 | 200 | }, |
michael@0 | 201 | |
michael@0 | 202 | /** |
michael@0 | 203 | * Adds an entry type attribute to the description if available. |
michael@0 | 204 | * @param {Array} aDesc Description array. |
michael@0 | 205 | * @param {nsIAccessible} aAccessible current accessible object. |
michael@0 | 206 | * @param {String} aRoleStr aAccessible's role string. |
michael@0 | 207 | */ |
michael@0 | 208 | _addType: function _addType(aDesc, aAccessible, aRoleStr) { |
michael@0 | 209 | if (aRoleStr !== 'entry') { |
michael@0 | 210 | return; |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | let typeName = Utils.getAttributes(aAccessible)['text-input-type']; |
michael@0 | 214 | // Ignore the the input type="text" case. |
michael@0 | 215 | if (!typeName || typeName === 'text') { |
michael@0 | 216 | return; |
michael@0 | 217 | } |
michael@0 | 218 | typeName = 'textInputType_' + typeName; |
michael@0 | 219 | try { |
michael@0 | 220 | aDesc.push(Utils.stringBundle.GetStringFromName(typeName)); |
michael@0 | 221 | } catch (x) { |
michael@0 | 222 | Logger.warning('Failed to get a string from a bundle for', typeName); |
michael@0 | 223 | } |
michael@0 | 224 | }, |
michael@0 | 225 | |
michael@0 | 226 | get outputOrder() { |
michael@0 | 227 | if (!this._utteranceOrder) { |
michael@0 | 228 | this._utteranceOrder = new PrefCache('accessibility.accessfu.utterance'); |
michael@0 | 229 | } |
michael@0 | 230 | return typeof this._utteranceOrder.value === 'number' ? |
michael@0 | 231 | this._utteranceOrder.value : this.defaultOutputOrder; |
michael@0 | 232 | }, |
michael@0 | 233 | |
michael@0 | 234 | _getOutputName: function _getOutputName(aName) { |
michael@0 | 235 | return aName.replace(' ', ''); |
michael@0 | 236 | }, |
michael@0 | 237 | |
michael@0 | 238 | _getLocalizedRole: function _getLocalizedRole(aRoleStr) {}, |
michael@0 | 239 | |
michael@0 | 240 | _getLocalizedState: function _getLocalizedState(aState) {}, |
michael@0 | 241 | |
michael@0 | 242 | _getPluralFormString: function _getPluralFormString(aString, aCount) { |
michael@0 | 243 | let str = Utils.stringBundle.GetStringFromName(this._getOutputName(aString)); |
michael@0 | 244 | str = PluralForm.get(aCount, str); |
michael@0 | 245 | return str.replace('#1', aCount); |
michael@0 | 246 | }, |
michael@0 | 247 | |
michael@0 | 248 | roleRuleMap: { |
michael@0 | 249 | 'menubar': INCLUDE_DESC, |
michael@0 | 250 | 'scrollbar': INCLUDE_DESC, |
michael@0 | 251 | 'grip': INCLUDE_DESC, |
michael@0 | 252 | 'alert': INCLUDE_DESC | INCLUDE_NAME, |
michael@0 | 253 | 'menupopup': INCLUDE_DESC, |
michael@0 | 254 | 'menuitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 255 | 'tooltip': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 256 | 'columnheader': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 257 | 'rowheader': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 258 | 'column': NAME_FROM_SUBTREE_RULE, |
michael@0 | 259 | 'row': NAME_FROM_SUBTREE_RULE, |
michael@0 | 260 | 'cell': INCLUDE_DESC | INCLUDE_NAME, |
michael@0 | 261 | 'application': INCLUDE_NAME, |
michael@0 | 262 | 'document': INCLUDE_NAME, |
michael@0 | 263 | 'grouping': INCLUDE_DESC | INCLUDE_NAME, |
michael@0 | 264 | 'toolbar': INCLUDE_DESC, |
michael@0 | 265 | 'table': INCLUDE_DESC | INCLUDE_NAME, |
michael@0 | 266 | 'link': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 267 | 'helpballoon': NAME_FROM_SUBTREE_RULE, |
michael@0 | 268 | 'list': INCLUDE_DESC | INCLUDE_NAME, |
michael@0 | 269 | 'listitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 270 | 'outline': INCLUDE_DESC, |
michael@0 | 271 | 'outlineitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 272 | 'pagetab': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 273 | 'graphic': INCLUDE_DESC, |
michael@0 | 274 | 'pushbutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 275 | 'checkbutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 276 | 'radiobutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 277 | 'buttondropdown': NAME_FROM_SUBTREE_RULE, |
michael@0 | 278 | 'combobox': INCLUDE_DESC, |
michael@0 | 279 | 'droplist': INCLUDE_DESC, |
michael@0 | 280 | 'progressbar': INCLUDE_DESC | INCLUDE_VALUE, |
michael@0 | 281 | 'slider': INCLUDE_DESC | INCLUDE_VALUE, |
michael@0 | 282 | 'spinbutton': INCLUDE_DESC | INCLUDE_VALUE, |
michael@0 | 283 | 'diagram': INCLUDE_DESC, |
michael@0 | 284 | 'animation': INCLUDE_DESC, |
michael@0 | 285 | 'equation': INCLUDE_DESC, |
michael@0 | 286 | 'buttonmenu': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 287 | 'buttondropdowngrid': NAME_FROM_SUBTREE_RULE, |
michael@0 | 288 | 'pagetablist': INCLUDE_DESC, |
michael@0 | 289 | 'canvas': INCLUDE_DESC, |
michael@0 | 290 | 'check menu item': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 291 | 'label': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 292 | 'password text': INCLUDE_DESC, |
michael@0 | 293 | 'popup menu': INCLUDE_DESC, |
michael@0 | 294 | 'radio menu item': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 295 | 'table column header': NAME_FROM_SUBTREE_RULE, |
michael@0 | 296 | 'table row header': NAME_FROM_SUBTREE_RULE, |
michael@0 | 297 | 'tear off menu item': NAME_FROM_SUBTREE_RULE, |
michael@0 | 298 | 'toggle button': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 299 | 'parent menuitem': NAME_FROM_SUBTREE_RULE, |
michael@0 | 300 | 'header': INCLUDE_DESC, |
michael@0 | 301 | 'footer': INCLUDE_DESC, |
michael@0 | 302 | 'entry': INCLUDE_DESC | INCLUDE_NAME | INCLUDE_VALUE, |
michael@0 | 303 | 'caption': INCLUDE_DESC, |
michael@0 | 304 | 'document frame': INCLUDE_DESC, |
michael@0 | 305 | 'heading': INCLUDE_DESC, |
michael@0 | 306 | 'calendar': INCLUDE_DESC | INCLUDE_NAME, |
michael@0 | 307 | 'combobox list': INCLUDE_DESC, |
michael@0 | 308 | 'combobox option': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 309 | 'listbox option': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE, |
michael@0 | 310 | 'listbox rich option': NAME_FROM_SUBTREE_RULE, |
michael@0 | 311 | 'gridcell': NAME_FROM_SUBTREE_RULE, |
michael@0 | 312 | 'check rich option': NAME_FROM_SUBTREE_RULE, |
michael@0 | 313 | 'term': NAME_FROM_SUBTREE_RULE, |
michael@0 | 314 | 'definition': NAME_FROM_SUBTREE_RULE, |
michael@0 | 315 | 'key': NAME_FROM_SUBTREE_RULE, |
michael@0 | 316 | 'image map': INCLUDE_DESC, |
michael@0 | 317 | 'option': INCLUDE_DESC, |
michael@0 | 318 | 'listbox': INCLUDE_DESC, |
michael@0 | 319 | 'definitionlist': INCLUDE_DESC | INCLUDE_NAME, |
michael@0 | 320 | 'dialog': INCLUDE_DESC | INCLUDE_NAME, |
michael@0 | 321 | 'chrome window': IGNORE_EXPLICIT_NAME, |
michael@0 | 322 | 'app root': IGNORE_EXPLICIT_NAME }, |
michael@0 | 323 | |
michael@0 | 324 | objectOutputFunctions: { |
michael@0 | 325 | _generateBaseOutput: function _generateBaseOutput(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 326 | let output = []; |
michael@0 | 327 | |
michael@0 | 328 | if (aFlags & INCLUDE_DESC) { |
michael@0 | 329 | let desc = this._getLocalizedState(aState); |
michael@0 | 330 | let roleStr = this._getLocalizedRole(aRoleStr); |
michael@0 | 331 | if (roleStr) { |
michael@0 | 332 | this._addType(desc, aAccessible, aRoleStr); |
michael@0 | 333 | desc.push(roleStr); |
michael@0 | 334 | } |
michael@0 | 335 | output.push(desc.join(' ')); |
michael@0 | 336 | } |
michael@0 | 337 | |
michael@0 | 338 | if (aFlags & INCLUDE_VALUE) { |
michael@0 | 339 | let value = aAccessible.value; |
michael@0 | 340 | if (value) { |
michael@0 | 341 | output[this.outputOrder === OUTPUT_DESC_FIRST ? |
michael@0 | 342 | 'push' : 'unshift'](value); |
michael@0 | 343 | } |
michael@0 | 344 | } |
michael@0 | 345 | |
michael@0 | 346 | this._addName(output, aAccessible, aFlags); |
michael@0 | 347 | this._addLandmark(output, aAccessible); |
michael@0 | 348 | |
michael@0 | 349 | return output; |
michael@0 | 350 | }, |
michael@0 | 351 | |
michael@0 | 352 | label: function label(aAccessible, aRoleStr, aState, aFlags, aContext) { |
michael@0 | 353 | if (aContext.isNestedControl || |
michael@0 | 354 | aContext.accessible == Utils.getEmbeddedControl(aAccessible)) { |
michael@0 | 355 | // If we are on a nested control, or a nesting label, |
michael@0 | 356 | // we don't need the context. |
michael@0 | 357 | return []; |
michael@0 | 358 | } |
michael@0 | 359 | |
michael@0 | 360 | return this.objectOutputFunctions.defaultFunc.apply(this, arguments); |
michael@0 | 361 | }, |
michael@0 | 362 | |
michael@0 | 363 | entry: function entry(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 364 | let rolestr = aState.contains(States.MULTI_LINE) ? 'textarea' : 'entry'; |
michael@0 | 365 | return this.objectOutputFunctions.defaultFunc.apply( |
michael@0 | 366 | this, [aAccessible, rolestr, aState, aFlags]); |
michael@0 | 367 | }, |
michael@0 | 368 | |
michael@0 | 369 | pagetab: function pagetab(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 370 | let localizedRole = this._getLocalizedRole(aRoleStr); |
michael@0 | 371 | let itemno = {}; |
michael@0 | 372 | let itemof = {}; |
michael@0 | 373 | aAccessible.groupPosition({}, itemof, itemno); |
michael@0 | 374 | let output = []; |
michael@0 | 375 | let desc = this._getLocalizedState(aState); |
michael@0 | 376 | desc.push( |
michael@0 | 377 | Utils.stringBundle.formatStringFromName( |
michael@0 | 378 | 'objItemOf', [localizedRole, itemno.value, itemof.value], 3)); |
michael@0 | 379 | output.push(desc.join(' ')); |
michael@0 | 380 | |
michael@0 | 381 | this._addName(output, aAccessible, aFlags); |
michael@0 | 382 | this._addLandmark(output, aAccessible); |
michael@0 | 383 | |
michael@0 | 384 | return output; |
michael@0 | 385 | }, |
michael@0 | 386 | |
michael@0 | 387 | table: function table(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 388 | let output = []; |
michael@0 | 389 | let table; |
michael@0 | 390 | try { |
michael@0 | 391 | table = aAccessible.QueryInterface(Ci.nsIAccessibleTable); |
michael@0 | 392 | } catch (x) { |
michael@0 | 393 | Logger.logException(x); |
michael@0 | 394 | return output; |
michael@0 | 395 | } finally { |
michael@0 | 396 | // Check if it's a layout table, and bail out if true. |
michael@0 | 397 | // We don't want to speak any table information for layout tables. |
michael@0 | 398 | if (table.isProbablyForLayout()) { |
michael@0 | 399 | return output; |
michael@0 | 400 | } |
michael@0 | 401 | let tableColumnInfo = this._getPluralFormString('tableColumnInfo', |
michael@0 | 402 | table.columnCount); |
michael@0 | 403 | let tableRowInfo = this._getPluralFormString('tableRowInfo', |
michael@0 | 404 | table.rowCount); |
michael@0 | 405 | output.push(Utils.stringBundle.formatStringFromName( |
michael@0 | 406 | this._getOutputName('tableInfo'), [this._getLocalizedRole(aRoleStr), |
michael@0 | 407 | tableColumnInfo, tableRowInfo], 3)); |
michael@0 | 408 | this._addName(output, aAccessible, aFlags); |
michael@0 | 409 | this._addLandmark(output, aAccessible); |
michael@0 | 410 | return output; |
michael@0 | 411 | } |
michael@0 | 412 | } |
michael@0 | 413 | } |
michael@0 | 414 | }; |
michael@0 | 415 | |
michael@0 | 416 | /** |
michael@0 | 417 | * Generates speech utterances from objects, actions and state changes. |
michael@0 | 418 | * An utterance is an array of strings. |
michael@0 | 419 | * |
michael@0 | 420 | * It should not be assumed that flattening an utterance array would create a |
michael@0 | 421 | * gramatically correct sentence. For example, {@link genForObject} might |
michael@0 | 422 | * return: ['graphic', 'Welcome to my home page']. |
michael@0 | 423 | * Each string element in an utterance should be gramatically correct in itself. |
michael@0 | 424 | * Another example from {@link genForObject}: ['list item 2 of 5', 'Alabama']. |
michael@0 | 425 | * |
michael@0 | 426 | * An utterance is ordered from the least to the most important. Speaking the |
michael@0 | 427 | * last string usually makes sense, but speaking the first often won't. |
michael@0 | 428 | * For example {@link genForAction} might return ['button', 'clicked'] for a |
michael@0 | 429 | * clicked event. Speaking only 'clicked' makes sense. Speaking 'button' does |
michael@0 | 430 | * not. |
michael@0 | 431 | */ |
michael@0 | 432 | this.UtteranceGenerator = { |
michael@0 | 433 | __proto__: OutputGenerator, |
michael@0 | 434 | |
michael@0 | 435 | gActionMap: { |
michael@0 | 436 | jump: 'jumpAction', |
michael@0 | 437 | press: 'pressAction', |
michael@0 | 438 | check: 'checkAction', |
michael@0 | 439 | uncheck: 'uncheckAction', |
michael@0 | 440 | select: 'selectAction', |
michael@0 | 441 | unselect: 'unselectAction', |
michael@0 | 442 | open: 'openAction', |
michael@0 | 443 | close: 'closeAction', |
michael@0 | 444 | switch: 'switchAction', |
michael@0 | 445 | click: 'clickAction', |
michael@0 | 446 | collapse: 'collapseAction', |
michael@0 | 447 | expand: 'expandAction', |
michael@0 | 448 | activate: 'activateAction', |
michael@0 | 449 | cycle: 'cycleAction' |
michael@0 | 450 | }, |
michael@0 | 451 | |
michael@0 | 452 | //TODO: May become more verbose in the future. |
michael@0 | 453 | genForAction: function genForAction(aObject, aActionName) { |
michael@0 | 454 | return [Utils.stringBundle.GetStringFromName(this.gActionMap[aActionName])]; |
michael@0 | 455 | }, |
michael@0 | 456 | |
michael@0 | 457 | genForLiveRegion: function genForLiveRegion(aContext, aIsHide, aModifiedText) { |
michael@0 | 458 | let utterance = []; |
michael@0 | 459 | if (aIsHide) { |
michael@0 | 460 | utterance.push(Utils.stringBundle.GetStringFromName('hidden')); |
michael@0 | 461 | } |
michael@0 | 462 | return utterance.concat( |
michael@0 | 463 | aModifiedText || this.genForContext(aContext).output); |
michael@0 | 464 | }, |
michael@0 | 465 | |
michael@0 | 466 | genForAnnouncement: function genForAnnouncement(aAnnouncement) { |
michael@0 | 467 | try { |
michael@0 | 468 | return [Utils.stringBundle.GetStringFromName(aAnnouncement)]; |
michael@0 | 469 | } catch (x) { |
michael@0 | 470 | return [aAnnouncement]; |
michael@0 | 471 | } |
michael@0 | 472 | }, |
michael@0 | 473 | |
michael@0 | 474 | genForTabStateChange: function genForTabStateChange(aObject, aTabState) { |
michael@0 | 475 | switch (aTabState) { |
michael@0 | 476 | case 'newtab': |
michael@0 | 477 | return [Utils.stringBundle.GetStringFromName('tabNew')]; |
michael@0 | 478 | case 'loading': |
michael@0 | 479 | return [Utils.stringBundle.GetStringFromName('tabLoading')]; |
michael@0 | 480 | case 'loaded': |
michael@0 | 481 | return [aObject.name || '', |
michael@0 | 482 | Utils.stringBundle.GetStringFromName('tabLoaded')]; |
michael@0 | 483 | case 'loadstopped': |
michael@0 | 484 | return [Utils.stringBundle.GetStringFromName('tabLoadStopped')]; |
michael@0 | 485 | case 'reload': |
michael@0 | 486 | return [Utils.stringBundle.GetStringFromName('tabReload')]; |
michael@0 | 487 | default: |
michael@0 | 488 | return []; |
michael@0 | 489 | } |
michael@0 | 490 | }, |
michael@0 | 491 | |
michael@0 | 492 | genForEditingMode: function genForEditingMode(aIsEditing) { |
michael@0 | 493 | return [Utils.stringBundle.GetStringFromName( |
michael@0 | 494 | aIsEditing ? 'editingMode' : 'navigationMode')]; |
michael@0 | 495 | }, |
michael@0 | 496 | |
michael@0 | 497 | objectOutputFunctions: { |
michael@0 | 498 | |
michael@0 | 499 | __proto__: OutputGenerator.objectOutputFunctions, |
michael@0 | 500 | |
michael@0 | 501 | defaultFunc: function defaultFunc(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 502 | return this.objectOutputFunctions._generateBaseOutput.apply(this, arguments); |
michael@0 | 503 | }, |
michael@0 | 504 | |
michael@0 | 505 | heading: function heading(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 506 | let level = {}; |
michael@0 | 507 | aAccessible.groupPosition(level, {}, {}); |
michael@0 | 508 | let utterance = |
michael@0 | 509 | [Utils.stringBundle.formatStringFromName( |
michael@0 | 510 | 'headingLevel', [level.value], 1)]; |
michael@0 | 511 | |
michael@0 | 512 | this._addName(utterance, aAccessible, aFlags); |
michael@0 | 513 | this._addLandmark(utterance, aAccessible); |
michael@0 | 514 | |
michael@0 | 515 | return utterance; |
michael@0 | 516 | }, |
michael@0 | 517 | |
michael@0 | 518 | listitem: function listitem(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 519 | let itemno = {}; |
michael@0 | 520 | let itemof = {}; |
michael@0 | 521 | aAccessible.groupPosition({}, itemof, itemno); |
michael@0 | 522 | let utterance = []; |
michael@0 | 523 | if (itemno.value == 1) // Start of list |
michael@0 | 524 | utterance.push(Utils.stringBundle.GetStringFromName('listStart')); |
michael@0 | 525 | else if (itemno.value == itemof.value) // last item |
michael@0 | 526 | utterance.push(Utils.stringBundle.GetStringFromName('listEnd')); |
michael@0 | 527 | |
michael@0 | 528 | this._addName(utterance, aAccessible, aFlags); |
michael@0 | 529 | this._addLandmark(utterance, aAccessible); |
michael@0 | 530 | |
michael@0 | 531 | return utterance; |
michael@0 | 532 | }, |
michael@0 | 533 | |
michael@0 | 534 | list: function list(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 535 | return this._getListUtterance |
michael@0 | 536 | (aAccessible, aRoleStr, aFlags, aAccessible.childCount); |
michael@0 | 537 | }, |
michael@0 | 538 | |
michael@0 | 539 | definitionlist: function definitionlist(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 540 | return this._getListUtterance |
michael@0 | 541 | (aAccessible, aRoleStr, aFlags, aAccessible.childCount / 2); |
michael@0 | 542 | }, |
michael@0 | 543 | |
michael@0 | 544 | application: function application(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 545 | // Don't utter location of applications, it gets tiring. |
michael@0 | 546 | if (aAccessible.name != aAccessible.DOMNode.location) |
michael@0 | 547 | return this.objectOutputFunctions.defaultFunc.apply(this, |
michael@0 | 548 | [aAccessible, aRoleStr, aState, aFlags]); |
michael@0 | 549 | |
michael@0 | 550 | return []; |
michael@0 | 551 | }, |
michael@0 | 552 | |
michael@0 | 553 | cell: function cell(aAccessible, aRoleStr, aState, aFlags, aContext) { |
michael@0 | 554 | let utterance = []; |
michael@0 | 555 | let cell = aContext.getCellInfo(aAccessible); |
michael@0 | 556 | if (cell) { |
michael@0 | 557 | let desc = []; |
michael@0 | 558 | let addCellChanged = function addCellChanged(aDesc, aChanged, aString, aIndex) { |
michael@0 | 559 | if (aChanged) { |
michael@0 | 560 | aDesc.push(Utils.stringBundle.formatStringFromName(aString, |
michael@0 | 561 | [aIndex + 1], 1)); |
michael@0 | 562 | } |
michael@0 | 563 | }; |
michael@0 | 564 | let addExtent = function addExtent(aDesc, aExtent, aString) { |
michael@0 | 565 | if (aExtent > 1) { |
michael@0 | 566 | aDesc.push(Utils.stringBundle.formatStringFromName(aString, |
michael@0 | 567 | [aExtent], 1)); |
michael@0 | 568 | } |
michael@0 | 569 | }; |
michael@0 | 570 | let addHeaders = function addHeaders(aDesc, aHeaders) { |
michael@0 | 571 | if (aHeaders.length > 0) { |
michael@0 | 572 | aDesc.push.apply(aDesc, aHeaders); |
michael@0 | 573 | } |
michael@0 | 574 | }; |
michael@0 | 575 | |
michael@0 | 576 | addCellChanged(desc, cell.columnChanged, 'columnInfo', cell.columnIndex); |
michael@0 | 577 | addCellChanged(desc, cell.rowChanged, 'rowInfo', cell.rowIndex); |
michael@0 | 578 | |
michael@0 | 579 | addExtent(desc, cell.columnExtent, 'spansColumns'); |
michael@0 | 580 | addExtent(desc, cell.rowExtent, 'spansRows'); |
michael@0 | 581 | |
michael@0 | 582 | addHeaders(desc, cell.columnHeaders); |
michael@0 | 583 | addHeaders(desc, cell.rowHeaders); |
michael@0 | 584 | |
michael@0 | 585 | utterance.push(desc.join(' ')); |
michael@0 | 586 | } |
michael@0 | 587 | |
michael@0 | 588 | this._addName(utterance, aAccessible, aFlags); |
michael@0 | 589 | this._addLandmark(utterance, aAccessible); |
michael@0 | 590 | |
michael@0 | 591 | return utterance; |
michael@0 | 592 | }, |
michael@0 | 593 | |
michael@0 | 594 | columnheader: function columnheader() { |
michael@0 | 595 | return this.objectOutputFunctions.cell.apply(this, arguments); |
michael@0 | 596 | }, |
michael@0 | 597 | |
michael@0 | 598 | rowheader: function rowheader() { |
michael@0 | 599 | return this.objectOutputFunctions.cell.apply(this, arguments); |
michael@0 | 600 | } |
michael@0 | 601 | }, |
michael@0 | 602 | |
michael@0 | 603 | _getContextStart: function _getContextStart(aContext) { |
michael@0 | 604 | return aContext.newAncestry; |
michael@0 | 605 | }, |
michael@0 | 606 | |
michael@0 | 607 | _getLocalizedRole: function _getLocalizedRole(aRoleStr) { |
michael@0 | 608 | try { |
michael@0 | 609 | return Utils.stringBundle.GetStringFromName( |
michael@0 | 610 | this._getOutputName(aRoleStr)); |
michael@0 | 611 | } catch (x) { |
michael@0 | 612 | return ''; |
michael@0 | 613 | } |
michael@0 | 614 | }, |
michael@0 | 615 | |
michael@0 | 616 | _getLocalizedState: function _getLocalizedState(aState) { |
michael@0 | 617 | let stateUtterances = []; |
michael@0 | 618 | |
michael@0 | 619 | if (aState.contains(States.UNAVAILABLE)) { |
michael@0 | 620 | stateUtterances.push( |
michael@0 | 621 | Utils.stringBundle.GetStringFromName('stateUnavailable')); |
michael@0 | 622 | } |
michael@0 | 623 | |
michael@0 | 624 | // Don't utter this in Jelly Bean, we let TalkBack do it for us there. |
michael@0 | 625 | // This is because we expose the checked information on the node itself. |
michael@0 | 626 | // XXX: this means the checked state is always appended to the end, regardless |
michael@0 | 627 | // of the utterance ordering preference. |
michael@0 | 628 | if ((Utils.AndroidSdkVersion < 16 || Utils.MozBuildApp === 'browser') && |
michael@0 | 629 | aState.contains(States.CHECKABLE)) { |
michael@0 | 630 | let statetr = aState.contains(States.CHECKED) ? |
michael@0 | 631 | 'stateChecked' : 'stateNotChecked'; |
michael@0 | 632 | stateUtterances.push(Utils.stringBundle.GetStringFromName(statetr)); |
michael@0 | 633 | } |
michael@0 | 634 | |
michael@0 | 635 | if (aState.contains(States.PRESSED)) { |
michael@0 | 636 | stateUtterances.push( |
michael@0 | 637 | Utils.stringBundle.GetStringFromName('statePressed')); |
michael@0 | 638 | } |
michael@0 | 639 | |
michael@0 | 640 | if (aState.contains(States.EXPANDABLE)) { |
michael@0 | 641 | let statetr = aState.contains(States.EXPANDED) ? |
michael@0 | 642 | 'stateExpanded' : 'stateCollapsed'; |
michael@0 | 643 | stateUtterances.push(Utils.stringBundle.GetStringFromName(statetr)); |
michael@0 | 644 | } |
michael@0 | 645 | |
michael@0 | 646 | if (aState.contains(States.REQUIRED)) { |
michael@0 | 647 | stateUtterances.push( |
michael@0 | 648 | Utils.stringBundle.GetStringFromName('stateRequired')); |
michael@0 | 649 | } |
michael@0 | 650 | |
michael@0 | 651 | if (aState.contains(States.TRAVERSED)) { |
michael@0 | 652 | stateUtterances.push( |
michael@0 | 653 | Utils.stringBundle.GetStringFromName('stateTraversed')); |
michael@0 | 654 | } |
michael@0 | 655 | |
michael@0 | 656 | if (aState.contains(States.HASPOPUP)) { |
michael@0 | 657 | stateUtterances.push( |
michael@0 | 658 | Utils.stringBundle.GetStringFromName('stateHasPopup')); |
michael@0 | 659 | } |
michael@0 | 660 | |
michael@0 | 661 | if (aState.contains(States.SELECTED)) { |
michael@0 | 662 | stateUtterances.push( |
michael@0 | 663 | Utils.stringBundle.GetStringFromName('stateSelected')); |
michael@0 | 664 | } |
michael@0 | 665 | |
michael@0 | 666 | return stateUtterances; |
michael@0 | 667 | }, |
michael@0 | 668 | |
michael@0 | 669 | _getListUtterance: function _getListUtterance(aAccessible, aRoleStr, aFlags, aItemCount) { |
michael@0 | 670 | let desc = []; |
michael@0 | 671 | let roleStr = this._getLocalizedRole(aRoleStr); |
michael@0 | 672 | if (roleStr) { |
michael@0 | 673 | desc.push(roleStr); |
michael@0 | 674 | } |
michael@0 | 675 | desc.push(this._getPluralFormString('listItemsCount', aItemCount)); |
michael@0 | 676 | let utterance = [desc.join(' ')]; |
michael@0 | 677 | |
michael@0 | 678 | this._addName(utterance, aAccessible, aFlags); |
michael@0 | 679 | this._addLandmark(utterance, aAccessible); |
michael@0 | 680 | |
michael@0 | 681 | return utterance; |
michael@0 | 682 | } |
michael@0 | 683 | }; |
michael@0 | 684 | |
michael@0 | 685 | |
michael@0 | 686 | this.BrailleGenerator = { |
michael@0 | 687 | __proto__: OutputGenerator, |
michael@0 | 688 | |
michael@0 | 689 | genForContext: function genForContext(aContext) { |
michael@0 | 690 | let output = OutputGenerator.genForContext.apply(this, arguments); |
michael@0 | 691 | |
michael@0 | 692 | let acc = aContext.accessible; |
michael@0 | 693 | |
michael@0 | 694 | // add the static text indicating a list item; do this for both listitems or |
michael@0 | 695 | // direct first children of listitems, because these are both common browsing |
michael@0 | 696 | // scenarios |
michael@0 | 697 | let addListitemIndicator = function addListitemIndicator(indicator = '*') { |
michael@0 | 698 | output.output.unshift(indicator); |
michael@0 | 699 | }; |
michael@0 | 700 | |
michael@0 | 701 | if (acc.indexInParent === 1 && |
michael@0 | 702 | acc.parent.role == Roles.LISTITEM && |
michael@0 | 703 | acc.previousSibling.role == Roles.STATICTEXT) { |
michael@0 | 704 | if (acc.parent.parent && acc.parent.parent.DOMNode && |
michael@0 | 705 | acc.parent.parent.DOMNode.nodeName == 'UL') { |
michael@0 | 706 | addListitemIndicator(); |
michael@0 | 707 | } else { |
michael@0 | 708 | addListitemIndicator(acc.previousSibling.name.trim()); |
michael@0 | 709 | } |
michael@0 | 710 | } else if (acc.role == Roles.LISTITEM && acc.firstChild && |
michael@0 | 711 | acc.firstChild.role == Roles.STATICTEXT) { |
michael@0 | 712 | if (acc.parent.DOMNode.nodeName == 'UL') { |
michael@0 | 713 | addListitemIndicator(); |
michael@0 | 714 | } else { |
michael@0 | 715 | addListitemIndicator(acc.firstChild.name.trim()); |
michael@0 | 716 | } |
michael@0 | 717 | } |
michael@0 | 718 | |
michael@0 | 719 | if (acc instanceof Ci.nsIAccessibleText) { |
michael@0 | 720 | output.endOffset = this.outputOrder === OUTPUT_DESC_FIRST ? |
michael@0 | 721 | output.output.join(' ').length : acc.characterCount; |
michael@0 | 722 | output.startOffset = output.endOffset - acc.characterCount; |
michael@0 | 723 | } |
michael@0 | 724 | |
michael@0 | 725 | return output; |
michael@0 | 726 | }, |
michael@0 | 727 | |
michael@0 | 728 | objectOutputFunctions: { |
michael@0 | 729 | |
michael@0 | 730 | __proto__: OutputGenerator.objectOutputFunctions, |
michael@0 | 731 | |
michael@0 | 732 | defaultFunc: function defaultFunc(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 733 | return this.objectOutputFunctions._generateBaseOutput.apply(this, arguments); |
michael@0 | 734 | }, |
michael@0 | 735 | |
michael@0 | 736 | listitem: function listitem(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 737 | let braille = []; |
michael@0 | 738 | |
michael@0 | 739 | this._addName(braille, aAccessible, aFlags); |
michael@0 | 740 | this._addLandmark(braille, aAccessible); |
michael@0 | 741 | |
michael@0 | 742 | return braille; |
michael@0 | 743 | }, |
michael@0 | 744 | |
michael@0 | 745 | cell: function cell(aAccessible, aRoleStr, aState, aFlags, aContext) { |
michael@0 | 746 | let braille = []; |
michael@0 | 747 | let cell = aContext.getCellInfo(aAccessible); |
michael@0 | 748 | if (cell) { |
michael@0 | 749 | let desc = []; |
michael@0 | 750 | let addHeaders = function addHeaders(aDesc, aHeaders) { |
michael@0 | 751 | if (aHeaders.length > 0) { |
michael@0 | 752 | aDesc.push.apply(aDesc, aHeaders); |
michael@0 | 753 | } |
michael@0 | 754 | }; |
michael@0 | 755 | |
michael@0 | 756 | desc.push(Utils.stringBundle.formatStringFromName( |
michael@0 | 757 | this._getOutputName('cellInfo'), [cell.columnIndex + 1, |
michael@0 | 758 | cell.rowIndex + 1], 2)); |
michael@0 | 759 | |
michael@0 | 760 | addHeaders(desc, cell.columnHeaders); |
michael@0 | 761 | addHeaders(desc, cell.rowHeaders); |
michael@0 | 762 | braille.push(desc.join(' ')); |
michael@0 | 763 | } |
michael@0 | 764 | |
michael@0 | 765 | this._addName(braille, aAccessible, aFlags); |
michael@0 | 766 | this._addLandmark(braille, aAccessible); |
michael@0 | 767 | return braille; |
michael@0 | 768 | }, |
michael@0 | 769 | |
michael@0 | 770 | columnheader: function columnheader() { |
michael@0 | 771 | return this.objectOutputFunctions.cell.apply(this, arguments); |
michael@0 | 772 | }, |
michael@0 | 773 | |
michael@0 | 774 | rowheader: function rowheader() { |
michael@0 | 775 | return this.objectOutputFunctions.cell.apply(this, arguments); |
michael@0 | 776 | }, |
michael@0 | 777 | |
michael@0 | 778 | statictext: function statictext(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 779 | // Since we customize the list bullet's output, we add the static |
michael@0 | 780 | // text from the first node in each listitem, so skip it here. |
michael@0 | 781 | if (aAccessible.parent.role == Roles.LISTITEM) { |
michael@0 | 782 | return []; |
michael@0 | 783 | } |
michael@0 | 784 | |
michael@0 | 785 | return this.objectOutputFunctions._useStateNotRole.apply(this, arguments); |
michael@0 | 786 | }, |
michael@0 | 787 | |
michael@0 | 788 | _useStateNotRole: function _useStateNotRole(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 789 | let braille = []; |
michael@0 | 790 | |
michael@0 | 791 | let desc = this._getLocalizedState(aState, aAccessible.role); |
michael@0 | 792 | braille.push(desc.join(' ')); |
michael@0 | 793 | |
michael@0 | 794 | this._addName(braille, aAccessible, aFlags); |
michael@0 | 795 | this._addLandmark(braille, aAccessible); |
michael@0 | 796 | |
michael@0 | 797 | return braille; |
michael@0 | 798 | }, |
michael@0 | 799 | |
michael@0 | 800 | checkbutton: function checkbutton(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 801 | return this.objectOutputFunctions._useStateNotRole.apply(this, arguments); |
michael@0 | 802 | }, |
michael@0 | 803 | |
michael@0 | 804 | radiobutton: function radiobutton(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 805 | return this.objectOutputFunctions._useStateNotRole.apply(this, arguments); |
michael@0 | 806 | }, |
michael@0 | 807 | |
michael@0 | 808 | togglebutton: function radiobutton(aAccessible, aRoleStr, aState, aFlags) { |
michael@0 | 809 | return this.objectOutputFunctions._useStateNotRole.apply(this, arguments); |
michael@0 | 810 | } |
michael@0 | 811 | }, |
michael@0 | 812 | |
michael@0 | 813 | _getContextStart: function _getContextStart(aContext) { |
michael@0 | 814 | if (aContext.accessible.parent.role == Roles.LINK) { |
michael@0 | 815 | return [aContext.accessible.parent]; |
michael@0 | 816 | } |
michael@0 | 817 | |
michael@0 | 818 | return []; |
michael@0 | 819 | }, |
michael@0 | 820 | |
michael@0 | 821 | _getOutputName: function _getOutputName(aName) { |
michael@0 | 822 | return OutputGenerator._getOutputName(aName) + 'Abbr'; |
michael@0 | 823 | }, |
michael@0 | 824 | |
michael@0 | 825 | _getLocalizedRole: function _getLocalizedRole(aRoleStr) { |
michael@0 | 826 | try { |
michael@0 | 827 | return Utils.stringBundle.GetStringFromName( |
michael@0 | 828 | this._getOutputName(aRoleStr)); |
michael@0 | 829 | } catch (x) { |
michael@0 | 830 | try { |
michael@0 | 831 | return Utils.stringBundle.GetStringFromName( |
michael@0 | 832 | OutputGenerator._getOutputName(aRoleStr)); |
michael@0 | 833 | } catch (y) { |
michael@0 | 834 | return ''; |
michael@0 | 835 | } |
michael@0 | 836 | } |
michael@0 | 837 | }, |
michael@0 | 838 | |
michael@0 | 839 | _getLocalizedState: function _getLocalizedState(aState, aRole) { |
michael@0 | 840 | let stateBraille = []; |
michael@0 | 841 | |
michael@0 | 842 | let getResultMarker = function getResultMarker(aMarker) { |
michael@0 | 843 | // aMarker is a simple boolean. |
michael@0 | 844 | let resultMarker = []; |
michael@0 | 845 | resultMarker.push('('); |
michael@0 | 846 | resultMarker.push(aMarker ? 'x' : ' '); |
michael@0 | 847 | resultMarker.push(')'); |
michael@0 | 848 | |
michael@0 | 849 | return resultMarker.join(''); |
michael@0 | 850 | }; |
michael@0 | 851 | |
michael@0 | 852 | if (aState.contains(States.CHECKABLE)) { |
michael@0 | 853 | stateBraille.push(getResultMarker(aState.contains(States.CHECKED))); |
michael@0 | 854 | } |
michael@0 | 855 | |
michael@0 | 856 | if (aRole === Roles.TOGGLE_BUTTON) { |
michael@0 | 857 | stateBraille.push(getResultMarker(aState.contains(States.PRESSED))); |
michael@0 | 858 | } |
michael@0 | 859 | |
michael@0 | 860 | return stateBraille; |
michael@0 | 861 | } |
michael@0 | 862 | |
michael@0 | 863 | }; |