Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/base/nsAccUtils.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "nsAccUtils.h"
7
8
#include "Accessible-inl.h"
9
#include "ARIAMap.h"
10
#include "nsAccessibilityService.h"
11
#include "nsCoreUtils.h"
12
#include "DocAccessible.h"
13
#include "HyperTextAccessible.h"
14
#include "nsIAccessibleTypes.h"
15
#include "Role.h"
16
#include "States.h"
17
#include "TextLeafAccessible.h"
18
19
#include "nsIDOMXULContainerElement.h"
20
#include "nsIPersistentProperties2.h"
21
#include "mozilla/dom/Element.h"
22
23
using namespace mozilla;
24
using namespace mozilla::a11y;
25
26
void
27
nsAccUtils::GetAccAttr(nsIPersistentProperties *aAttributes,
28
                       nsAtom *aAttrName, nsAString& aAttrValue)
29
0
{
30
0
  aAttrValue.Truncate();
31
0
32
0
  aAttributes->GetStringProperty(nsAtomCString(aAttrName), aAttrValue);
33
0
}
34
35
void
36
nsAccUtils::SetAccAttr(nsIPersistentProperties *aAttributes,
37
                       nsAtom *aAttrName, const nsAString& aAttrValue)
38
0
{
39
0
  nsAutoString oldValue;
40
0
  aAttributes->SetStringProperty(nsAtomCString(aAttrName), aAttrValue, oldValue);
41
0
}
42
43
void
44
nsAccUtils::SetAccAttr(nsIPersistentProperties *aAttributes,
45
                       nsAtom* aAttrName, nsAtom* aAttrValue)
46
0
{
47
0
  nsAutoString oldValue;
48
0
  aAttributes->SetStringProperty(nsAtomCString(aAttrName),
49
0
                                 nsAtomString(aAttrValue), oldValue);
50
0
}
51
52
void
53
nsAccUtils::SetAccGroupAttrs(nsIPersistentProperties *aAttributes,
54
                             int32_t aLevel, int32_t aSetSize,
55
                             int32_t aPosInSet)
56
0
{
57
0
  nsAutoString value;
58
0
59
0
  if (aLevel) {
60
0
    value.AppendInt(aLevel);
61
0
    SetAccAttr(aAttributes, nsGkAtoms::level, value);
62
0
  }
63
0
64
0
  if (aSetSize && aPosInSet) {
65
0
    value.Truncate();
66
0
    value.AppendInt(aPosInSet);
67
0
    SetAccAttr(aAttributes, nsGkAtoms::posinset, value);
68
0
69
0
    value.Truncate();
70
0
    value.AppendInt(aSetSize);
71
0
    SetAccAttr(aAttributes, nsGkAtoms::setsize, value);
72
0
  }
73
0
}
74
75
int32_t
76
nsAccUtils::GetDefaultLevel(const Accessible* aAccessible)
77
0
{
78
0
  roles::Role role = aAccessible->Role();
79
0
80
0
  if (role == roles::OUTLINEITEM)
81
0
    return 1;
82
0
83
0
  if (role == roles::ROW) {
84
0
    Accessible* parent = aAccessible->Parent();
85
0
    // It is a row inside flatten treegrid. Group level is always 1 until it
86
0
    // is overriden by aria-level attribute.
87
0
    if (parent && parent->Role() == roles::TREE_TABLE)
88
0
      return 1;
89
0
  }
90
0
91
0
  return 0;
92
0
}
93
94
int32_t
95
nsAccUtils::GetARIAOrDefaultLevel(const Accessible* aAccessible)
96
0
{
97
0
  int32_t level = 0;
98
0
  nsCoreUtils::GetUIntAttr(aAccessible->GetContent(),
99
0
                           nsGkAtoms::aria_level, &level);
100
0
101
0
  if (level != 0)
102
0
    return level;
103
0
104
0
  return GetDefaultLevel(aAccessible);
105
0
}
106
107
int32_t
108
nsAccUtils::GetLevelForXULContainerItem(nsIContent *aContent)
109
0
{
110
0
  nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent));
111
0
  if (!item)
112
0
    return 0;
113
0
114
0
  nsCOMPtr<nsIDOMXULContainerElement> container;
115
0
  item->GetParentContainer(getter_AddRefs(container));
116
0
  if (!container)
117
0
    return 0;
118
0
119
0
  // Get level of the item.
120
0
  int32_t level = -1;
121
0
  while (container) {
122
0
    level++;
123
0
124
0
    nsCOMPtr<nsIDOMXULContainerElement> parentContainer;
125
0
    container->GetParentContainer(getter_AddRefs(parentContainer));
126
0
    parentContainer.swap(container);
127
0
  }
128
0
129
0
  return level;
130
0
}
131
132
void
133
nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
134
                                       nsIContent* aStartContent,
135
                                       dom::Element* aTopEl)
136
0
{
137
0
  nsAutoString live, relevant, busy;
138
0
  nsIContent* ancestor = aStartContent;
139
0
  while (ancestor) {
140
0
141
0
    // container-relevant attribute
142
0
    if (relevant.IsEmpty() &&
143
0
        HasDefinedARIAToken(ancestor, nsGkAtoms::aria_relevant) &&
144
0
        ancestor->AsElement()->GetAttr(kNameSpaceID_None,
145
0
                                       nsGkAtoms::aria_relevant, relevant))
146
0
      SetAccAttr(aAttributes, nsGkAtoms::containerRelevant, relevant);
147
0
148
0
    // container-live, and container-live-role attributes
149
0
    if (live.IsEmpty()) {
150
0
      const nsRoleMapEntry* role = nullptr;
151
0
      if (ancestor->IsElement()) {
152
0
        role = aria::GetRoleMap(ancestor->AsElement());
153
0
      }
154
0
      if (HasDefinedARIAToken(ancestor, nsGkAtoms::aria_live)) {
155
0
        ancestor->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live, live);
156
0
      } else if (role) {
157
0
        GetLiveAttrValue(role->liveAttRule, live);
158
0
      }
159
0
      if (!live.IsEmpty()) {
160
0
        SetAccAttr(aAttributes, nsGkAtoms::containerLive, live);
161
0
        if (role) {
162
0
          SetAccAttr(aAttributes, nsGkAtoms::containerLiveRole,
163
0
                     role->ARIARoleString());
164
0
        }
165
0
      }
166
0
    }
167
0
168
0
    // container-atomic attribute
169
0
    if (ancestor->IsElement() &&
170
0
        ancestor->AsElement()->AttrValueIs(kNameSpaceID_None,
171
0
                                           nsGkAtoms::aria_atomic,
172
0
                                           nsGkAtoms::_true, eCaseMatters)) {
173
0
      SetAccAttr(aAttributes, nsGkAtoms::containerAtomic,
174
0
                 NS_LITERAL_STRING("true"));
175
0
    }
176
0
177
0
    // container-busy attribute
178
0
    if (busy.IsEmpty() &&
179
0
        HasDefinedARIAToken(ancestor, nsGkAtoms::aria_busy) &&
180
0
        ancestor->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_busy, busy))
181
0
      SetAccAttr(aAttributes, nsGkAtoms::containerBusy, busy);
182
0
183
0
    if (ancestor == aTopEl)
184
0
      break;
185
0
186
0
    ancestor = ancestor->GetParent();
187
0
    if (!ancestor)
188
0
      ancestor = aTopEl; // Use <body>/<frameset>
189
0
  }
