// ////////////////////////////////////////////////////////////////////////////// // 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 + "'"); }