summaryrefslogtreecommitdiffstats
path: root/accessible/tests/mochitest/states.js
blob: c8ce9dd79d3d921e73d2a327531472d1016062ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
// //////////////////////////////////////////////////////////////////////////////
// Helper functions for accessible states testing.
//
// requires:
//   common.js
//   role.js
//
// //////////////////////////////////////////////////////////////////////////////
/* import-globals-from common.js */
/* import-globals-from role.js */

// //////////////////////////////////////////////////////////////////////////////
// State constants

// const STATE_BUSY is defined in common.js
const STATE_ANIMATED = nsIAccessibleStates.STATE_ANIMATED;
const STATE_CHECKED = nsIAccessibleStates.STATE_CHECKED;
const STATE_CHECKABLE = nsIAccessibleStates.STATE_CHECKABLE;
const STATE_COLLAPSED = nsIAccessibleStates.STATE_COLLAPSED;
const STATE_DEFAULT = nsIAccessibleStates.STATE_DEFAULT;
const STATE_EXPANDED = nsIAccessibleStates.STATE_EXPANDED;
const STATE_EXTSELECTABLE = nsIAccessibleStates.STATE_EXTSELECTABLE;
const STATE_FLOATING = nsIAccessibleStates.STATE_FLOATING;
const STATE_FOCUSABLE = nsIAccessibleStates.STATE_FOCUSABLE;
const STATE_FOCUSED = nsIAccessibleStates.STATE_FOCUSED;
const STATE_HASPOPUP = nsIAccessibleStates.STATE_HASPOPUP;
const STATE_INVALID = nsIAccessibleStates.STATE_INVALID;
const STATE_INVISIBLE = nsIAccessibleStates.STATE_INVISIBLE;
const STATE_LINKED = nsIAccessibleStates.STATE_LINKED;
const STATE_MIXED = nsIAccessibleStates.STATE_MIXED;
const STATE_MULTISELECTABLE = nsIAccessibleStates.STATE_MULTISELECTABLE;
const STATE_OFFSCREEN = nsIAccessibleStates.STATE_OFFSCREEN;
const STATE_PRESSED = nsIAccessibleStates.STATE_PRESSED;
const STATE_PROTECTED = nsIAccessibleStates.STATE_PROTECTED;
const STATE_READONLY = nsIAccessibleStates.STATE_READONLY;
const STATE_REQUIRED = nsIAccessibleStates.STATE_REQUIRED;
const STATE_SELECTABLE = nsIAccessibleStates.STATE_SELECTABLE;
const STATE_SELECTED = nsIAccessibleStates.STATE_SELECTED;
const STATE_TRAVERSED = nsIAccessibleStates.STATE_TRAVERSED;
const STATE_UNAVAILABLE = nsIAccessibleStates.STATE_UNAVAILABLE;

const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE;
const EXT_STATE_CURRENT = nsIAccessibleStates.EXT_STATE_CURRENT;
const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT;
const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE;
const EXT_STATE_ENABLED = nsIAccessibleStates.EXT_STATE_ENABLED;
const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE;
const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL;
const EXT_STATE_MODAL = nsIAccessibleStates.EXT_STATE_MODAL;
const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE;
const EXT_STATE_PINNED = nsIAccessibleStates.EXT_STATE_PINNED;
const EXT_STATE_SENSITIVE = nsIAccessibleStates.EXT_STATE_SENSITIVE;
const EXT_STATE_SINGLE_LINE = nsIAccessibleStates.EXT_STATE_SINGLE_LINE;
const EXT_STATE_STALE = nsIAccessibleStates.EXT_STATE_STALE;
const EXT_STATE_SUPPORTS_AUTOCOMPLETION =
  nsIAccessibleStates.EXT_STATE_SUPPORTS_AUTOCOMPLETION;
const EXT_STATE_VERTICAL = nsIAccessibleStates.EXT_STATE_VERTICAL;
const EXT_STATE_SELECTABLE_TEXT = nsIAccessibleStates.EXT_STATE_SELECTABLE_TEXT;
const EXT_STATE_OPAQUE = nsIAccessibleStates.EXT_STATE_OPAQUE;

const kOrdinalState = false;
const kExtraState = 1;

// //////////////////////////////////////////////////////////////////////////////
// Test functions

/**
 * Tests the states and extra states of the given accessible.
 * Also tests for unwanted states and extra states.
 * In addition, the function performs a few plausibility checks derived from the
 * sstates and extra states passed in.
 *
 * @param aAccOrElmOrID      The accessible, DOM element or ID to be tested.
 * @param aState             The state bits that are wanted.
 * @param aExtraState        The extra state bits that are wanted.
 * @param aAbsentState       State bits that are not wanted.
 * @param aAbsentExtraState  Extra state bits that are not wanted.
 * @param aTestName          The test name.
 */
function testStates(
  aAccOrElmOrID,
  aState,
  aExtraState,
  aAbsentState,
  aAbsentExtraState,
  aTestName
) {
  var [state, extraState] = getStates(aAccOrElmOrID);
  var role = getRole(aAccOrElmOrID);
  var id =
    prettyName(aAccOrElmOrID) + (aTestName ? " [" + aTestName + "]" : "");

  // Primary test.
  if (aState) {
    isState(state & aState, aState, false, "wrong state bits for " + id + "!");
  }

  if (aExtraState) {
    isState(
      extraState & aExtraState,
      aExtraState,
      true,
      "wrong extra state bits for " + id + "!"
    );
  }

  if (aAbsentState) {
    isState(
      state & aAbsentState,
      0,
      false,
      "state bits should not be present in ID " + id + "!"
    );
  }

  if (aAbsentExtraState) {
    isState(
      extraState & aAbsentExtraState,
      0,
      true,
      "extraState bits should not be present in ID " + id + "!"
    );
  }

  // Additional test.

  // focused/focusable
  if (state & STATE_FOCUSED) {
    isState(
      state & STATE_FOCUSABLE,
      STATE_FOCUSABLE,
      false,
      "Focused " + id + " must be focusable!"
    );
  }

  if (aAbsentState && aAbsentState & STATE_FOCUSABLE) {
    isState(
      state & STATE_FOCUSED,
      0,
      false,
      "Not focusable " + id + " must be not focused!"
    );
  }

  // multiline/singleline
  if (extraState & EXT_STATE_MULTI_LINE) {
    isState(
      extraState & EXT_STATE_SINGLE_LINE,
      0,
      true,
      "Multiline " + id + " cannot be singleline!"
    );
  }

  if (extraState & EXT_STATE_SINGLE_LINE) {
    isState(
      extraState & EXT_STATE_MULTI_LINE,
      0,
      true,
      "Singleline " + id + " cannot be multiline!"
    );
  }

  // expanded/collapsed/expandable
  if (state & STATE_COLLAPSED || state & STATE_EXPANDED) {
    isState(
      extraState & EXT_STATE_EXPANDABLE,
      EXT_STATE_EXPANDABLE,
      true,
      "Collapsed or expanded " + id + " must be expandable!"
    );
  }

  if (state & STATE_COLLAPSED) {
    isState(
      state & STATE_EXPANDED,
      0,
      false,
      "Collapsed " + id + " cannot be expanded!"
    );
  }

  if (state & STATE_EXPANDED) {
    isState(
      state & STATE_COLLAPSED,
      0,
      false,
      "Expanded " + id + " cannot be collapsed!"
    );
  }

  if (aAbsentState && extraState & EXT_STATE_EXPANDABLE) {
    if (aAbsentState & STATE_EXPANDED) {
      isState(
        state & STATE_COLLAPSED,
        STATE_COLLAPSED,
        false,
        "Not expanded " + id + " must be collapsed!"
      );
    } else if (aAbsentState & STATE_COLLAPSED) {
      isState(
        state & STATE_EXPANDED,
        STATE_EXPANDED,
        false,
        "Not collapsed " + id + " must be expanded!"
      );
    }
  }

  // checked/mixed/checkable
  if (
    state & STATE_CHECKED ||
    (state & STATE_MIXED &&
      role != ROLE_TOGGLE_BUTTON &&
      role != ROLE_PROGRESSBAR)
  ) {
    isState(
      state & STATE_CHECKABLE,
      STATE_CHECKABLE,
      false,
      "Checked or mixed element must be checkable!"
    );
  }

  if (state & STATE_CHECKED) {
    isState(
      state & STATE_MIXED,
      0,
      false,
      "Checked element cannot be state mixed!"
    );
  }

  if (state & STATE_MIXED) {
    isState(
      state & STATE_CHECKED,
      0,
      false,
      "Mixed element cannot be state checked!"
    );
  }

  // selected/selectable
  if (state & STATE_SELECTED && !(aAbsentState & STATE_SELECTABLE)) {
    isState(
      state & STATE_SELECTABLE,
      STATE_SELECTABLE,
      false,
      "Selected element must be selectable!"
    );
  }
}

/**
 * Tests an accessible and its sub tree for the passed in state bits.
 * Used to make sure that states are propagated to descendants, for example the
 * STATE_UNAVAILABLE from a container to its children.
 *
 * @param aAccOrElmOrID  The accessible, DOM element or ID to be tested.
 * @param aState         The state bits that are wanted.
 * @param aExtraState    The extra state bits that are wanted.
 * @param aAbsentState   State bits that are not wanted.
 */
function testStatesInSubtree(aAccOrElmOrID, aState, aExtraState, aAbsentState) {
  // test accessible and its subtree for propagated states.
  var acc = getAccessible(aAccOrElmOrID);
  if (!acc) {
    return;
  }

  if (getRole(acc) != ROLE_TEXT_LEAF) {
    // Right now, text leafs don't get tested because the states are not being
    // propagated.
    testStates(acc, aState, aExtraState, aAbsentState);
  }

  // Iterate over its children to see if the state got propagated.
  var children = null;
  try {
    children = acc.children;
  } catch (e) {}
  ok(children, "Could not get children for " + aAccOrElmOrID + "!");

  if (children) {
    for (var i = 0; i < children.length; i++) {
      var childAcc = children.queryElementAt(i, nsIAccessible);
      testStatesInSubtree(childAcc, aState, aExtraState, aAbsentState);
    }
  }
}

/**
 * Fails if no defunct state on the accessible.
 */
function testIsDefunct(aAccessible, aTestName) {
  var id = prettyName(aAccessible) + (aTestName ? " [" + aTestName + "]" : "");
  var [, /* state*/ extraState] = getStates(aAccessible);
  isState(
    extraState & EXT_STATE_DEFUNCT,
    EXT_STATE_DEFUNCT,
    true,
    "no defuct state for " + id + "!"
  );
}

function getStringStates(aAccOrElmOrID) {
  var [state, extraState] = getStates(aAccOrElmOrID);
  return statesToString(state, extraState);
}

function getStates(aAccOrElmOrID) {
  var acc = getAccessible(aAccOrElmOrID);
  if (!acc) {
    return [0, 0];
  }

  var state = {},
    extraState = {};
  acc.getState(state, extraState);

  return [state.value, extraState.value];
}

/**
 * Return true if the accessible has given states.
 */
function hasState(aAccOrElmOrID, aState, aExtraState) {
  var [state, exstate] = getStates(aAccOrElmOrID);
  return (
    (aState ? state & aState : true) &&
    (aExtraState ? exstate & aExtraState : true)
  );
}

// //////////////////////////////////////////////////////////////////////////////
// Private implementation details

/**
 * Analogy of SimpleTest.is function used to compare states.
 */
function isState(aState1, aState2, aIsExtraStates, aMsg) {
  if (aState1 == aState2) {
    ok(true, aMsg);
    return;
  }

  var got = "0";
  if (aState1) {
    got = statesToString(
      aIsExtraStates ? 0 : aState1,
      aIsExtraStates ? aState1 : 0
    );
  }

  var expected = "0";
  if (aState2) {
    expected = statesToString(
      aIsExtraStates ? 0 : aState2,
      aIsExtraStates ? aState2 : 0
    );
  }

  ok(false, aMsg + "got '" + got + "', expected '" + expected + "'");
}