accessible/src/jsat/TraversalRules.jsm

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

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 this.EXPORTED_SYMBOLS = ['TraversalRules'];
michael@0 13
michael@0 14 Cu.import('resource://gre/modules/accessibility/Utils.jsm');
michael@0 15 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
michael@0 16 XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
michael@0 17 'resource://gre/modules/accessibility/Constants.jsm');
michael@0 18 XPCOMUtils.defineLazyModuleGetter(this, 'Filters',
michael@0 19 'resource://gre/modules/accessibility/Constants.jsm');
michael@0 20 XPCOMUtils.defineLazyModuleGetter(this, 'States',
michael@0 21 'resource://gre/modules/accessibility/Constants.jsm');
michael@0 22 XPCOMUtils.defineLazyModuleGetter(this, 'Prefilters',
michael@0 23 'resource://gre/modules/accessibility/Constants.jsm');
michael@0 24
michael@0 25 let gSkipEmptyImages = new PrefCache('accessibility.accessfu.skip_empty_images');
michael@0 26
michael@0 27 function BaseTraversalRule(aRoles, aMatchFunc, aPreFilter) {
michael@0 28 this._explicitMatchRoles = new Set(aRoles);
michael@0 29 this._matchRoles = aRoles;
michael@0 30 if (aRoles.indexOf(Roles.LABEL) < 0) {
michael@0 31 this._matchRoles.push(Roles.LABEL);
michael@0 32 }
michael@0 33 this._matchFunc = aMatchFunc || function (acc) { return Filters.MATCH; };
michael@0 34 this.preFilter = aPreFilter || gSimplePreFilter;
michael@0 35 }
michael@0 36
michael@0 37 BaseTraversalRule.prototype = {
michael@0 38 getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
michael@0 39 aRules.value = this._matchRoles;
michael@0 40 return aRules.value.length;
michael@0 41 },
michael@0 42
michael@0 43 match: function BaseTraversalRule_match(aAccessible)
michael@0 44 {
michael@0 45 let role = aAccessible.role;
michael@0 46 if (role == Roles.INTERNAL_FRAME) {
michael@0 47 return (Utils.getMessageManager(aAccessible.DOMNode)) ?
michael@0 48 Filters.MATCH | Filters.IGNORE_SUBTREE : Filters.IGNORE;
michael@0 49 }
michael@0 50
michael@0 51 let matchResult = this._explicitMatchRoles.has(role) ?
michael@0 52 this._matchFunc(aAccessible) : Filters.IGNORE;
michael@0 53
michael@0 54 // If we are on a label that nests a checkbox/radio we should land on it.
michael@0 55 // It is a bigger touch target, and it reduces clutter.
michael@0 56 if (role == Roles.LABEL && !(matchResult & Filters.IGNORE_SUBTREE)) {
michael@0 57 let control = Utils.getEmbeddedControl(aAccessible);
michael@0 58 if (control && this._explicitMatchRoles.has(control.role)) {
michael@0 59 matchResult = this._matchFunc(control) | Filters.IGNORE_SUBTREE;
michael@0 60 }
michael@0 61 }
michael@0 62
michael@0 63 return matchResult;
michael@0 64 },
michael@0 65
michael@0 66 QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessibleTraversalRule])
michael@0 67 };
michael@0 68
michael@0 69 var gSimpleTraversalRoles =
michael@0 70 [Roles.MENUITEM,
michael@0 71 Roles.LINK,
michael@0 72 Roles.PAGETAB,
michael@0 73 Roles.GRAPHIC,
michael@0 74 Roles.STATICTEXT,
michael@0 75 Roles.TEXT_LEAF,
michael@0 76 Roles.PUSHBUTTON,
michael@0 77 Roles.CHECKBUTTON,
michael@0 78 Roles.RADIOBUTTON,
michael@0 79 Roles.COMBOBOX,
michael@0 80 Roles.PROGRESSBAR,
michael@0 81 Roles.BUTTONDROPDOWN,
michael@0 82 Roles.BUTTONMENU,
michael@0 83 Roles.CHECK_MENU_ITEM,
michael@0 84 Roles.PASSWORD_TEXT,
michael@0 85 Roles.RADIO_MENU_ITEM,
michael@0 86 Roles.TOGGLE_BUTTON,
michael@0 87 Roles.ENTRY,
michael@0 88 Roles.KEY,
michael@0 89 Roles.HEADER,
michael@0 90 Roles.HEADING,
michael@0 91 Roles.SLIDER,
michael@0 92 Roles.SPINBUTTON,
michael@0 93 Roles.OPTION,
michael@0 94 // Used for traversing in to child OOP frames.
michael@0 95 Roles.INTERNAL_FRAME];
michael@0 96
michael@0 97 var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
michael@0 98 function hasZeroOrSingleChildDescendants () {
michael@0 99 for (let acc = aAccessible; acc.childCount > 0; acc = acc.firstChild) {
michael@0 100 if (acc.childCount > 1) {
michael@0 101 return false;
michael@0 102 }
michael@0 103 }
michael@0 104
michael@0 105 return true;
michael@0 106 }
michael@0 107
michael@0 108 switch (aAccessible.role) {
michael@0 109 case Roles.COMBOBOX:
michael@0 110 // We don't want to ignore the subtree because this is often
michael@0 111 // where the list box hangs out.
michael@0 112 return Filters.MATCH;
michael@0 113 case Roles.TEXT_LEAF:
michael@0 114 {
michael@0 115 // Nameless text leaves are boring, skip them.
michael@0 116 let name = aAccessible.name;
michael@0 117 if (name && name.trim())
michael@0 118 return Filters.MATCH;
michael@0 119 else
michael@0 120 return Filters.IGNORE;
michael@0 121 }
michael@0 122 case Roles.STATICTEXT:
michael@0 123 {
michael@0 124 let parent = aAccessible.parent;
michael@0 125 // Ignore prefix static text in list items. They are typically bullets or numbers.
michael@0 126 if (parent.childCount > 1 && aAccessible.indexInParent == 0 &&
michael@0 127 parent.role == Roles.LISTITEM)
michael@0 128 return Filters.IGNORE;
michael@0 129
michael@0 130 return Filters.MATCH;
michael@0 131 }
michael@0 132 case Roles.GRAPHIC:
michael@0 133 return TraversalRules._shouldSkipImage(aAccessible);
michael@0 134 case Roles.HEADER:
michael@0 135 case Roles.HEADING:
michael@0 136 if ((aAccessible.childCount > 0 || aAccessible.name) &&
michael@0 137 hasZeroOrSingleChildDescendants()) {
michael@0 138 return Filters.MATCH | Filters.IGNORE_SUBTREE;
michael@0 139 } else {
michael@0 140 return Filters.IGNORE;
michael@0 141 }
michael@0 142 default:
michael@0 143 // Ignore the subtree, if there is one. So that we don't land on
michael@0 144 // the same content that was already presented by its parent.
michael@0 145 return Filters.MATCH |
michael@0 146 Filters.IGNORE_SUBTREE;
michael@0 147 }
michael@0 148 };
michael@0 149
michael@0 150 var gSimplePreFilter = Prefilters.DEFUNCT |
michael@0 151 Prefilters.INVISIBLE |
michael@0 152 Prefilters.ARIA_HIDDEN |
michael@0 153 Prefilters.TRANSPARENT;
michael@0 154
michael@0 155 this.TraversalRules = {
michael@0 156 Simple: new BaseTraversalRule(gSimpleTraversalRoles, gSimpleMatchFunc),
michael@0 157
michael@0 158 SimpleOnScreen: new BaseTraversalRule(
michael@0 159 gSimpleTraversalRoles, gSimpleMatchFunc,
michael@0 160 Prefilters.DEFUNCT | Prefilters.INVISIBLE | Prefilters.ARIA_HIDDEN |
michael@0 161 Prefilters.TRANSPARENT | Prefilters.OFFSCREEN),
michael@0 162
michael@0 163 Anchor: new BaseTraversalRule(
michael@0 164 [Roles.LINK],
michael@0 165 function Anchor_match(aAccessible)
michael@0 166 {
michael@0 167 // We want to ignore links, only focus named anchors.
michael@0 168 if (Utils.getState(aAccessible).contains(States.LINKED)) {
michael@0 169 return Filters.IGNORE;
michael@0 170 } else {
michael@0 171 return Filters.MATCH;
michael@0 172 }
michael@0 173 }),
michael@0 174
michael@0 175 Button: new BaseTraversalRule(
michael@0 176 [Roles.PUSHBUTTON,
michael@0 177 Roles.SPINBUTTON,
michael@0 178 Roles.TOGGLE_BUTTON,
michael@0 179 Roles.BUTTONDROPDOWN,
michael@0 180 Roles.BUTTONDROPDOWNGRID]),
michael@0 181
michael@0 182 Combobox: new BaseTraversalRule(
michael@0 183 [Roles.COMBOBOX,
michael@0 184 Roles.LISTBOX]),
michael@0 185
michael@0 186 Landmark: new BaseTraversalRule(
michael@0 187 [],
michael@0 188 function Landmark_match(aAccessible) {
michael@0 189 return Utils.getLandmarkName(aAccessible) ? Filters.MATCH :
michael@0 190 Filters.IGNORE;
michael@0 191 }
michael@0 192 ),
michael@0 193
michael@0 194 Entry: new BaseTraversalRule(
michael@0 195 [Roles.ENTRY,
michael@0 196 Roles.PASSWORD_TEXT]),
michael@0 197
michael@0 198 FormElement: new BaseTraversalRule(
michael@0 199 [Roles.PUSHBUTTON,
michael@0 200 Roles.SPINBUTTON,
michael@0 201 Roles.TOGGLE_BUTTON,
michael@0 202 Roles.BUTTONDROPDOWN,
michael@0 203 Roles.BUTTONDROPDOWNGRID,
michael@0 204 Roles.COMBOBOX,
michael@0 205 Roles.LISTBOX,
michael@0 206 Roles.ENTRY,
michael@0 207 Roles.PASSWORD_TEXT,
michael@0 208 Roles.PAGETAB,
michael@0 209 Roles.RADIOBUTTON,
michael@0 210 Roles.RADIO_MENU_ITEM,
michael@0 211 Roles.SLIDER,
michael@0 212 Roles.CHECKBUTTON,
michael@0 213 Roles.CHECK_MENU_ITEM]),
michael@0 214
michael@0 215 Graphic: new BaseTraversalRule(
michael@0 216 [Roles.GRAPHIC],
michael@0 217 function Graphic_match(aAccessible) {
michael@0 218 return TraversalRules._shouldSkipImage(aAccessible);
michael@0 219 }),
michael@0 220
michael@0 221 Heading: new BaseTraversalRule(
michael@0 222 [Roles.HEADING],
michael@0 223 function Heading_match(aAccessible) {
michael@0 224 return aAccessible.childCount > 0 ? Filters.MATCH : Filters.IGNORE;
michael@0 225 }),
michael@0 226
michael@0 227 ListItem: new BaseTraversalRule(
michael@0 228 [Roles.LISTITEM,
michael@0 229 Roles.TERM]),
michael@0 230
michael@0 231 Link: new BaseTraversalRule(
michael@0 232 [Roles.LINK],
michael@0 233 function Link_match(aAccessible)
michael@0 234 {
michael@0 235 // We want to ignore anchors, only focus real links.
michael@0 236 if (Utils.getState(aAccessible).contains(States.LINKED)) {
michael@0 237 return Filters.MATCH;
michael@0 238 } else {
michael@0 239 return Filters.IGNORE;
michael@0 240 }
michael@0 241 }),
michael@0 242
michael@0 243 List: new BaseTraversalRule(
michael@0 244 [Roles.LIST,
michael@0 245 Roles.DEFINITION_LIST]),
michael@0 246
michael@0 247 PageTab: new BaseTraversalRule(
michael@0 248 [Roles.PAGETAB]),
michael@0 249
michael@0 250 Paragraph: new BaseTraversalRule(
michael@0 251 [Roles.PARAGRAPH,
michael@0 252 Roles.SECTION],
michael@0 253 function Paragraph_match(aAccessible) {
michael@0 254 for (let child = aAccessible.firstChild; child; child = child.nextSibling) {
michael@0 255 if (child.role === Roles.TEXT_LEAF) {
michael@0 256 return Filters.MATCH | Filters.IGNORE_SUBTREE;
michael@0 257 }
michael@0 258 }
michael@0 259
michael@0 260 return Filters.IGNORE;
michael@0 261 }),
michael@0 262
michael@0 263 RadioButton: new BaseTraversalRule(
michael@0 264 [Roles.RADIOBUTTON,
michael@0 265 Roles.RADIO_MENU_ITEM]),
michael@0 266
michael@0 267 Separator: new BaseTraversalRule(
michael@0 268 [Roles.SEPARATOR]),
michael@0 269
michael@0 270 Table: new BaseTraversalRule(
michael@0 271 [Roles.TABLE]),
michael@0 272
michael@0 273 Checkbox: new BaseTraversalRule(
michael@0 274 [Roles.CHECKBUTTON,
michael@0 275 Roles.CHECK_MENU_ITEM]),
michael@0 276
michael@0 277 _shouldSkipImage: function _shouldSkipImage(aAccessible) {
michael@0 278 if (gSkipEmptyImages.value && aAccessible.name === '') {
michael@0 279 return Filters.IGNORE;
michael@0 280 }
michael@0 281 return Filters.MATCH;
michael@0 282 }
michael@0 283 };

mercurial