Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #include "ARIAMap.h"
8 #include "nsAccUtils.h"
9 #include "States.h"
11 #include "mozilla/dom/Element.h"
13 using namespace mozilla;
14 using namespace mozilla::a11y;
15 using namespace mozilla::a11y::aria;
17 /**
18 * Used to store state map rule data for ARIA attribute of enum type.
19 */
20 struct EnumTypeData
21 {
22 EnumTypeData(nsIAtom* aAttrName,
23 nsIAtom** aValue1, uint64_t aState1,
24 nsIAtom** aValue2, uint64_t aState2,
25 nsIAtom** aValue3 = 0, uint64_t aState3 = 0) :
26 mState1(aState1), mState2(aState2), mState3(aState3), mDefaultState(0),
27 mAttrName(aAttrName), mValue1(aValue1), mValue2(aValue2), mValue3(aValue3),
28 mNullValue(nullptr)
29 { }
31 EnumTypeData(nsIAtom* aAttrName, uint64_t aDefaultState,
32 nsIAtom** aValue1, uint64_t aState1) :
33 mState1(aState1), mState2(0), mState3(0), mDefaultState(aDefaultState),
34 mAttrName(aAttrName), mValue1(aValue1), mValue2(nullptr), mValue3(nullptr),
35 mNullValue(nullptr)
36 { }
38 // States applied if corresponding enum values are matched.
39 const uint64_t mState1;
40 const uint64_t mState2;
41 const uint64_t mState3;
43 // Default state if no one enum value is matched.
44 const uint64_t mDefaultState;
46 // ARIA attribute name.
47 nsIAtom* const mAttrName;
49 // States if the attribute value is matched to the enum value. Used as
50 // nsIContent::AttrValuesArray.
51 nsIAtom* const* const mValue1;
52 nsIAtom* const* const mValue2;
53 nsIAtom* const* const mValue3;
54 nsIAtom* const* const mNullValue;
55 };
57 enum ETokenType
58 {
59 eBoolType = 0,
60 eMixedType = 1, // can take 'mixed' value
61 eDefinedIfAbsent = 2 // permanent and false state are applied if absent
62 };
64 /**
65 * Used to store state map rule data for ARIA attribute of token type (including
66 * mixed value).
67 */
68 struct TokenTypeData
69 {
70 TokenTypeData(nsIAtom* aAttrName, uint32_t aType,
71 uint64_t aPermanentState,
72 uint64_t aTrueState,
73 uint64_t aFalseState = 0) :
74 mAttrName(aAttrName), mType(aType), mPermanentState(aPermanentState),
75 mTrueState(aTrueState), mFalseState(aFalseState)
76 { }
78 // ARIA attribute name.
79 nsIAtom* const mAttrName;
81 // Type.
82 const uint32_t mType;
84 // State applied if the attribute is defined or mType doesn't have
85 // eDefinedIfAbsent flag set.
86 const uint64_t mPermanentState;
88 // States applied if the attribute value is true/false.
89 const uint64_t mTrueState;
90 const uint64_t mFalseState;
91 };
93 /**
94 * Map enum type attribute value to accessible state.
95 */
96 static void MapEnumType(dom::Element* aElement, uint64_t* aState,
97 const EnumTypeData& aData);
99 /**
100 * Map token type attribute value to states.
101 */
102 static void MapTokenType(dom::Element* aContent, uint64_t* aState,
103 const TokenTypeData& aData);
105 bool
106 aria::MapToState(EStateRule aRule, dom::Element* aElement, uint64_t* aState)
107 {
108 switch (aRule) {
109 case eARIAAutoComplete:
110 {
111 static const EnumTypeData data(
112 nsGkAtoms::aria_autocomplete,
113 &nsGkAtoms::inlinevalue, states::SUPPORTS_AUTOCOMPLETION,
114 &nsGkAtoms::list, states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION,
115 &nsGkAtoms::both, states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION);
117 MapEnumType(aElement, aState, data);
118 return true;
119 }
121 case eARIABusy:
122 {
123 static const EnumTypeData data(
124 nsGkAtoms::aria_busy,
125 &nsGkAtoms::_true, states::BUSY,
126 &nsGkAtoms::error, states::INVALID);
128 MapEnumType(aElement, aState, data);
129 return true;
130 }
132 case eARIACheckableBool:
133 {
134 static const TokenTypeData data(
135 nsGkAtoms::aria_checked, eBoolType | eDefinedIfAbsent,
136 states::CHECKABLE, states::CHECKED);
138 MapTokenType(aElement, aState, data);
139 return true;
140 }
142 case eARIACheckableMixed:
143 {
144 static const TokenTypeData data(
145 nsGkAtoms::aria_checked, eMixedType | eDefinedIfAbsent,
146 states::CHECKABLE, states::CHECKED);
148 MapTokenType(aElement, aState, data);
149 return true;
150 }
152 case eARIACheckedMixed:
153 {
154 static const TokenTypeData data(
155 nsGkAtoms::aria_checked, eMixedType,
156 states::CHECKABLE, states::CHECKED);
158 MapTokenType(aElement, aState, data);
159 return true;
160 }
162 case eARIADisabled:
163 {
164 static const TokenTypeData data(
165 nsGkAtoms::aria_disabled, eBoolType,
166 0, states::UNAVAILABLE);
168 MapTokenType(aElement, aState, data);
169 return true;
170 }
172 case eARIAExpanded:
173 {
174 static const TokenTypeData data(
175 nsGkAtoms::aria_expanded, eBoolType,
176 0, states::EXPANDED, states::COLLAPSED);
178 MapTokenType(aElement, aState, data);
179 return true;
180 }
182 case eARIAHasPopup:
183 {
184 static const TokenTypeData data(
185 nsGkAtoms::aria_haspopup, eBoolType,
186 0, states::HASPOPUP);
188 MapTokenType(aElement, aState, data);
189 return true;
190 }
192 case eARIAInvalid:
193 {
194 static const TokenTypeData data(
195 nsGkAtoms::aria_invalid, eBoolType,
196 0, states::INVALID);
198 MapTokenType(aElement, aState, data);
199 return true;
200 }
202 case eARIAMultiline:
203 {
204 static const TokenTypeData data(
205 nsGkAtoms::aria_multiline, eBoolType | eDefinedIfAbsent,
206 0, states::MULTI_LINE, states::SINGLE_LINE);
208 MapTokenType(aElement, aState, data);
209 return true;
210 }
212 case eARIAMultiSelectable:
213 {
214 static const TokenTypeData data(
215 nsGkAtoms::aria_multiselectable, eBoolType,
216 0, states::MULTISELECTABLE | states::EXTSELECTABLE);
218 MapTokenType(aElement, aState, data);
219 return true;
220 }
222 case eARIAOrientation:
223 {
224 if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_orientation,
225 NS_LITERAL_STRING("horizontal"), eCaseMatters)) {
226 *aState &= ~states::VERTICAL;
227 *aState |= states::HORIZONTAL;
228 } else if (aElement->AttrValueIs(kNameSpaceID_None,
229 nsGkAtoms::aria_orientation,
230 NS_LITERAL_STRING("vertical"),
231 eCaseMatters)) {
232 *aState &= ~states::HORIZONTAL;
233 *aState |= states::VERTICAL;
234 } else {
235 NS_ASSERTION(!(*aState & (states::HORIZONTAL | states::VERTICAL)),
236 "orientation state on role with default aria-orientation!");
237 *aState |= GetRoleMap(aElement)->Is(nsGkAtoms::scrollbar) ?
238 states::VERTICAL : states::HORIZONTAL;
239 }
241 return true;
242 }
244 case eARIAPressed:
245 {
246 static const TokenTypeData data(
247 nsGkAtoms::aria_pressed, eMixedType,
248 0, states::PRESSED);
250 MapTokenType(aElement, aState, data);
251 return true;
252 }
254 case eARIAReadonly:
255 {
256 static const TokenTypeData data(
257 nsGkAtoms::aria_readonly, eBoolType,
258 0, states::READONLY);
260 MapTokenType(aElement, aState, data);
261 return true;
262 }
264 case eARIAReadonlyOrEditable:
265 {
266 static const TokenTypeData data(
267 nsGkAtoms::aria_readonly, eBoolType | eDefinedIfAbsent,
268 0, states::READONLY, states::EDITABLE);
270 MapTokenType(aElement, aState, data);
271 return true;
272 }
274 case eARIAReadonlyOrEditableIfDefined:
275 {
276 static const TokenTypeData data(
277 nsGkAtoms::aria_readonly, eBoolType,
278 0, states::READONLY, states::EDITABLE);
280 MapTokenType(aElement, aState, data);
281 return true;
282 }
284 case eARIARequired:
285 {
286 static const TokenTypeData data(
287 nsGkAtoms::aria_required, eBoolType,
288 0, states::REQUIRED);
290 MapTokenType(aElement, aState, data);
291 return true;
292 }
294 case eARIASelectable:
295 {
296 static const TokenTypeData data(
297 nsGkAtoms::aria_selected, eBoolType | eDefinedIfAbsent,
298 states::SELECTABLE, states::SELECTED);
300 MapTokenType(aElement, aState, data);
301 return true;
302 }
304 case eARIASelectableIfDefined:
305 {
306 static const TokenTypeData data(
307 nsGkAtoms::aria_selected, eBoolType,
308 states::SELECTABLE, states::SELECTED);
310 MapTokenType(aElement, aState, data);
311 return true;
312 }
314 case eReadonlyUntilEditable:
315 {
316 if (!(*aState & states::EDITABLE))
317 *aState |= states::READONLY;
319 return true;
320 }
322 case eIndeterminateIfNoValue:
323 {
324 if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow) &&
325 !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext))
326 *aState |= states::MIXED;
328 return true;
329 }
331 case eFocusableUntilDisabled:
332 {
333 if (!nsAccUtils::HasDefinedARIAToken(aElement, nsGkAtoms::aria_disabled) ||
334 aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_disabled,
335 nsGkAtoms::_false, eCaseMatters))
336 *aState |= states::FOCUSABLE;
338 return true;
339 }
341 default:
342 return false;
343 }
344 }
346 static void
347 MapEnumType(dom::Element* aElement, uint64_t* aState, const EnumTypeData& aData)
348 {
349 switch (aElement->FindAttrValueIn(kNameSpaceID_None, aData.mAttrName,
350 &aData.mValue1, eCaseMatters)) {
351 case 0:
352 *aState |= aData.mState1;
353 return;
354 case 1:
355 *aState |= aData.mState2;
356 return;
357 case 2:
358 *aState |= aData.mState3;
359 return;
360 }
362 *aState |= aData.mDefaultState;
363 }
365 static void
366 MapTokenType(dom::Element* aElement, uint64_t* aState,
367 const TokenTypeData& aData)
368 {
369 if (nsAccUtils::HasDefinedARIAToken(aElement, aData.mAttrName)) {
370 if ((aData.mType & eMixedType) &&
371 aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
372 nsGkAtoms::mixed, eCaseMatters)) {
373 *aState |= aData.mPermanentState | states::MIXED;
374 return;
375 }
377 if (aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
378 nsGkAtoms::_false, eCaseMatters)) {
379 *aState |= aData.mPermanentState | aData.mFalseState;
380 return;
381 }
383 *aState |= aData.mPermanentState | aData.mTrueState;
384 return;
385 }
387 if (aData.mType & eDefinedIfAbsent)
388 *aState |= aData.mPermanentState | aData.mFalseState;
389 }