|
1 //////////////////////////////////////////////////////////////////////////////// |
|
2 // Helper functions for accessible states testing. |
|
3 // |
|
4 // requires: |
|
5 // common.js |
|
6 // role.js |
|
7 // |
|
8 //////////////////////////////////////////////////////////////////////////////// |
|
9 |
|
10 //////////////////////////////////////////////////////////////////////////////// |
|
11 // State constants |
|
12 |
|
13 // const STATE_BUSY is defined in common.js |
|
14 const STATE_CHECKED = nsIAccessibleStates.STATE_CHECKED; |
|
15 const STATE_CHECKABLE = nsIAccessibleStates.STATE_CHECKABLE; |
|
16 const STATE_COLLAPSED = nsIAccessibleStates.STATE_COLLAPSED; |
|
17 const STATE_DEFAULT = nsIAccessibleStates.STATE_DEFAULT; |
|
18 const STATE_EXPANDED = nsIAccessibleStates.STATE_EXPANDED; |
|
19 const STATE_EXTSELECTABLE = nsIAccessibleStates.STATE_EXTSELECTABLE; |
|
20 const STATE_FLOATING = nsIAccessibleStates.STATE_FLOATING; |
|
21 const STATE_FOCUSABLE = nsIAccessibleStates.STATE_FOCUSABLE; |
|
22 const STATE_FOCUSED = nsIAccessibleStates.STATE_FOCUSED; |
|
23 const STATE_HASPOPUP = nsIAccessibleStates.STATE_HASPOPUP; |
|
24 const STATE_INVALID = nsIAccessibleStates.STATE_INVALID; |
|
25 const STATE_INVISIBLE = nsIAccessibleStates.STATE_INVISIBLE; |
|
26 const STATE_LINKED = nsIAccessibleStates.STATE_LINKED; |
|
27 const STATE_MIXED = nsIAccessibleStates.STATE_MIXED; |
|
28 const STATE_MULTISELECTABLE = nsIAccessibleStates.STATE_MULTISELECTABLE; |
|
29 const STATE_OFFSCREEN = nsIAccessibleStates.STATE_OFFSCREEN; |
|
30 const STATE_PRESSED = nsIAccessibleStates.STATE_PRESSED; |
|
31 const STATE_PROTECTED = nsIAccessibleStates.STATE_PROTECTED; |
|
32 const STATE_READONLY = nsIAccessibleStates.STATE_READONLY; |
|
33 const STATE_REQUIRED = nsIAccessibleStates.STATE_REQUIRED; |
|
34 const STATE_SELECTABLE = nsIAccessibleStates.STATE_SELECTABLE; |
|
35 const STATE_SELECTED = nsIAccessibleStates.STATE_SELECTED; |
|
36 const STATE_TRAVERSED = nsIAccessibleStates.STATE_TRAVERSED; |
|
37 const STATE_UNAVAILABLE = nsIAccessibleStates.STATE_UNAVAILABLE; |
|
38 |
|
39 const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE; |
|
40 const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT; |
|
41 const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE; |
|
42 const EXT_STATE_ENABLED = nsIAccessibleStates.EXT_STATE_ENABLED; |
|
43 const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE; |
|
44 const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL; |
|
45 const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE; |
|
46 const EXT_STATE_PINNED = nsIAccessibleStates.EXT_STATE_PINNED; |
|
47 const EXT_STATE_SENSITIVE = nsIAccessibleStates.EXT_STATE_SENSITIVE; |
|
48 const EXT_STATE_SINGLE_LINE = nsIAccessibleStates.EXT_STATE_SINGLE_LINE; |
|
49 const EXT_STATE_STALE = nsIAccessibleStates.EXT_STATE_STALE; |
|
50 const EXT_STATE_SUPPORTS_AUTOCOMPLETION = |
|
51 nsIAccessibleStates.EXT_STATE_SUPPORTS_AUTOCOMPLETION; |
|
52 const EXT_STATE_VERTICAL = nsIAccessibleStates.EXT_STATE_VERTICAL; |
|
53 |
|
54 const kOrdinalState = 0; |
|
55 const kExtraState = 1; |
|
56 |
|
57 //////////////////////////////////////////////////////////////////////////////// |
|
58 // Test functions |
|
59 |
|
60 /** |
|
61 * Tests the states and extra states of the given accessible. |
|
62 * Also tests for unwanted states and extra states. |
|
63 * In addition, the function performs a few plausibility checks derived from the |
|
64 * sstates and extra states passed in. |
|
65 * |
|
66 * @param aAccOrElmOrID The accessible, DOM element or ID to be tested. |
|
67 * @param aState The state bits that are wanted. |
|
68 * @param aExtraState The extra state bits that are wanted. |
|
69 * @param aAbsentState State bits that are not wanted. |
|
70 * @param aAbsentExtraState Extra state bits that are not wanted. |
|
71 * @param aTestName The test name. |
|
72 */ |
|
73 function testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState, |
|
74 aAbsentExtraState, aTestName) |
|
75 { |
|
76 var [state, extraState] = getStates(aAccOrElmOrID); |
|
77 var role = getRole(aAccOrElmOrID); |
|
78 var id = prettyName(aAccOrElmOrID) + (aTestName ? " [" + aTestName + "]": ""); |
|
79 |
|
80 // Primary test. |
|
81 if (aState) { |
|
82 isState(state & aState, aState, false, |
|
83 "wrong state bits for " + id + "!"); |
|
84 } |
|
85 |
|
86 if (aExtraState) |
|
87 isState(extraState & aExtraState, aExtraState, true, |
|
88 "wrong extra state bits for " + id + "!"); |
|
89 |
|
90 if (aAbsentState) |
|
91 isState(state & aAbsentState, 0, false, |
|
92 "state bits should not be present in ID " + id + "!"); |
|
93 |
|
94 if (aAbsentExtraState) |
|
95 isState(extraState & aAbsentExtraState, 0, true, |
|
96 "extraState bits should not be present in ID " + id + "!"); |
|
97 |
|
98 // Additional test. |
|
99 |
|
100 // focused/focusable |
|
101 if (state & STATE_FOCUSED) |
|
102 isState(state & STATE_FOCUSABLE, STATE_FOCUSABLE, false, |
|
103 "Focussed " + id + " must be focusable!"); |
|
104 |
|
105 if (aAbsentState && (aAbsentState & STATE_FOCUSABLE)) { |
|
106 isState(state & STATE_FOCUSED, 0, false, |
|
107 "Not focusable " + id + " must be not focused!"); |
|
108 } |
|
109 |
|
110 // multiline/singleline |
|
111 if (extraState & EXT_STATE_MULTI_LINE) |
|
112 isState(extraState & EXT_STATE_SINGLE_LINE, 0, true, |
|
113 "Multiline " + id + " cannot be singleline!"); |
|
114 |
|
115 if (extraState & EXT_STATE_SINGLE_LINE) |
|
116 isState(extraState & EXT_STATE_MULTI_LINE, 0, true, |
|
117 "Singleline " + id + " cannot be multiline!"); |
|
118 |
|
119 // expanded/collapsed/expandable |
|
120 if (state & STATE_COLLAPSED || state & STATE_EXPANDED) |
|
121 isState(extraState & EXT_STATE_EXPANDABLE, EXT_STATE_EXPANDABLE, true, |
|
122 "Collapsed or expanded " + id + " must be expandable!"); |
|
123 |
|
124 if (state & STATE_COLLAPSED) |
|
125 isState(state & STATE_EXPANDED, 0, false, |
|
126 "Collapsed " + id + " cannot be expanded!"); |
|
127 |
|
128 if (state & STATE_EXPANDED) |
|
129 isState(state & STATE_COLLAPSED, 0, false, |
|
130 "Expanded " + id + " cannot be collapsed!"); |
|
131 |
|
132 if (aAbsentState && (extraState & EXT_STATE_EXPANDABLE)) { |
|
133 if (aAbsentState & STATE_EXPANDED) { |
|
134 isState(state & STATE_COLLAPSED, STATE_COLLAPSED, false, |
|
135 "Not expanded " + id + " must be collapsed!"); |
|
136 } else if (aAbsentState & STATE_COLLAPSED) { |
|
137 isState(state & STATE_EXPANDED, STATE_EXPANDED, false, |
|
138 "Not collapsed " + id + " must be expanded!"); |
|
139 } |
|
140 } |
|
141 |
|
142 // checked/mixed/checkable |
|
143 if (state & STATE_CHECKED || state & STATE_MIXED && |
|
144 role != ROLE_TOGGLE_BUTTON && role != ROLE_PROGRESSBAR) |
|
145 isState(state & STATE_CHECKABLE, STATE_CHECKABLE, false, |
|
146 "Checked or mixed element must be checkable!"); |
|
147 |
|
148 if (state & STATE_CHECKED) |
|
149 isState(state & STATE_MIXED, 0, false, |
|
150 "Checked element cannot be state mixed!"); |
|
151 |
|
152 if (state & STATE_MIXED) |
|
153 isState(state & STATE_CHECKED, 0, false, |
|
154 "Mixed element cannot be state checked!"); |
|
155 |
|
156 // selected/selectable |
|
157 if (state & STATE_SELECTED) { |
|
158 isState(state & STATE_SELECTABLE, STATE_SELECTABLE, false, |
|
159 "Selected element must be selectable!"); |
|
160 } |
|
161 } |
|
162 |
|
163 /** |
|
164 * Tests an acessible and its sub tree for the passed in state bits. |
|
165 * Used to make sure that states are propagated to descendants, for example the |
|
166 * STATE_UNAVAILABLE from a container to its children. |
|
167 * |
|
168 * @param aAccOrElmOrID The accessible, DOM element or ID to be tested. |
|
169 * @param aState The state bits that are wanted. |
|
170 * @param aExtraState The extra state bits that are wanted. |
|
171 * @param aAbsentState State bits that are not wanted. |
|
172 */ |
|
173 function testStatesInSubtree(aAccOrElmOrID, aState, aExtraState, aAbsentState) |
|
174 { |
|
175 // test accessible and its subtree for propagated states. |
|
176 var acc = getAccessible(aAccOrElmOrID); |
|
177 if (!acc) |
|
178 return; |
|
179 |
|
180 if (getRole(acc) != ROLE_TEXT_LEAF) |
|
181 // Right now, text leafs don't get tested because the states are not being |
|
182 // propagated. |
|
183 testStates(acc, aState, aExtraState, aAbsentState); |
|
184 |
|
185 // Iterate over its children to see if the state got propagated. |
|
186 var children = null; |
|
187 try { |
|
188 children = acc.children; |
|
189 } catch(e) {} |
|
190 ok(children, "Could not get children for " + aAccOrElmOrID +"!"); |
|
191 |
|
192 if (children) { |
|
193 for (var i = 0; i < children.length; i++) { |
|
194 var childAcc = children.queryElementAt(i, nsIAccessible); |
|
195 testStatesInSubtree(childAcc, aState, aExtraState, aAbsentState); |
|
196 } |
|
197 } |
|
198 } |
|
199 |
|
200 function getStringStates(aAccOrElmOrID) |
|
201 { |
|
202 var [state, extraState] = getStates(aAccOrElmOrID); |
|
203 return statesToString(state, extraState); |
|
204 } |
|
205 |
|
206 function getStates(aAccOrElmOrID) |
|
207 { |
|
208 var acc = getAccessible(aAccOrElmOrID); |
|
209 if (!acc) |
|
210 return [0, 0]; |
|
211 |
|
212 var state = {}, extraState = {}; |
|
213 acc.getState(state, extraState); |
|
214 |
|
215 return [state.value, extraState.value]; |
|
216 } |
|
217 |
|
218 /** |
|
219 * Return true if the accessible has given states. |
|
220 */ |
|
221 function hasState(aAccOrElmOrID, aState, aExtraState) |
|
222 { |
|
223 var [state, exstate] = getStates(aAccOrElmOrID); |
|
224 return (aState ? state & aState : true) && |
|
225 (aExtraState ? exstate & aExtraState : true); |
|
226 } |
|
227 |
|
228 //////////////////////////////////////////////////////////////////////////////// |
|
229 // Private implementation details |
|
230 |
|
231 /** |
|
232 * Analogy of SimpleTest.is function used to compare states. |
|
233 */ |
|
234 function isState(aState1, aState2, aIsExtraStates, aMsg) |
|
235 { |
|
236 if (aState1 == aState2) { |
|
237 ok(true, aMsg); |
|
238 return; |
|
239 } |
|
240 |
|
241 var got = "0"; |
|
242 if (aState1) { |
|
243 got = statesToString(aIsExtraStates ? 0 : aState1, |
|
244 aIsExtraStates ? aState1 : 0); |
|
245 } |
|
246 |
|
247 var expected = "0"; |
|
248 if (aState2) { |
|
249 expected = statesToString(aIsExtraStates ? 0 : aState2, |
|
250 aIsExtraStates ? aState2 : 0); |
|
251 } |
|
252 |
|
253 ok(false, aMsg + "got '" + got + "', expected '" + expected + "'"); |
|
254 } |