michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Helper functions for accessible states testing. michael@0: // michael@0: // requires: michael@0: // common.js michael@0: // role.js michael@0: // michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // State constants michael@0: michael@0: // const STATE_BUSY is defined in common.js michael@0: const STATE_CHECKED = nsIAccessibleStates.STATE_CHECKED; michael@0: const STATE_CHECKABLE = nsIAccessibleStates.STATE_CHECKABLE; michael@0: const STATE_COLLAPSED = nsIAccessibleStates.STATE_COLLAPSED; michael@0: const STATE_DEFAULT = nsIAccessibleStates.STATE_DEFAULT; michael@0: const STATE_EXPANDED = nsIAccessibleStates.STATE_EXPANDED; michael@0: const STATE_EXTSELECTABLE = nsIAccessibleStates.STATE_EXTSELECTABLE; michael@0: const STATE_FLOATING = nsIAccessibleStates.STATE_FLOATING; michael@0: const STATE_FOCUSABLE = nsIAccessibleStates.STATE_FOCUSABLE; michael@0: const STATE_FOCUSED = nsIAccessibleStates.STATE_FOCUSED; michael@0: const STATE_HASPOPUP = nsIAccessibleStates.STATE_HASPOPUP; michael@0: const STATE_INVALID = nsIAccessibleStates.STATE_INVALID; michael@0: const STATE_INVISIBLE = nsIAccessibleStates.STATE_INVISIBLE; michael@0: const STATE_LINKED = nsIAccessibleStates.STATE_LINKED; michael@0: const STATE_MIXED = nsIAccessibleStates.STATE_MIXED; michael@0: const STATE_MULTISELECTABLE = nsIAccessibleStates.STATE_MULTISELECTABLE; michael@0: const STATE_OFFSCREEN = nsIAccessibleStates.STATE_OFFSCREEN; michael@0: const STATE_PRESSED = nsIAccessibleStates.STATE_PRESSED; michael@0: const STATE_PROTECTED = nsIAccessibleStates.STATE_PROTECTED; michael@0: const STATE_READONLY = nsIAccessibleStates.STATE_READONLY; michael@0: const STATE_REQUIRED = nsIAccessibleStates.STATE_REQUIRED; michael@0: const STATE_SELECTABLE = nsIAccessibleStates.STATE_SELECTABLE; michael@0: const STATE_SELECTED = nsIAccessibleStates.STATE_SELECTED; michael@0: const STATE_TRAVERSED = nsIAccessibleStates.STATE_TRAVERSED; michael@0: const STATE_UNAVAILABLE = nsIAccessibleStates.STATE_UNAVAILABLE; michael@0: michael@0: const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE; michael@0: const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT; michael@0: const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE; michael@0: const EXT_STATE_ENABLED = nsIAccessibleStates.EXT_STATE_ENABLED; michael@0: const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE; michael@0: const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL; michael@0: const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE; michael@0: const EXT_STATE_PINNED = nsIAccessibleStates.EXT_STATE_PINNED; michael@0: const EXT_STATE_SENSITIVE = nsIAccessibleStates.EXT_STATE_SENSITIVE; michael@0: const EXT_STATE_SINGLE_LINE = nsIAccessibleStates.EXT_STATE_SINGLE_LINE; michael@0: const EXT_STATE_STALE = nsIAccessibleStates.EXT_STATE_STALE; michael@0: const EXT_STATE_SUPPORTS_AUTOCOMPLETION = michael@0: nsIAccessibleStates.EXT_STATE_SUPPORTS_AUTOCOMPLETION; michael@0: const EXT_STATE_VERTICAL = nsIAccessibleStates.EXT_STATE_VERTICAL; michael@0: michael@0: const kOrdinalState = 0; michael@0: const kExtraState = 1; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Test functions michael@0: michael@0: /** michael@0: * Tests the states and extra states of the given accessible. michael@0: * Also tests for unwanted states and extra states. michael@0: * In addition, the function performs a few plausibility checks derived from the michael@0: * sstates and extra states passed in. michael@0: * michael@0: * @param aAccOrElmOrID The accessible, DOM element or ID to be tested. michael@0: * @param aState The state bits that are wanted. michael@0: * @param aExtraState The extra state bits that are wanted. michael@0: * @param aAbsentState State bits that are not wanted. michael@0: * @param aAbsentExtraState Extra state bits that are not wanted. michael@0: * @param aTestName The test name. michael@0: */ michael@0: function testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState, michael@0: aAbsentExtraState, aTestName) michael@0: { michael@0: var [state, extraState] = getStates(aAccOrElmOrID); michael@0: var role = getRole(aAccOrElmOrID); michael@0: var id = prettyName(aAccOrElmOrID) + (aTestName ? " [" + aTestName + "]": ""); michael@0: michael@0: // Primary test. michael@0: if (aState) { michael@0: isState(state & aState, aState, false, michael@0: "wrong state bits for " + id + "!"); michael@0: } michael@0: michael@0: if (aExtraState) michael@0: isState(extraState & aExtraState, aExtraState, true, michael@0: "wrong extra state bits for " + id + "!"); michael@0: michael@0: if (aAbsentState) michael@0: isState(state & aAbsentState, 0, false, michael@0: "state bits should not be present in ID " + id + "!"); michael@0: michael@0: if (aAbsentExtraState) michael@0: isState(extraState & aAbsentExtraState, 0, true, michael@0: "extraState bits should not be present in ID " + id + "!"); michael@0: michael@0: // Additional test. michael@0: michael@0: // focused/focusable michael@0: if (state & STATE_FOCUSED) michael@0: isState(state & STATE_FOCUSABLE, STATE_FOCUSABLE, false, michael@0: "Focussed " + id + " must be focusable!"); michael@0: michael@0: if (aAbsentState && (aAbsentState & STATE_FOCUSABLE)) { michael@0: isState(state & STATE_FOCUSED, 0, false, michael@0: "Not focusable " + id + " must be not focused!"); michael@0: } michael@0: michael@0: // multiline/singleline michael@0: if (extraState & EXT_STATE_MULTI_LINE) michael@0: isState(extraState & EXT_STATE_SINGLE_LINE, 0, true, michael@0: "Multiline " + id + " cannot be singleline!"); michael@0: michael@0: if (extraState & EXT_STATE_SINGLE_LINE) michael@0: isState(extraState & EXT_STATE_MULTI_LINE, 0, true, michael@0: "Singleline " + id + " cannot be multiline!"); michael@0: michael@0: // expanded/collapsed/expandable michael@0: if (state & STATE_COLLAPSED || state & STATE_EXPANDED) michael@0: isState(extraState & EXT_STATE_EXPANDABLE, EXT_STATE_EXPANDABLE, true, michael@0: "Collapsed or expanded " + id + " must be expandable!"); michael@0: michael@0: if (state & STATE_COLLAPSED) michael@0: isState(state & STATE_EXPANDED, 0, false, michael@0: "Collapsed " + id + " cannot be expanded!"); michael@0: michael@0: if (state & STATE_EXPANDED) michael@0: isState(state & STATE_COLLAPSED, 0, false, michael@0: "Expanded " + id + " cannot be collapsed!"); michael@0: michael@0: if (aAbsentState && (extraState & EXT_STATE_EXPANDABLE)) { michael@0: if (aAbsentState & STATE_EXPANDED) { michael@0: isState(state & STATE_COLLAPSED, STATE_COLLAPSED, false, michael@0: "Not expanded " + id + " must be collapsed!"); michael@0: } else if (aAbsentState & STATE_COLLAPSED) { michael@0: isState(state & STATE_EXPANDED, STATE_EXPANDED, false, michael@0: "Not collapsed " + id + " must be expanded!"); michael@0: } michael@0: } michael@0: michael@0: // checked/mixed/checkable michael@0: if (state & STATE_CHECKED || state & STATE_MIXED && michael@0: role != ROLE_TOGGLE_BUTTON && role != ROLE_PROGRESSBAR) michael@0: isState(state & STATE_CHECKABLE, STATE_CHECKABLE, false, michael@0: "Checked or mixed element must be checkable!"); michael@0: michael@0: if (state & STATE_CHECKED) michael@0: isState(state & STATE_MIXED, 0, false, michael@0: "Checked element cannot be state mixed!"); michael@0: michael@0: if (state & STATE_MIXED) michael@0: isState(state & STATE_CHECKED, 0, false, michael@0: "Mixed element cannot be state checked!"); michael@0: michael@0: // selected/selectable michael@0: if (state & STATE_SELECTED) { michael@0: isState(state & STATE_SELECTABLE, STATE_SELECTABLE, false, michael@0: "Selected element must be selectable!"); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Tests an acessible and its sub tree for the passed in state bits. michael@0: * Used to make sure that states are propagated to descendants, for example the michael@0: * STATE_UNAVAILABLE from a container to its children. michael@0: * michael@0: * @param aAccOrElmOrID The accessible, DOM element or ID to be tested. michael@0: * @param aState The state bits that are wanted. michael@0: * @param aExtraState The extra state bits that are wanted. michael@0: * @param aAbsentState State bits that are not wanted. michael@0: */ michael@0: function testStatesInSubtree(aAccOrElmOrID, aState, aExtraState, aAbsentState) michael@0: { michael@0: // test accessible and its subtree for propagated states. michael@0: var acc = getAccessible(aAccOrElmOrID); michael@0: if (!acc) michael@0: return; michael@0: michael@0: if (getRole(acc) != ROLE_TEXT_LEAF) michael@0: // Right now, text leafs don't get tested because the states are not being michael@0: // propagated. michael@0: testStates(acc, aState, aExtraState, aAbsentState); michael@0: michael@0: // Iterate over its children to see if the state got propagated. michael@0: var children = null; michael@0: try { michael@0: children = acc.children; michael@0: } catch(e) {} michael@0: ok(children, "Could not get children for " + aAccOrElmOrID +"!"); michael@0: michael@0: if (children) { michael@0: for (var i = 0; i < children.length; i++) { michael@0: var childAcc = children.queryElementAt(i, nsIAccessible); michael@0: testStatesInSubtree(childAcc, aState, aExtraState, aAbsentState); michael@0: } michael@0: } michael@0: } michael@0: michael@0: function getStringStates(aAccOrElmOrID) michael@0: { michael@0: var [state, extraState] = getStates(aAccOrElmOrID); michael@0: return statesToString(state, extraState); michael@0: } michael@0: michael@0: function getStates(aAccOrElmOrID) michael@0: { michael@0: var acc = getAccessible(aAccOrElmOrID); michael@0: if (!acc) michael@0: return [0, 0]; michael@0: michael@0: var state = {}, extraState = {}; michael@0: acc.getState(state, extraState); michael@0: michael@0: return [state.value, extraState.value]; michael@0: } michael@0: michael@0: /** michael@0: * Return true if the accessible has given states. michael@0: */ michael@0: function hasState(aAccOrElmOrID, aState, aExtraState) michael@0: { michael@0: var [state, exstate] = getStates(aAccOrElmOrID); michael@0: return (aState ? state & aState : true) && michael@0: (aExtraState ? exstate & aExtraState : true); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Private implementation details michael@0: michael@0: /** michael@0: * Analogy of SimpleTest.is function used to compare states. michael@0: */ michael@0: function isState(aState1, aState2, aIsExtraStates, aMsg) michael@0: { michael@0: if (aState1 == aState2) { michael@0: ok(true, aMsg); michael@0: return; michael@0: } michael@0: michael@0: var got = "0"; michael@0: if (aState1) { michael@0: got = statesToString(aIsExtraStates ? 0 : aState1, michael@0: aIsExtraStates ? aState1 : 0); michael@0: } michael@0: michael@0: var expected = "0"; michael@0: if (aState2) { michael@0: expected = statesToString(aIsExtraStates ? 0 : aState2, michael@0: aIsExtraStates ? aState2 : 0); michael@0: } michael@0: michael@0: ok(false, aMsg + "got '" + got + "', expected '" + expected + "'"); michael@0: }