tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }