tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }