Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "ARIAMap.h"
8 : #include "nsAccUtils.h"
9 : #include "States.h"
10 :
11 : #include "mozilla/dom/Element.h"
12 :
13 : using namespace mozilla;
14 : using namespace mozilla::a11y;
15 : using namespace mozilla::a11y::aria;
16 :
17 : /**
18 : * Used to store state map rule data for ARIA attribute of enum type.
19 : */
20 : struct EnumTypeData
21 : {
22 : // ARIA attribute name.
23 : nsIAtom* const mAttrName;
24 :
25 : // States if the attribute value is matched to the enum value. Used as
26 : // nsIContent::AttrValuesArray, last item must be nullptr.
27 : nsIAtom* const* const mValues[4];
28 :
29 : // States applied if corresponding enum values are matched.
30 : const uint64_t mStates[3];
31 :
32 : // States to clear in case of match.
33 : const uint64_t mClearState;
34 : };
35 :
36 : enum ETokenType
37 : {
38 : eBoolType = 0,
39 : eMixedType = 1, // can take 'mixed' value
40 : eDefinedIfAbsent = 2 // permanent and false state are applied if absent
41 : };
42 :
43 : /**
44 : * Used to store state map rule data for ARIA attribute of token type (including
45 : * mixed value).
46 : */
47 : struct TokenTypeData
48 : {
49 0 : TokenTypeData(nsIAtom* aAttrName, uint32_t aType,
50 : uint64_t aPermanentState,
51 : uint64_t aTrueState,
52 0 : uint64_t aFalseState = 0) :
53 : mAttrName(aAttrName), mType(aType), mPermanentState(aPermanentState),
54 0 : mTrueState(aTrueState), mFalseState(aFalseState)
55 0 : { }
56 :
57 : // ARIA attribute name.
58 : nsIAtom* const mAttrName;
59 :
60 : // Type.
61 : const uint32_t mType;
62 :
63 : // State applied if the attribute is defined or mType doesn't have
64 : // eDefinedIfAbsent flag set.
65 : const uint64_t mPermanentState;
66 :
67 : // States applied if the attribute value is true/false.
68 : const uint64_t mTrueState;
69 : const uint64_t mFalseState;
70 : };
71 :
72 : /**
73 : * Map enum type attribute value to accessible state.
74 : */
75 : static void MapEnumType(dom::Element* aElement, uint64_t* aState,
76 : const EnumTypeData& aData);
77 :
78 : /**
79 : * Map token type attribute value to states.
80 : */
81 : static void MapTokenType(dom::Element* aContent, uint64_t* aState,
82 : const TokenTypeData& aData);
83 :
84 : bool
85 0 : aria::MapToState(EStateRule aRule, dom::Element* aElement, uint64_t* aState)
86 : {
87 0 : switch (aRule) {
88 : case eARIAAutoComplete:
89 : {
90 : static const EnumTypeData data = {
91 : nsGkAtoms::aria_autocomplete,
92 : { &nsGkAtoms::inlinevalue,
93 : &nsGkAtoms::list,
94 : &nsGkAtoms::both, nullptr },
95 : { states::SUPPORTS_AUTOCOMPLETION,
96 : states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION,
97 : states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION }, 0
98 0 : };
99 :
100 0 : MapEnumType(aElement, aState, data);
101 0 : return true;
102 : }
103 :
104 : case eARIABusy:
105 : {
106 : static const EnumTypeData data = {
107 : nsGkAtoms::aria_busy,
108 : { &nsGkAtoms::_true,
109 : &nsGkAtoms::error, nullptr },
110 : { states::BUSY,
111 : states::INVALID }, 0
112 0 : };
113 :
114 0 : MapEnumType(aElement, aState, data);
115 0 : return true;
116 : }
117 :
118 : case eARIACheckableBool:
119 : {
120 : static const TokenTypeData data(
121 : nsGkAtoms::aria_checked, eBoolType | eDefinedIfAbsent,
122 0 : states::CHECKABLE, states::CHECKED);
123 :
124 0 : MapTokenType(aElement, aState, data);
125 0 : return true;
126 : }
127 :
128 : case eARIACheckableMixed:
129 : {
130 : static const TokenTypeData data(
131 : nsGkAtoms::aria_checked, eMixedType | eDefinedIfAbsent,
132 0 : states::CHECKABLE, states::CHECKED);
133 :
134 0 : MapTokenType(aElement, aState, data);
135 0 : return true;
136 : }
137 :
138 : case eARIACheckedMixed:
139 : {
140 : static const TokenTypeData data(
141 : nsGkAtoms::aria_checked, eMixedType,
142 0 : states::CHECKABLE, states::CHECKED);
143 :
144 0 : MapTokenType(aElement, aState, data);
145 0 : return true;
146 : }
147 :
148 : case eARIACurrent:
149 : {
150 : static const TokenTypeData data(
151 : nsGkAtoms::aria_current, eBoolType,
152 0 : 0, states::CURRENT);
153 :
154 0 : MapTokenType(aElement, aState, data);
155 0 : return true;
156 : }
157 :
158 : case eARIADisabled:
159 : {
160 : static const TokenTypeData data(
161 : nsGkAtoms::aria_disabled, eBoolType,
162 0 : 0, states::UNAVAILABLE);
163 :
164 0 : MapTokenType(aElement, aState, data);
165 0 : return true;
166 : }
167 :
168 : case eARIAExpanded:
169 : {
170 : static const TokenTypeData data(
171 : nsGkAtoms::aria_expanded, eBoolType,
172 0 : 0, states::EXPANDED, states::COLLAPSED);
173 :
174 0 : MapTokenType(aElement, aState, data);
175 0 : return true;
176 : }
177 :
178 : case eARIAHasPopup:
179 : {
180 : static const TokenTypeData data(
181 : nsGkAtoms::aria_haspopup, eBoolType,
182 0 : 0, states::HASPOPUP);
183 :
184 0 : MapTokenType(aElement, aState, data);
185 0 : return true;
186 : }
187 :
188 : case eARIAInvalid:
189 : {
190 : static const TokenTypeData data(
191 : nsGkAtoms::aria_invalid, eBoolType,
192 0 : 0, states::INVALID);
193 :
194 0 : MapTokenType(aElement, aState, data);
195 0 : return true;
196 : }
197 :
198 : case eARIAModal:
199 : {
200 : static const TokenTypeData data(
201 : nsGkAtoms::aria_modal, eBoolType,
202 0 : 0, states::MODAL);
203 :
204 0 : MapTokenType(aElement, aState, data);
205 0 : return true;
206 : }
207 :
208 : case eARIAMultiline:
209 : {
210 : static const TokenTypeData data(
211 : nsGkAtoms::aria_multiline, eBoolType | eDefinedIfAbsent,
212 0 : 0, states::MULTI_LINE, states::SINGLE_LINE);
213 :
214 0 : MapTokenType(aElement, aState, data);
215 0 : return true;
216 : }
217 :
218 : case eARIAMultiSelectable:
219 : {
220 : static const TokenTypeData data(
221 : nsGkAtoms::aria_multiselectable, eBoolType,
222 0 : 0, states::MULTISELECTABLE | states::EXTSELECTABLE);
223 :
224 0 : MapTokenType(aElement, aState, data);
225 0 : return true;
226 : }
227 :
228 : case eARIAOrientation:
229 : {
230 : static const EnumTypeData data = {
231 : nsGkAtoms::aria_orientation,
232 : { &nsGkAtoms::horizontal,
233 : &nsGkAtoms::vertical, nullptr },
234 : { states::HORIZONTAL,
235 : states::VERTICAL },
236 : states::HORIZONTAL | states::VERTICAL
237 0 : };
238 :
239 0 : MapEnumType(aElement, aState, data);
240 0 : return true;
241 : }
242 :
243 : case eARIAPressed:
244 : {
245 : static const TokenTypeData data(
246 : nsGkAtoms::aria_pressed, eMixedType,
247 0 : 0, states::PRESSED);
248 :
249 0 : MapTokenType(aElement, aState, data);
250 0 : return true;
251 : }
252 :
253 : case eARIAReadonly:
254 : {
255 : static const TokenTypeData data(
256 : nsGkAtoms::aria_readonly, eBoolType,
257 0 : 0, states::READONLY);
258 :
259 0 : MapTokenType(aElement, aState, data);
260 0 : return true;
261 : }
262 :
263 : case eARIAReadonlyOrEditable:
264 : {
265 : static const TokenTypeData data(
266 : nsGkAtoms::aria_readonly, eBoolType | eDefinedIfAbsent,
267 0 : 0, states::READONLY, states::EDITABLE);
268 :
269 0 : MapTokenType(aElement, aState, data);
270 0 : return true;
271 : }
272 :
273 : case eARIAReadonlyOrEditableIfDefined:
274 : {
275 : static const TokenTypeData data(
276 : nsGkAtoms::aria_readonly, eBoolType,
277 0 : 0, states::READONLY, states::EDITABLE);
278 :
279 0 : MapTokenType(aElement, aState, data);
280 0 : return true;
281 : }
282 :
283 : case eARIARequired:
284 : {
285 : static const TokenTypeData data(
286 : nsGkAtoms::aria_required, eBoolType,
287 0 : 0, states::REQUIRED);
288 :
289 0 : MapTokenType(aElement, aState, data);
290 0 : return true;
291 : }
292 :
293 : case eARIASelectable:
294 : {
295 : static const TokenTypeData data(
296 : nsGkAtoms::aria_selected, eBoolType | eDefinedIfAbsent,
297 0 : states::SELECTABLE, states::SELECTED);
298 :
299 0 : MapTokenType(aElement, aState, data);
300 0 : return true;
301 : }
302 :
303 : case eARIASelectableIfDefined:
304 : {
305 : static const TokenTypeData data(
306 : nsGkAtoms::aria_selected, eBoolType,
307 0 : states::SELECTABLE, states::SELECTED);
308 :
309 0 : MapTokenType(aElement, aState, data);
310 0 : return true;
311 : }
312 :
313 : case eReadonlyUntilEditable:
314 : {
315 0 : if (!(*aState & states::EDITABLE))
316 0 : *aState |= states::READONLY;
317 :
318 0 : return true;
319 : }
320 :
321 : case eIndeterminateIfNoValue:
322 : {
323 0 : if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow) &&
324 0 : !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext))
325 0 : *aState |= states::MIXED;
326 :
327 0 : return true;
328 : }
329 :
330 : case eFocusableUntilDisabled:
331 : {
332 0 : if (!nsAccUtils::HasDefinedARIAToken(aElement, nsGkAtoms::aria_disabled) ||
333 0 : aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_disabled,
334 : nsGkAtoms::_false, eCaseMatters))
335 0 : *aState |= states::FOCUSABLE;
336 :
337 0 : return true;
338 : }
339 :
340 : default:
341 0 : return false;
342 : }
343 : }
344 :
345 : static void
346 0 : MapEnumType(dom::Element* aElement, uint64_t* aState, const EnumTypeData& aData)
347 : {
348 0 : switch (aElement->FindAttrValueIn(kNameSpaceID_None, aData.mAttrName,
349 0 : aData.mValues, eCaseMatters)) {
350 : case 0:
351 0 : *aState = (*aState & ~aData.mClearState) | aData.mStates[0];
352 0 : return;
353 : case 1:
354 0 : *aState = (*aState & ~aData.mClearState) | aData.mStates[1];
355 0 : return;
356 : case 2:
357 0 : *aState = (*aState & ~aData.mClearState) | aData.mStates[2];
358 0 : return;
359 : }
360 : }
361 :
362 : static void
363 0 : MapTokenType(dom::Element* aElement, uint64_t* aState,
364 : const TokenTypeData& aData)
365 : {
366 0 : if (nsAccUtils::HasDefinedARIAToken(aElement, aData.mAttrName)) {
367 0 : if (aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
368 : nsGkAtoms::mixed, eCaseMatters)) {
369 0 : if (aData.mType & eMixedType)
370 0 : *aState |= aData.mPermanentState | states::MIXED;
371 : else // unsupported use of 'mixed' is an authoring error
372 0 : *aState |= aData.mPermanentState | aData.mFalseState;
373 0 : return;
374 : }
375 :
376 0 : if (aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
377 : nsGkAtoms::_false, eCaseMatters)) {
378 0 : *aState |= aData.mPermanentState | aData.mFalseState;
379 0 : return;
380 : }
381 :
382 0 : *aState |= aData.mPermanentState | aData.mTrueState;
383 0 : return;
384 : }
385 :
386 0 : if (aData.mType & eDefinedIfAbsent)
387 0 : *aState |= aData.mPermanentState | aData.mFalseState;
388 : }
|