Coverage Report

Created: 2018-09-25 14:53

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