190
0
}
191
192
bool
193
nsAccUtils::HasDefinedARIAToken(nsIContent *aContent, nsAtom *aAtom)
194
0
{
195
0
  NS_ASSERTION(aContent, "aContent is null in call to HasDefinedARIAToken!");
196
0
197
0
  if (!aContent->IsElement())
198
0
    return false;
199
0
200
0
  Element* element = aContent->AsElement();
201
0
  if (!element->HasAttr(kNameSpaceID_None, aAtom) ||
202
0
      element->AttrValueIs(kNameSpaceID_None, aAtom, nsGkAtoms::_empty,
203
0
                           eCaseMatters) ||
204
0
      element->AttrValueIs(kNameSpaceID_None, aAtom, nsGkAtoms::_undefined,
205
0
                           eCaseMatters)) {
206
0
        return false;
207
0
  }
208
0
  return true;
209
0
}
210
211
nsAtom*
212
nsAccUtils::GetARIAToken(dom::Element* aElement, nsAtom* aAttr)
213
0
{
214
0
  if (!HasDefinedARIAToken(aElement, aAttr))
215
0
    return nsGkAtoms::_empty;
216
0
217
0
  static Element::AttrValuesArray tokens[] =
218
0
    { &nsGkAtoms::_false, &nsGkAtoms::_true,
219
0
      &nsGkAtoms::mixed, nullptr};
220
0
221
0
  int32_t idx = aElement->FindAttrValueIn(kNameSpaceID_None,
222
0
                                          aAttr, tokens, eCaseMatters);
223
0
  if (idx >= 0)
224
0
    return *(tokens[idx]);
225
0
226
0
  return nullptr;
227
0
}
228
229
Accessible*
230
nsAccUtils::GetSelectableContainer(Accessible* aAccessible, uint64_t aState)
231
0
{
232
0
  if (!aAccessible)
233
0
    return nullptr;
234
0
235
0
  if (!(aState & states::SELECTABLE))
236
0
    return nullptr;
237
0
238
0
  Accessible* parent = aAccessible;
239
0
  while ((parent = parent->Parent()) && !parent->IsSelect()) {
240
0
    if (parent->Role() == roles::PANE)
241
0
      return nullptr;
242
0
  }
243
0
  return parent;
244
0
}
245
246
bool
247
nsAccUtils::IsDOMAttrTrue(const Accessible* aAccessible, nsAtom* aAttr)
248
0
{
249
0
  dom::Element* el = aAccessible->Elm();
250
0
  return el && el->AttrValueIs(kNameSpaceID_None, aAttr, nsGkAtoms::_true,
251
0
                               eCaseMatters);
252
0
}
253
254
Accessible*
255
nsAccUtils::TableFor(Accessible* aRow)
256
0
{
257
0
  if (aRow) {
258
0
    Accessible* table = aRow->Parent();
259
0
    if (table) {
260
0
      roles::Role tableRole = table->Role();
261
0
      if (tableRole == roles::GROUPING) { // if there's a rowgroup.
262
0
        table = table->Parent();
263
0
        if (table)
264
0
          tableRole = table->Role();
265
0
      }
266
0
267
0
      return (tableRole == roles::TABLE || tableRole == roles::TREE_TABLE ||
268
0
              tableRole == roles::MATHML_TABLE) ? table : nullptr;
269
0
    }
270
0
  }
271
0
272
0
  return nullptr;
273
0
}
274
275
HyperTextAccessible*
276
nsAccUtils::GetTextContainer(nsINode* aNode)
277
0
{
278
0
  // Get text accessible containing the result node.
279
0
  DocAccessible* doc =
280
0
    GetAccService()->GetDocAccessible(aNode->OwnerDoc());
281
0
  Accessible* accessible =
282
0
    doc ? doc->GetAccessibleOrContainer(aNode) : nullptr;
283
0
  if (!accessible)
284
0
    return nullptr;
285
0
286
0
  do {
287
0
    HyperTextAccessible* textAcc = accessible->AsHyperText();
288
0
    if (textAcc)
289
0
      return textAcc;
290
0
291
0
    accessible = accessible->Parent();
292
0
  } while (accessible);
293
0
294
0
  return nullptr;
295
0
}
296
297
nsIntPoint
298
nsAccUtils::ConvertToScreenCoords(int32_t aX, int32_t aY,
299
                                  uint32_t aCoordinateType,
300
                                  Accessible* aAccessible)
301
0
{
302
0
  nsIntPoint coords(aX, aY);
303
0
304
0
  switch (aCoordinateType) {
305
0
    case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
306
0
      break;
307
0
308
0
    case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
309
0
    {
310
0
      coords += nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
311
0
      break;
312
0
    }
313
0
314
0
    case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
315
0
    {
316
0
      coords += GetScreenCoordsForParent(aAccessible);
317
0
      break;
318
0
    }
319
0
320
0
    default:
321
0
      MOZ_ASSERT_UNREACHABLE("invalid coord type!");
322
0
  }
323
0
324
0
  return coords;
325
0
}
326
327
void
328
nsAccUtils::ConvertScreenCoordsTo(int32_t *aX, int32_t *aY,
329
                                  uint32_t aCoordinateType,
330
                                  Accessible* aAccessible)
