//////////////////////////////////////////////////////////////////////////////// // Helper functions for accessible states testing. // // requires: // common.js // role.js // //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // State constants // const STATE_BUSY is defined in common.js 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_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 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, "Focussed " + 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) { isState(state & STATE_SELECTABLE, STATE_SELECTABLE, false, "Selected element must be selectable!"); } } /** * Tests an acessible 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 + "'"); }