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/. */
8 #include "nsAccUtils.h"
11 #include "mozilla/dom/Element.h"
13 using namespace mozilla
;
14 using namespace mozilla::a11y
;
15 using namespace mozilla::a11y::aria
;
18 * Used to store state map rule data for ARIA attribute of enum type.
21 // ARIA attribute name.
22 nsStaticAtom
* const mAttrName
;
24 // States if the attribute value is matched to the enum value. Used as
25 // Element::AttrValuesArray, last item must be nullptr.
26 nsStaticAtom
* const mValues
[4];
28 // States applied if corresponding enum values are matched.
29 const uint64_t mStates
[3];
31 // States to clear in case of match.
32 const uint64_t mClearState
;
37 eMixedType
= 1, // can take 'mixed' value
38 eDefinedIfAbsent
= 2 // permanent and false state are applied if absent
42 * Used to store state map rule data for ARIA attribute of token type (including
45 struct TokenTypeData
{
46 TokenTypeData(nsAtom
* aAttrName
, uint32_t aType
, uint64_t aPermanentState
,
47 uint64_t aTrueState
, uint64_t aFalseState
= 0)
48 : mAttrName(aAttrName
),
50 mPermanentState(aPermanentState
),
51 mTrueState(aTrueState
),
52 mFalseState(aFalseState
) {}
54 // ARIA attribute name.
55 nsAtom
* const mAttrName
;
60 // State applied if the attribute is defined or mType doesn't have
61 // eDefinedIfAbsent flag set.
62 const uint64_t mPermanentState
;
64 // States applied if the attribute value is true/false.
65 const uint64_t mTrueState
;
66 const uint64_t mFalseState
;
70 * Map enum type attribute value to accessible state.
72 static void MapEnumType(dom::Element
* aElement
, uint64_t* aState
,
73 const EnumTypeData
& aData
);
76 * Map token type attribute value to states.
78 static void MapTokenType(dom::Element
* aContent
, uint64_t* aState
,
79 const TokenTypeData
& aData
);
81 bool aria::MapToState(EStateRule aRule
, dom::Element
* aElement
,
84 case eARIAAutoComplete
: {
85 static const EnumTypeData data
= {
86 nsGkAtoms::aria_autocomplete
,
87 {nsGkAtoms::inlinevalue
, nsGkAtoms::list_
, nsGkAtoms::both
, nullptr},
88 {states::SUPPORTS_AUTOCOMPLETION
,
89 states::HASPOPUP
| states::SUPPORTS_AUTOCOMPLETION
,
90 states::HASPOPUP
| states::SUPPORTS_AUTOCOMPLETION
},
93 MapEnumType(aElement
, aState
, data
);
98 static const EnumTypeData data
= {
100 {nsGkAtoms::_true
, nsGkAtoms::error
, nullptr},
101 {states::BUSY
, states::INVALID
},
104 MapEnumType(aElement
, aState
, data
);
108 case eARIACheckableBool
: {
109 static const TokenTypeData
data(nsGkAtoms::aria_checked
,
110 eBoolType
| eDefinedIfAbsent
,
111 states::CHECKABLE
, states::CHECKED
);
113 MapTokenType(aElement
, aState
, data
);
117 case eARIACheckableMixed
: {
118 static const TokenTypeData
data(nsGkAtoms::aria_checked
,
119 eMixedType
| eDefinedIfAbsent
,
120 states::CHECKABLE
, states::CHECKED
);
122 MapTokenType(aElement
, aState
, data
);
126 case eARIACheckedMixed
: {
127 static const TokenTypeData
data(nsGkAtoms::aria_checked
, eMixedType
,
128 states::CHECKABLE
, states::CHECKED
);
130 MapTokenType(aElement
, aState
, data
);
135 static const TokenTypeData
data(nsGkAtoms::aria_current
, eBoolType
, 0,
138 MapTokenType(aElement
, aState
, data
);
142 case eARIADisabled
: {
143 static const TokenTypeData
data(nsGkAtoms::aria_disabled
, eBoolType
, 0,
144 states::UNAVAILABLE
);
146 MapTokenType(aElement
, aState
, data
);
150 case eARIAExpanded
: {
151 static const TokenTypeData
data(nsGkAtoms::aria_expanded
, eBoolType
, 0,
152 states::EXPANDED
, states::COLLAPSED
);
154 MapTokenType(aElement
, aState
, data
);
158 case eARIAHasPopup
: {
159 static const TokenTypeData
data(nsGkAtoms::aria_haspopup
, eBoolType
, 0,
162 MapTokenType(aElement
, aState
, data
);
167 static const TokenTypeData
data(nsGkAtoms::aria_invalid
, eBoolType
, 0,
170 MapTokenType(aElement
, aState
, data
);
175 static const TokenTypeData
data(nsGkAtoms::aria_modal
, eBoolType
, 0,
178 MapTokenType(aElement
, aState
, data
);
182 case eARIAMultiline
: {
183 static const TokenTypeData
data(nsGkAtoms::aria_multiline
,
184 eBoolType
| eDefinedIfAbsent
, 0,
185 states::MULTI_LINE
, states::SINGLE_LINE
);
187 MapTokenType(aElement
, aState
, data
);
191 case eARIAMultiSelectable
: {
192 static const TokenTypeData
data(
193 nsGkAtoms::aria_multiselectable
, eBoolType
, 0,
194 states::MULTISELECTABLE
| states::EXTSELECTABLE
);
196 MapTokenType(aElement
, aState
, data
);
200 case eARIAOrientation
: {
201 static const EnumTypeData data
= {
202 nsGkAtoms::aria_orientation
,
203 {nsGkAtoms::horizontal
, nsGkAtoms::vertical
, nullptr},
204 {states::HORIZONTAL
, states::VERTICAL
},
205 states::HORIZONTAL
| states::VERTICAL
};
207 MapEnumType(aElement
, aState
, data
);
212 static const TokenTypeData
data(nsGkAtoms::aria_pressed
, eMixedType
, 0,
215 MapTokenType(aElement
, aState
, data
);
219 case eARIAReadonly
: {
220 static const TokenTypeData
data(nsGkAtoms::aria_readonly
, eBoolType
, 0,
223 MapTokenType(aElement
, aState
, data
);
227 case eARIAReadonlyOrEditable
: {
228 static const TokenTypeData
data(nsGkAtoms::aria_readonly
,
229 eBoolType
| eDefinedIfAbsent
, 0,
230 states::READONLY
, states::EDITABLE
);
232 MapTokenType(aElement
, aState
, data
);
236 case eARIARequired
: {
237 static const TokenTypeData
data(nsGkAtoms::aria_required
, eBoolType
, 0,
240 MapTokenType(aElement
, aState
, data
);
244 case eARIASelectable
: {
245 static const TokenTypeData
data(nsGkAtoms::aria_selected
,
246 eBoolType
| eDefinedIfAbsent
,
247 states::SELECTABLE
, states::SELECTED
);
249 MapTokenType(aElement
, aState
, data
);
253 case eARIASelectableIfDefined
: {
254 static const TokenTypeData
data(nsGkAtoms::aria_selected
, eBoolType
,
255 states::SELECTABLE
, states::SELECTED
);
257 MapTokenType(aElement
, aState
, data
);
261 case eReadonlyUntilEditable
: {
262 if (!(*aState
& states::EDITABLE
)) *aState
|= states::READONLY
;
267 case eIndeterminateIfNoValue
: {
268 if (!nsAccUtils::HasARIAAttr(aElement
, nsGkAtoms::aria_valuenow
) &&
269 !nsAccUtils::HasARIAAttr(aElement
, nsGkAtoms::aria_valuetext
)) {
270 *aState
|= states::MIXED
;
276 case eFocusableUntilDisabled
: {
277 if (!nsAccUtils::HasDefinedARIAToken(aElement
,
278 nsGkAtoms::aria_disabled
) ||
279 nsAccUtils::ARIAAttrValueIs(aElement
, nsGkAtoms::aria_disabled
,
280 nsGkAtoms::_false
, eCaseMatters
)) {
281 *aState
|= states::FOCUSABLE
;
292 static void MapEnumType(dom::Element
* aElement
, uint64_t* aState
,
293 const EnumTypeData
& aData
) {
294 switch (nsAccUtils::FindARIAAttrValueIn(aElement
, aData
.mAttrName
,
295 aData
.mValues
, eCaseMatters
)) {
297 *aState
= (*aState
& ~aData
.mClearState
) | aData
.mStates
[0];
300 *aState
= (*aState
& ~aData
.mClearState
) | aData
.mStates
[1];
303 *aState
= (*aState
& ~aData
.mClearState
) | aData
.mStates
[2];
308 static void MapTokenType(dom::Element
* aElement
, uint64_t* aState
,
309 const TokenTypeData
& aData
) {
310 if (nsAccUtils::HasDefinedARIAToken(aElement
, aData
.mAttrName
)) {
311 if (nsAccUtils::ARIAAttrValueIs(aElement
, aData
.mAttrName
, nsGkAtoms::mixed
,
313 if (aData
.mType
& eMixedType
) {
314 *aState
|= aData
.mPermanentState
| states::MIXED
;
315 } else { // unsupported use of 'mixed' is an authoring error
316 *aState
|= aData
.mPermanentState
| aData
.mFalseState
;
321 if (nsAccUtils::ARIAAttrValueIs(aElement
, aData
.mAttrName
,
322 nsGkAtoms::_false
, eCaseMatters
)) {
323 *aState
|= aData
.mPermanentState
| aData
.mFalseState
;
327 *aState
|= aData
.mPermanentState
| aData
.mTrueState
;
331 if (aData
.mType
& eDefinedIfAbsent
) {
332 *aState
|= aData
.mPermanentState
| aData
.mFalseState
;