ARIAStateMap.cpp (10967B)
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 // ARIA attribute name. 22 nsStaticAtom* const mAttrName; 23 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]; 27 28 // States applied if corresponding enum values are matched. 29 const uint64_t mStates[3]; 30 31 // States to clear in case of match. 32 const uint64_t mClearState; 33 34 // State if attribute is missing or value doesn't match any enum values. 35 const uint64_t mDefaultState; 36 }; 37 38 enum ETokenType { 39 eBoolType = 0, 40 eMixedType = 1, // can take 'mixed' value 41 eDefinedIfAbsent = 2 // permanent and false state are applied if absent 42 }; 43 44 /** 45 * Used to store state map rule data for ARIA attribute of token type (including 46 * mixed value). 47 */ 48 struct TokenTypeData { 49 TokenTypeData(nsAtom* aAttrName, uint32_t aType, uint64_t aPermanentState, 50 uint64_t aTrueState, uint64_t aFalseState = 0) 51 : mAttrName(aAttrName), 52 mType(aType), 53 mPermanentState(aPermanentState), 54 mTrueState(aTrueState), 55 mFalseState(aFalseState) {} 56 57 // ARIA attribute name. 58 nsAtom* 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 aria::MapToState(EStateRule aRule, dom::Element* aElement, 85 uint64_t* aState) { 86 switch (aRule) { 87 case eARIAAutoComplete: { 88 static const EnumTypeData data = { 89 nsGkAtoms::aria_autocomplete, 90 {nsGkAtoms::inlinevalue, nsGkAtoms::list, nsGkAtoms::both, nullptr}, 91 {states::SUPPORTS_AUTOCOMPLETION, 92 states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION, 93 states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION}, 94 0, 95 0}; 96 97 MapEnumType(aElement, aState, data); 98 return true; 99 } 100 101 case eARIABusy: { 102 static const EnumTypeData data = { 103 nsGkAtoms::aria_busy, 104 {nsGkAtoms::_true, nsGkAtoms::error, nullptr}, 105 {states::BUSY, states::INVALID}, 106 0, 107 0}; 108 109 MapEnumType(aElement, aState, data); 110 return true; 111 } 112 113 case eARIACheckableBool: { 114 static const TokenTypeData data(nsGkAtoms::aria_checked, 115 eBoolType | eDefinedIfAbsent, 116 states::CHECKABLE, states::CHECKED); 117 118 MapTokenType(aElement, aState, data); 119 return true; 120 } 121 122 case eARIACheckableMixed: { 123 static const TokenTypeData data(nsGkAtoms::aria_checked, 124 eMixedType | eDefinedIfAbsent, 125 states::CHECKABLE, states::CHECKED); 126 127 MapTokenType(aElement, aState, data); 128 return true; 129 } 130 131 case eARIACheckedMixed: { 132 static const TokenTypeData data(nsGkAtoms::aria_checked, eMixedType, 133 states::CHECKABLE, states::CHECKED); 134 135 MapTokenType(aElement, aState, data); 136 return true; 137 } 138 139 case eARIACurrent: { 140 static const TokenTypeData data(nsGkAtoms::aria_current, eBoolType, 0, 141 states::CURRENT); 142 143 MapTokenType(aElement, aState, data); 144 return true; 145 } 146 147 case eARIADisabled: { 148 static const TokenTypeData data(nsGkAtoms::aria_disabled, eBoolType, 0, 149 states::UNAVAILABLE); 150 151 MapTokenType(aElement, aState, data); 152 return true; 153 } 154 155 case eARIAExpanded: { 156 static const TokenTypeData data(nsGkAtoms::aria_expanded, eBoolType, 157 states::EXPANDABLE, states::EXPANDED); 158 159 MapTokenType(aElement, aState, data); 160 return true; 161 } 162 163 case eARIAHasPopup: { 164 static const TokenTypeData data(nsGkAtoms::aria_haspopup, eBoolType, 0, 165 states::HASPOPUP); 166 167 MapTokenType(aElement, aState, data); 168 return true; 169 } 170 171 case eARIAInvalid: { 172 static const TokenTypeData data(nsGkAtoms::aria_invalid, eBoolType, 0, 173 states::INVALID); 174 175 MapTokenType(aElement, aState, data); 176 return true; 177 } 178 179 case eARIAModal: { 180 static const TokenTypeData data(nsGkAtoms::aria_modal, eBoolType, 0, 181 states::MODAL); 182 183 MapTokenType(aElement, aState, data); 184 return true; 185 } 186 187 case eARIAMultiline: { 188 static const EnumTypeData data = { 189 nsGkAtoms::aria_multiline, 190 {nsGkAtoms::_true, nsGkAtoms::_false, nullptr}, 191 {states::MULTI_LINE, states::SINGLE_LINE}, 192 states::MULTI_LINE | states::SINGLE_LINE, 193 states::SINGLE_LINE}; 194 195 MapEnumType(aElement, aState, data); 196 return true; 197 } 198 199 case eARIAMultilineByDefault: { 200 static const EnumTypeData data = { 201 nsGkAtoms::aria_multiline, 202 {nsGkAtoms::_true, nsGkAtoms::_false, nullptr}, 203 {states::MULTI_LINE, states::SINGLE_LINE}, 204 states::MULTI_LINE | states::SINGLE_LINE, 205 states::MULTI_LINE}; 206 207 MapEnumType(aElement, aState, data); 208 return true; 209 } 210 211 case eARIAMultiSelectable: { 212 static const TokenTypeData data( 213 nsGkAtoms::aria_multiselectable, eBoolType, 0, 214 states::MULTISELECTABLE | states::EXTSELECTABLE); 215 216 MapTokenType(aElement, aState, data); 217 return true; 218 } 219 220 case eARIAOrientation: { 221 static const EnumTypeData data = { 222 nsGkAtoms::aria_orientation, 223 {nsGkAtoms::horizontal, nsGkAtoms::vertical, nullptr}, 224 {states::HORIZONTAL, states::VERTICAL}, 225 states::HORIZONTAL | states::VERTICAL, 226 0}; 227 228 MapEnumType(aElement, aState, data); 229 return true; 230 } 231 232 case eARIAPressed: { 233 static const TokenTypeData data(nsGkAtoms::aria_pressed, eMixedType, 0, 234 states::PRESSED); 235 236 MapTokenType(aElement, aState, data); 237 return true; 238 } 239 240 case eARIAReadonly: { 241 static const TokenTypeData data(nsGkAtoms::aria_readonly, eBoolType, 0, 242 states::READONLY); 243 244 MapTokenType(aElement, aState, data); 245 return true; 246 } 247 248 case eARIAReadonlyOrEditable: { 249 static const TokenTypeData data(nsGkAtoms::aria_readonly, 250 eBoolType | eDefinedIfAbsent, 0, 251 states::READONLY, states::EDITABLE); 252 253 MapTokenType(aElement, aState, data); 254 return true; 255 } 256 257 case eARIARequired: { 258 static const TokenTypeData data(nsGkAtoms::aria_required, eBoolType, 0, 259 states::REQUIRED); 260 261 MapTokenType(aElement, aState, data); 262 return true; 263 } 264 265 case eARIASelectable: { 266 static const TokenTypeData data(nsGkAtoms::aria_selected, 267 eBoolType | eDefinedIfAbsent, 268 states::SELECTABLE, states::SELECTED); 269 270 MapTokenType(aElement, aState, data); 271 return true; 272 } 273 274 case eARIASelectableIfDefined: { 275 static const TokenTypeData data(nsGkAtoms::aria_selected, eBoolType, 276 states::SELECTABLE, states::SELECTED); 277 278 MapTokenType(aElement, aState, data); 279 return true; 280 } 281 282 case eReadonlyUntilEditable: { 283 if (!(*aState & states::EDITABLE)) *aState |= states::READONLY; 284 285 return true; 286 } 287 288 case eIndeterminateIfNoValue: { 289 if (!nsAccUtils::HasARIAAttr(aElement, nsGkAtoms::aria_valuenow) && 290 !nsAccUtils::HasARIAAttr(aElement, nsGkAtoms::aria_valuetext)) { 291 *aState |= states::MIXED; 292 } 293 294 return true; 295 } 296 297 case eFocusableUntilDisabled: { 298 if (!nsAccUtils::HasDefinedARIAToken(aElement, 299 nsGkAtoms::aria_disabled) || 300 nsAccUtils::ARIAAttrValueIs(aElement, nsGkAtoms::aria_disabled, 301 nsGkAtoms::_false, eCaseMatters)) { 302 *aState |= states::FOCUSABLE; 303 } 304 305 return true; 306 } 307 308 default: 309 return false; 310 } 311 } 312 313 static void MapEnumType(dom::Element* aElement, uint64_t* aState, 314 const EnumTypeData& aData) { 315 switch (nsAccUtils::FindARIAAttrValueIn(aElement, aData.mAttrName, 316 aData.mValues, eCaseMatters)) { 317 case 0: 318 *aState = (*aState & ~aData.mClearState) | aData.mStates[0]; 319 return; 320 case 1: 321 *aState = (*aState & ~aData.mClearState) | aData.mStates[1]; 322 return; 323 case 2: 324 *aState = (*aState & ~aData.mClearState) | aData.mStates[2]; 325 return; 326 default: 327 if (aData.mDefaultState) { 328 *aState = (*aState & ~aData.mClearState) | aData.mDefaultState; 329 } 330 return; 331 } 332 } 333 334 static void MapTokenType(dom::Element* aElement, uint64_t* aState, 335 const TokenTypeData& aData) { 336 if (nsAccUtils::HasDefinedARIAToken(aElement, aData.mAttrName)) { 337 if (nsAccUtils::ARIAAttrValueIs(aElement, aData.mAttrName, nsGkAtoms::mixed, 338 eCaseMatters)) { 339 if (aData.mType & eMixedType) { 340 *aState |= aData.mPermanentState | states::MIXED; 341 } else { // unsupported use of 'mixed' is an authoring error 342 *aState |= aData.mPermanentState | aData.mFalseState; 343 } 344 return; 345 } 346 347 if (nsAccUtils::ARIAAttrValueIs(aElement, aData.mAttrName, 348 nsGkAtoms::_false, eCaseMatters)) { 349 *aState |= aData.mPermanentState | aData.mFalseState; 350 return; 351 } 352 353 *aState |= aData.mPermanentState | aData.mTrueState; 354 return; 355 } 356 357 if (aData.mType & eDefinedIfAbsent) { 358 *aState |= aData.mPermanentState | aData.mFalseState; 359 } 360 }