mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
6f380d42dd
Differential Revision: https://phabricator.services.mozilla.com/D127720
365 lines
10 KiB
JavaScript
365 lines
10 KiB
JavaScript
// //////////////////////////////////////////////////////////////////////////////
|
|
// 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 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 && !(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 + "'");
|
|
}
|