331
0
{
332
0
  switch (aCoordinateType) {
333
0
    case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
334
0
      break;
335
0
336
0
    case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
337
0
    {
338
0
      nsIntPoint coords = nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
339
0
      *aX -= coords.x;
340
0
      *aY -= coords.y;
341
0
      break;
342
0
    }
343
0
344
0
    case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
345
0
    {
346
0
      nsIntPoint coords = GetScreenCoordsForParent(aAccessible);
347
0
      *aX -= coords.x;
348
0
      *aY -= coords.y;
349
0
      break;
350
0
    }
351
0
352
0
    default:
353
0
      MOZ_ASSERT_UNREACHABLE("invalid coord type!");
354
0
  }
355
0
}
356
357
nsIntPoint
358
nsAccUtils::GetScreenCoordsForParent(Accessible* aAccessible)
359
0
{
360
0
  Accessible* parent = aAccessible->Parent();
361
0
  if (!parent)
362
0
    return nsIntPoint(0, 0);
363
0
364
0
  nsIFrame *parentFrame = parent->GetFrame();
365
0
  if (!parentFrame)
366
0
    return nsIntPoint(0, 0);
367
0
368
0
  nsRect rect = parentFrame->GetScreenRectInAppUnits();
369
0
  return nsPoint(rect.X(), rect.Y()).
370
0
    ToNearestPixels(parentFrame->PresContext()->AppUnitsPerDevPixel());
371
0
}
372
373
bool
374
nsAccUtils::GetLiveAttrValue(uint32_t aRule, nsAString& aValue)
375
0
{
376
0
  switch (aRule) {
377
0
    case eOffLiveAttr:
378
0
      aValue = NS_LITERAL_STRING("off");
379
0
      return true;
380
0
    case ePoliteLiveAttr:
381
0
      aValue = NS_LITERAL_STRING("polite");
382
0
      return true;
383
0
  }
384
0
385
0
  return false;
386
0
}
387
388
#ifdef DEBUG
389
390
bool
391
nsAccUtils::IsTextInterfaceSupportCorrect(Accessible* aAccessible)
392
{
393
  // Don't test for accessible docs, it makes us create accessibles too
394
  // early and fire mutation events before we need to
395
  if (aAccessible->IsDoc())
396
    return true;
397
398
  bool foundText = false;
399
  uint32_t childCount = aAccessible->ChildCount();
400
  for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
401
    Accessible* child = aAccessible->GetChildAt(childIdx);
402
    if (child->IsText()) {
403
      foundText = true;
404
      break;
405
    }
406
  }
407
408
  return !foundText || aAccessible->IsHyperText();
409
}
410
#endif
411
412
uint32_t
413
nsAccUtils::TextLength(Accessible* aAccessible)
414
0
{
415
0
  if (!aAccessible->IsText()) {
416
0
    return 1;
417
0
  }
418
0
419
0
  TextLeafAccessible* textLeaf = aAccessible->AsTextLeaf();
420
0
  if (textLeaf)
421
0
    return textLeaf->Text().Length();
422
0
423
0
  // For list bullets (or anything other accessible which would compute its own
424
0
  // text. They don't have their own frame.
425
0
  // XXX In the future, list bullets may have frame and anon content, so
426
0
  // we should be able to remove this at that point
427
0
  nsAutoString text;
428
0
  aAccessible->AppendTextTo(text); // Get all the text
429
0
  return text.Length();
430
0
}
431
432
bool
433
nsAccUtils::MustPrune(Accessible* aAccessible)
434
0
{
435
0
  roles::Role role = aAccessible->Role();
436
0
437
0
  return
438
0
    // Always prune the tree for sliders, as it doesn't make sense for a slider
439
0
    // to have descendants and this confuses NVDA.
440
0
    role == roles::SLIDER ||
441
0
    // Don't prune the tree for certain roles if the tree is more complex than
442
0
    // a single text leaf.
443
0
    (
444
0
     (
445
0
      role == roles::MENUITEM ||
446
0
      role == roles::COMBOBOX_OPTION ||
447
0
      role == roles::OPTION ||
448
0
      role == roles::ENTRY ||
449
0
      role == roles::FLAT_EQUATION ||
450
0
      role == roles::PASSWORD_TEXT ||
451
0
      role == roles::PUSHBUTTON ||
452
0
      role == roles::TOGGLE_BUTTON ||
453
0
      role == roles::GRAPHIC ||
454
0
      role == roles::PROGRESSBAR ||
455
0
      role == roles::SEPARATOR
456
0
     ) &&
457
0
     aAccessible->ContentChildCount() == 1 &&
458
0
     aAccessible->ContentChildAt(0)->IsTextLeaf()
459
0
    );
460
0
}