states.js (10523B)
1 // ////////////////////////////////////////////////////////////////////////////// 2 // Helper functions for accessible states testing. 3 // 4 // requires: 5 // common.js 6 // role.js 7 // 8 // ////////////////////////////////////////////////////////////////////////////// 9 /* import-globals-from common.js */ 10 /* import-globals-from role.js */ 11 12 // ////////////////////////////////////////////////////////////////////////////// 13 // State constants 14 15 // const STATE_BUSY is defined in common.js 16 const STATE_ANIMATED = nsIAccessibleStates.STATE_ANIMATED; 17 const STATE_CHECKED = nsIAccessibleStates.STATE_CHECKED; 18 const STATE_CHECKABLE = nsIAccessibleStates.STATE_CHECKABLE; 19 const STATE_COLLAPSED = nsIAccessibleStates.STATE_COLLAPSED; 20 const STATE_DEFAULT = nsIAccessibleStates.STATE_DEFAULT; 21 const STATE_EXPANDED = nsIAccessibleStates.STATE_EXPANDED; 22 const STATE_EXTSELECTABLE = nsIAccessibleStates.STATE_EXTSELECTABLE; 23 const STATE_FLOATING = nsIAccessibleStates.STATE_FLOATING; 24 const STATE_FOCUSABLE = nsIAccessibleStates.STATE_FOCUSABLE; 25 const STATE_FOCUSED = nsIAccessibleStates.STATE_FOCUSED; 26 const STATE_HASPOPUP = nsIAccessibleStates.STATE_HASPOPUP; 27 const STATE_INVALID = nsIAccessibleStates.STATE_INVALID; 28 const STATE_INVISIBLE = nsIAccessibleStates.STATE_INVISIBLE; 29 const STATE_LINKED = nsIAccessibleStates.STATE_LINKED; 30 const STATE_MIXED = nsIAccessibleStates.STATE_MIXED; 31 const STATE_MULTISELECTABLE = nsIAccessibleStates.STATE_MULTISELECTABLE; 32 const STATE_OFFSCREEN = nsIAccessibleStates.STATE_OFFSCREEN; 33 const STATE_PRESSED = nsIAccessibleStates.STATE_PRESSED; 34 const STATE_PROTECTED = nsIAccessibleStates.STATE_PROTECTED; 35 const STATE_READONLY = nsIAccessibleStates.STATE_READONLY; 36 const STATE_REQUIRED = nsIAccessibleStates.STATE_REQUIRED; 37 const STATE_SELECTABLE = nsIAccessibleStates.STATE_SELECTABLE; 38 const STATE_SELECTED = nsIAccessibleStates.STATE_SELECTED; 39 const STATE_TRAVERSED = nsIAccessibleStates.STATE_TRAVERSED; 40 const STATE_UNAVAILABLE = nsIAccessibleStates.STATE_UNAVAILABLE; 41 42 const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE; 43 const EXT_STATE_CURRENT = nsIAccessibleStates.EXT_STATE_CURRENT; 44 const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT; 45 const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE; 46 const EXT_STATE_ENABLED = nsIAccessibleStates.EXT_STATE_ENABLED; 47 const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE; 48 const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL; 49 const EXT_STATE_MODAL = nsIAccessibleStates.EXT_STATE_MODAL; 50 const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE; 51 const EXT_STATE_PINNED = nsIAccessibleStates.EXT_STATE_PINNED; 52 const EXT_STATE_SENSITIVE = nsIAccessibleStates.EXT_STATE_SENSITIVE; 53 const EXT_STATE_SINGLE_LINE = nsIAccessibleStates.EXT_STATE_SINGLE_LINE; 54 const EXT_STATE_STALE = nsIAccessibleStates.EXT_STATE_STALE; 55 const EXT_STATE_SUPPORTS_AUTOCOMPLETION = 56 nsIAccessibleStates.EXT_STATE_SUPPORTS_AUTOCOMPLETION; 57 const EXT_STATE_VERTICAL = nsIAccessibleStates.EXT_STATE_VERTICAL; 58 const EXT_STATE_SELECTABLE_TEXT = nsIAccessibleStates.EXT_STATE_SELECTABLE_TEXT; 59 const EXT_STATE_OPAQUE = nsIAccessibleStates.EXT_STATE_OPAQUE; 60 61 const kOrdinalState = false; 62 const kExtraState = 1; 63 64 // ////////////////////////////////////////////////////////////////////////////// 65 // Test functions 66 67 /** 68 * Tests the states and extra states of the given accessible. 69 * Also tests for unwanted states and extra states. 70 * In addition, the function performs a few plausibility checks derived from the 71 * sstates and extra states passed in. 72 * 73 * @param aAccOrElmOrID The accessible, DOM element or ID to be tested. 74 * @param aState The state bits that are wanted. 75 * @param aExtraState The extra state bits that are wanted. 76 * @param aAbsentState State bits that are not wanted. 77 * @param aAbsentExtraState Extra state bits that are not wanted. 78 * @param aTestName The test name. 79 */ 80 function testStates( 81 aAccOrElmOrID, 82 aState, 83 aExtraState, 84 aAbsentState, 85 aAbsentExtraState, 86 aTestName 87 ) { 88 var [state, extraState] = getStates(aAccOrElmOrID); 89 var role = getRole(aAccOrElmOrID); 90 var id = 91 prettyName(aAccOrElmOrID) + (aTestName ? " [" + aTestName + "]" : ""); 92 93 // Primary test. 94 if (aState) { 95 isState(state & aState, aState, false, "wrong state bits for " + id + "!"); 96 } 97 98 if (aExtraState) { 99 isState( 100 extraState & aExtraState, 101 aExtraState, 102 true, 103 "wrong extra state bits for " + id + "!" 104 ); 105 } 106 107 if (aAbsentState) { 108 isState( 109 state & aAbsentState, 110 0, 111 false, 112 "state bits should not be present in ID " + id + "!" 113 ); 114 } 115 116 if (aAbsentExtraState) { 117 isState( 118 extraState & aAbsentExtraState, 119 0, 120 true, 121 "extraState bits should not be present in ID " + id + "!" 122 ); 123 } 124 125 // Additional test. 126 127 // focused/focusable 128 if (state & STATE_FOCUSED) { 129 isState( 130 state & STATE_FOCUSABLE, 131 STATE_FOCUSABLE, 132 false, 133 "Focused " + id + " must be focusable!" 134 ); 135 } 136 137 if (aAbsentState && aAbsentState & STATE_FOCUSABLE) { 138 isState( 139 state & STATE_FOCUSED, 140 0, 141 false, 142 "Not focusable " + id + " must be not focused!" 143 ); 144 } 145 146 // multiline/singleline 147 if (extraState & EXT_STATE_MULTI_LINE) { 148 isState( 149 extraState & EXT_STATE_SINGLE_LINE, 150 0, 151 true, 152 "Multiline " + id + " cannot be singleline!" 153 ); 154 } 155 156 if (extraState & EXT_STATE_SINGLE_LINE) { 157 isState( 158 extraState & EXT_STATE_MULTI_LINE, 159 0, 160 true, 161 "Singleline " + id + " cannot be multiline!" 162 ); 163 } 164 165 // expanded/collapsed/expandable 166 if (state & STATE_COLLAPSED || state & STATE_EXPANDED) { 167 isState( 168 extraState & EXT_STATE_EXPANDABLE, 169 EXT_STATE_EXPANDABLE, 170 true, 171 "Collapsed or expanded " + id + " must be expandable!" 172 ); 173 } 174 175 if (state & STATE_COLLAPSED) { 176 isState( 177 state & STATE_EXPANDED, 178 0, 179 false, 180 "Collapsed " + id + " cannot be expanded!" 181 ); 182 } 183 184 if (state & STATE_EXPANDED) { 185 isState( 186 state & STATE_COLLAPSED, 187 0, 188 false, 189 "Expanded " + id + " cannot be collapsed!" 190 ); 191 } 192 193 if (aAbsentState && extraState & EXT_STATE_EXPANDABLE) { 194 if (aAbsentState & STATE_EXPANDED) { 195 isState( 196 state & STATE_COLLAPSED, 197 STATE_COLLAPSED, 198 false, 199 "Not expanded " + id + " must be collapsed!" 200 ); 201 } else if (aAbsentState & STATE_COLLAPSED) { 202 isState( 203 state & STATE_EXPANDED, 204 STATE_EXPANDED, 205 false, 206 "Not collapsed " + id + " must be expanded!" 207 ); 208 } 209 } 210 211 // checked/mixed/checkable 212 if ( 213 state & STATE_CHECKED || 214 (state & STATE_MIXED && 215 role != ROLE_TOGGLE_BUTTON && 216 role != ROLE_PROGRESSBAR) 217 ) { 218 isState( 219 state & STATE_CHECKABLE, 220 STATE_CHECKABLE, 221 false, 222 "Checked or mixed element must be checkable!" 223 ); 224 } 225 226 if (state & STATE_CHECKED) { 227 isState( 228 state & STATE_MIXED, 229 0, 230 false, 231 "Checked element cannot be state mixed!" 232 ); 233 } 234 235 if (state & STATE_MIXED) { 236 isState( 237 state & STATE_CHECKED, 238 0, 239 false, 240 "Mixed element cannot be state checked!" 241 ); 242 } 243 244 // selected/selectable 245 if (state & STATE_SELECTED && !(aAbsentState & STATE_SELECTABLE)) { 246 isState( 247 state & STATE_SELECTABLE, 248 STATE_SELECTABLE, 249 false, 250 "Selected element must be selectable!" 251 ); 252 } 253 } 254 255 /** 256 * Tests an accessible and its sub tree for the passed in state bits. 257 * Used to make sure that states are propagated to descendants, for example the 258 * STATE_UNAVAILABLE from a container to its children. 259 * 260 * @param aAccOrElmOrID The accessible, DOM element or ID to be tested. 261 * @param aState The state bits that are wanted. 262 * @param aExtraState The extra state bits that are wanted. 263 * @param aAbsentState State bits that are not wanted. 264 */ 265 function testStatesInSubtree(aAccOrElmOrID, aState, aExtraState, aAbsentState) { 266 // test accessible and its subtree for propagated states. 267 var acc = getAccessible(aAccOrElmOrID); 268 if (!acc) { 269 return; 270 } 271 272 if (getRole(acc) != ROLE_TEXT_LEAF) { 273 // Right now, text leafs don't get tested because the states are not being 274 // propagated. 275 testStates(acc, aState, aExtraState, aAbsentState); 276 } 277 278 // Iterate over its children to see if the state got propagated. 279 var children = null; 280 try { 281 children = acc.children; 282 } catch (e) {} 283 ok(children, "Could not get children for " + aAccOrElmOrID + "!"); 284 285 if (children) { 286 for (var i = 0; i < children.length; i++) { 287 var childAcc = children.queryElementAt(i, nsIAccessible); 288 testStatesInSubtree(childAcc, aState, aExtraState, aAbsentState); 289 } 290 } 291 } 292 293 /** 294 * Fails if no defunct state on the accessible. 295 */ 296 function testIsDefunct(aAccessible, aTestName) { 297 var id = prettyName(aAccessible) + (aTestName ? " [" + aTestName + "]" : ""); 298 var [, /* state*/ extraState] = getStates(aAccessible); 299 isState( 300 extraState & EXT_STATE_DEFUNCT, 301 EXT_STATE_DEFUNCT, 302 true, 303 "no defuct state for " + id + "!" 304 ); 305 } 306 307 function getStringStates(aAccOrElmOrID) { 308 var [state, extraState] = getStates(aAccOrElmOrID); 309 return statesToString(state, extraState); 310 } 311 312 function getStates(aAccOrElmOrID) { 313 var acc = getAccessible(aAccOrElmOrID); 314 if (!acc) { 315 return [0, 0]; 316 } 317 318 var state = {}, 319 extraState = {}; 320 acc.getState(state, extraState); 321 322 return [state.value, extraState.value]; 323 } 324 325 /** 326 * Return true if the accessible has given states. 327 */ 328 function hasState(aAccOrElmOrID, aState, aExtraState) { 329 var [state, exstate] = getStates(aAccOrElmOrID); 330 return ( 331 (aState ? state & aState : true) && 332 (aExtraState ? exstate & aExtraState : true) 333 ); 334 } 335 336 // ////////////////////////////////////////////////////////////////////////////// 337 // Private implementation details 338 339 /** 340 * Analogy of SimpleTest.is function used to compare states. 341 */ 342 function isState(aState1, aState2, aIsExtraStates, aMsg) { 343 if (aState1 == aState2) { 344 ok(true, aMsg); 345 return; 346 } 347 348 var got = "0"; 349 if (aState1) { 350 got = statesToString( 351 aIsExtraStates ? 0 : aState1, 352 aIsExtraStates ? aState1 : 0 353 ); 354 } 355 356 var expected = "0"; 357 if (aState2) { 358 expected = statesToString( 359 aIsExtraStates ? 0 : aState2, 360 aIsExtraStates ? aState2 : 0 361 ); 362 } 363 364 ok(false, aMsg + "got '" + got + "', expected '" + expected + "'"); 365 }