Bug 1631735 Part 1: Make nsCocoaWindow animated transitions asynchronous and atomic...
[gecko.git] / accessible / base / ARIAStateMap.cpp
blob6bf20cf1cce63f74ba654143e7b6fe73072f6c57
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.
20 struct EnumTypeData {
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;
35 enum ETokenType {
36 eBoolType = 0,
37 eMixedType = 1, // can take 'mixed' value
38 eDefinedIfAbsent = 2 // permanent and false state are applied if absent
41 /**
42 * Used to store state map rule data for ARIA attribute of token type (including
43 * mixed value).
45 struct TokenTypeData {
46 TokenTypeData(nsAtom* aAttrName, uint32_t aType, uint64_t aPermanentState,
47 uint64_t aTrueState, uint64_t aFalseState = 0)
48 : mAttrName(aAttrName),
49 mType(aType),
50 mPermanentState(aPermanentState),
51 mTrueState(aTrueState),
52 mFalseState(aFalseState) {}
54 // ARIA attribute name.
55 nsAtom* const mAttrName;
57 // Type.
58 const uint32_t mType;
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;
69 /**
70 * Map enum type attribute value to accessible state.
72 static void MapEnumType(dom::Element* aElement, uint64_t* aState,
73 const EnumTypeData& aData);
75 /**
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,
82 uint64_t* aState) {
83 switch (aRule) {
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},
91 0};
93 MapEnumType(aElement, aState, data);
94 return true;
97 case eARIABusy: {
98 static const EnumTypeData data = {
99 nsGkAtoms::aria_busy,
100 {nsGkAtoms::_true, nsGkAtoms::error, nullptr},
101 {states::BUSY, states::INVALID},
104 MapEnumType(aElement, aState, data);
105 return true;
108 case eARIACheckableBool: {
109 static const TokenTypeData data(nsGkAtoms::aria_checked,
110 eBoolType | eDefinedIfAbsent,
111 states::CHECKABLE, states::CHECKED);
113 MapTokenType(aElement, aState, data);
114 return true;
117 case eARIACheckableMixed: {
118 static const TokenTypeData data(nsGkAtoms::aria_checked,
119 eMixedType | eDefinedIfAbsent,
120 states::CHECKABLE, states::CHECKED);
122 MapTokenType(aElement, aState, data);
123 return true;
126 case eARIACheckedMixed: {
127 static const TokenTypeData data(nsGkAtoms::aria_checked, eMixedType,
128 states::CHECKABLE, states::CHECKED);
130 MapTokenType(aElement, aState, data);
131 return true;
134 case eARIACurrent: {
135 static const TokenTypeData data(nsGkAtoms::aria_current, eBoolType, 0,
136 states::CURRENT);
138 MapTokenType(aElement, aState, data);
139 return true;
142 case eARIADisabled: {
143 static const TokenTypeData data(nsGkAtoms::aria_disabled, eBoolType, 0,
144 states::UNAVAILABLE);
146 MapTokenType(aElement, aState, data);
147 return true;
150 case eARIAExpanded: {
151 static const TokenTypeData data(nsGkAtoms::aria_expanded, eBoolType, 0,
152 states::EXPANDED, states::COLLAPSED);
154 MapTokenType(aElement, aState, data);
155 return true;
158 case eARIAHasPopup: {
159 static const TokenTypeData data(nsGkAtoms::aria_haspopup, eBoolType, 0,
160 states::HASPOPUP);
162 MapTokenType(aElement, aState, data);
163 return true;
166 case eARIAInvalid: {
167 static const TokenTypeData data(nsGkAtoms::aria_invalid, eBoolType, 0,
168 states::INVALID);
170 MapTokenType(aElement, aState, data);
171 return true;
174 case eARIAModal: {
175 static const TokenTypeData data(nsGkAtoms::aria_modal, eBoolType, 0,
176 states::MODAL);
178 MapTokenType(aElement, aState, data);
179 return true;
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);
188 return true;
191 case eARIAMultiSelectable: {
192 static const TokenTypeData data(
193 nsGkAtoms::aria_multiselectable, eBoolType, 0,
194 states::MULTISELECTABLE | states::EXTSELECTABLE);
196 MapTokenType(aElement, aState, data);
197 return true;
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);
208 return true;
211 case eARIAPressed: {
212 static const TokenTypeData data(nsGkAtoms::aria_pressed, eMixedType, 0,
213 states::PRESSED);
215 MapTokenType(aElement, aState, data);
216 return true;
219 case eARIAReadonly: {
220 static const TokenTypeData data(nsGkAtoms::aria_readonly, eBoolType, 0,
221 states::READONLY);
223 MapTokenType(aElement, aState, data);
224 return true;
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);
233 return true;
236 case eARIARequired: {
237 static const TokenTypeData data(nsGkAtoms::aria_required, eBoolType, 0,
238 states::REQUIRED);
240 MapTokenType(aElement, aState, data);
241 return true;
244 case eARIASelectable: {
245 static const TokenTypeData data(nsGkAtoms::aria_selected,
246 eBoolType | eDefinedIfAbsent,
247 states::SELECTABLE, states::SELECTED);
249 MapTokenType(aElement, aState, data);
250 return true;
253 case eARIASelectableIfDefined: {
254 static const TokenTypeData data(nsGkAtoms::aria_selected, eBoolType,
255 states::SELECTABLE, states::SELECTED);
257 MapTokenType(aElement, aState, data);
258 return true;
261 case eReadonlyUntilEditable: {
262 if (!(*aState & states::EDITABLE)) *aState |= states::READONLY;
264 return true;
267 case eIndeterminateIfNoValue: {
268 if (!nsAccUtils::HasARIAAttr(aElement, nsGkAtoms::aria_valuenow) &&
269 !nsAccUtils::HasARIAAttr(aElement, nsGkAtoms::aria_valuetext)) {
270 *aState |= states::MIXED;
273 return true;
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;
284 return true;
287 default:
288 return false;
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)) {
296 case 0:
297 *aState = (*aState & ~aData.mClearState) | aData.mStates[0];
298 return;
299 case 1:
300 *aState = (*aState & ~aData.mClearState) | aData.mStates[1];
301 return;
302 case 2:
303 *aState = (*aState & ~aData.mClearState) | aData.mStates[2];
304 return;
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,
312 eCaseMatters)) {
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;
318 return;
321 if (nsAccUtils::ARIAAttrValueIs(aElement, aData.mAttrName,
322 nsGkAtoms::_false, eCaseMatters)) {
323 *aState |= aData.mPermanentState | aData.mFalseState;
324 return;
327 *aState |= aData.mPermanentState | aData.mTrueState;
328 return;
331 if (aData.mType & eDefinedIfAbsent) {
332 *aState |= aData.mPermanentState | aData.mFalseState;