Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/base/AccGroupInfo.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "AccGroupInfo.h"
6
#include "nsAccUtils.h"
7
#include "TableAccessible.h"
8
9
#include "Role.h"
10
#include "States.h"
11
12
using namespace mozilla::a11y;
13
14
AccGroupInfo::AccGroupInfo(const Accessible* aItem, role aRole) :
15
  mPosInSet(0), mSetSize(0), mParent(nullptr), mItem(aItem), mRole(aRole)
16
0
{
17
0
  MOZ_COUNT_CTOR(AccGroupInfo);
18
0
  Update();
19
0
}
20
21
void
22
AccGroupInfo::Update()
23
0
{
24
0
  Accessible* parent = mItem->Parent();
25
0
  if (!parent)
26
0
    return;
27
0
28
0
  int32_t indexInParent = mItem->IndexInParent();
29
0
  uint32_t siblingCount = parent->ChildCount();
30
0
  if (indexInParent == -1 ||
31
0
      indexInParent >= static_cast<int32_t>(siblingCount)) {
32
0
    NS_ERROR("Wrong index in parent! Tree invalidation problem.");
33
0
    return;
34
0
  }
35
0
36
0
  int32_t level = nsAccUtils::GetARIAOrDefaultLevel(mItem);
37
0
38
0
  // Compute position in set.
39
0
  mPosInSet = 1;
40
0
  for (int32_t idx = indexInParent - 1; idx >= 0 ; idx--) {
41
0
    Accessible* sibling = parent->GetChildAt(idx);
42
0
    roles::Role siblingRole = sibling->Role();
43
0
44
0
    // If the sibling is separator then the group is ended.
45
0
    if (siblingRole == roles::SEPARATOR)
46
0
      break;
47
0
48
0
    // If sibling is not visible and hasn't the same base role.
49
0
    if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE)
50
0
      continue;
51
0
52
0
    // Check if it's hierarchical flatten structure, i.e. if the sibling
53
0
    // level is lesser than this one then group is ended, if the sibling level
54
0
    // is greater than this one then the group is split by some child elements
55
0
    // (group will be continued).
56
0
    int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
57
0
    if (siblingLevel < level) {
58
0
      mParent = sibling;
59
0
      break;
60
0
    }
61
0
62
0
    // Skip subset.
63
0
    if (siblingLevel > level)
64
0
      continue;
65
0
66
0
    // If the previous item in the group has calculated group information then
67
0
    // build group information for this item based on found one.
68
0
    if (sibling->mBits.groupInfo) {
69
0
      mPosInSet += sibling->mBits.groupInfo->mPosInSet;
70
0
      mParent = sibling->mBits.groupInfo->mParent;
71
0
      mSetSize = sibling->mBits.groupInfo->mSetSize;
72
0
      return;
73
0
    }
74
0
75
0
    mPosInSet++;
76
0
  }
77
0
78
0
  // Compute set size.
79
0
  mSetSize = mPosInSet;
80
0
81
0
  for (uint32_t idx = indexInParent + 1; idx < siblingCount; idx++) {
82
0
    Accessible* sibling = parent->GetChildAt(idx);
83
0
84
0
    roles::Role siblingRole = sibling->Role();
85
0
86
0
    // If the sibling is separator then the group is ended.
87
0
    if (siblingRole == roles::SEPARATOR)
88
0
      break;
89
0
90
0
    // If sibling is visible and has the same base role
91
0
    if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE)
92
0
      continue;
93
0
94
0
    // and check if it's hierarchical flatten structure.
95
0
    int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
96
0
    if (siblingLevel < level)
97
0
      break;
98
0
99
0
    // Skip subset.
100
0
    if (siblingLevel > level)
101
0
      continue;
102
0
103
0
    // If the next item in the group has calculated group information then
104
0
    // build group information for this item based on found one.
105
0
    if (sibling->mBits.groupInfo) {
106
0
      mParent = sibling->mBits.groupInfo->mParent;
107
0
      mSetSize = sibling->mBits.groupInfo->mSetSize;
108
0
      return;
109
0
    }
110
0
111
0
    mSetSize++;
112
0
  }
113
0
114
0
  if (mParent)
115
0
    return;
116
0
117
0
  roles::Role parentRole = parent->Role();
118
0
  if (ShouldReportRelations(mRole, parentRole))
119
0
    mParent = parent;
120
0
121
0
  // ARIA tree and list can be arranged by using ARIA groups to organize levels.
122
0
  if (parentRole != roles::GROUPING)
123
0
    return;
124
0
125
0
  // Way #1 for ARIA tree (not ARIA treegrid): previous sibling of a group is a
126
0
  // parent. In other words the parent of the tree item will be a group and
127
0
  // the previous tree item of the group is a conceptual parent of the tree
128
0
  // item.
129
0
  if (mRole == roles::OUTLINEITEM) {
130
0
    Accessible* parentPrevSibling = parent->PrevSibling();
131
0
    if (parentPrevSibling && parentPrevSibling->Role() == mRole) {
132
0
      mParent = parentPrevSibling;
133
0
      return;
134
0
    }
135
0
  }
136
0
137
0
  // Way #2 for ARIA list and tree: group is a child of an item. In other words
138
0
  // the parent of the item will be a group and containing item of the group is
139
0
  // a conceptual parent of the item.
140
0
  if (mRole == roles::LISTITEM || mRole == roles::OUTLINEITEM) {
141
0
    Accessible* grandParent = parent->Parent();
142
0
    if (grandParent && grandParent->Role() == mRole)
143
0
      mParent = grandParent;
144
0
  }
145
0
}
146
147
Accessible*
148
AccGroupInfo::FirstItemOf(const Accessible* aContainer)
149
0
{
150
0
  // ARIA tree can be arranged by ARIA groups case #1 (previous sibling of a
151
0
  // group is a parent) or by aria-level.
152
0
  a11y::role containerRole = aContainer->Role();
153
0
  Accessible* item = aContainer->NextSibling();
154
0
  if (item) {
155
0
    if (containerRole == roles::OUTLINEITEM && item->Role() == roles::GROUPING)
156
0
      item = item->FirstChild();
157
0
158
0
    if (item) {
159
0
      AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
160
0
      if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
161
0
        return item;
162
0
    }
163
0
  }
164
0
165
0
  // ARIA list and tree can be arranged by ARIA groups case #2 (group is
166
0
  // a child of an item).
167
0
  item = aContainer->LastChild();
168
0
  if (!item)
169
0
    return nullptr;
170
0
171
0
  if (item->Role() == roles::GROUPING &&
172
0
      (containerRole == roles::LISTITEM || containerRole == roles::OUTLINEITEM)) {
173
0
    item = item->FirstChild();
174
0
    if (item) {
175
0
      AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
176
0
      if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
177
0
        return item;
178
0
    }
179
0
  }
180
0
181
0
  // Otherwise, it can be a direct child if the container is a list or tree.
182
0
  item = aContainer->FirstChild();
183
0
  if (ShouldReportRelations(item->Role(), containerRole))
184
0
    return item;
185
0
186
0
  return nullptr;
187
0
}
188
189
uint32_t
190
AccGroupInfo::TotalItemCount(Accessible* aContainer, bool* aIsHierarchical)
191
0
{
192
0
  uint32_t itemCount = 0;
193
0
  switch (aContainer->Role()) {
194
0
    case roles::TABLE:
195
0
      if (nsCoreUtils::GetUIntAttr(aContainer->GetContent(),
196
0
                                   nsGkAtoms::aria_rowcount,
197
0
                                   (int32_t*)&itemCount)) {
198
0
        break;
199
0
      }
200
0
201
0
      if (TableAccessible* tableAcc = aContainer->AsTable()) {
202
0
        return tableAcc->RowCount();
203
0
      }
204
0
205
0
      break;
206
0
    case roles::ROW:
207
0
      if (Accessible* table = nsAccUtils::TableFor(aContainer)) {
208
0
        if (nsCoreUtils::GetUIntAttr(table->GetContent(),
209
0
                                     nsGkAtoms::aria_colcount,
210
0
                                     (int32_t*)&itemCount)) {
211
0
          break;
212
0
        }
213
0
214
0
        if (TableAccessible* tableAcc = table->AsTable()) {
215
0
          return tableAcc->ColCount();
216
0
        }
217
0
      }
218
0
219
0
      break;
220
0
    case roles::OUTLINE:
221
0
    case roles::LIST:
222
0
    case roles::MENUBAR:
223
0
    case roles::MENUPOPUP:
224
0
    case roles::COMBOBOX:
225
0
    case roles::GROUPING:
226
0
    case roles::TREE_TABLE:
227
0
    case roles::COMBOBOX_LIST:
228
0
    case roles::LISTBOX:
229
0
    case roles::DEFINITION_LIST:
230
0
    case roles::EDITCOMBOBOX:
231
0
    case roles::RADIO_GROUP:
232
0
    case roles::PAGETABLIST: {
233
0
      Accessible* childItem = AccGroupInfo::FirstItemOf(aContainer);
234
0
      if (!childItem) {
235
0
        childItem = aContainer->FirstChild();
236
0
        if (childItem && childItem->IsTextLeaf()) {
237
0
          // First child can be a text leaf, check its sibling for an item.
238
0
          childItem = childItem->NextSibling();
239
0
        }
240
0
      }
241
0
242
0
      if (childItem) {
243
0
        GroupPos groupPos = childItem->GroupPosition();
244
0
        itemCount = groupPos.setSize;
245
0
        if (groupPos.level && aIsHierarchical) {
246
0
          *aIsHierarchical = true;
247
0
        }
248
0
      }
249
0
      break;
250
0
    }
251
0
    default:
252
0
      break;
253
0
  }
254
0
255
0
  return itemCount;
256
0
}
257
258
Accessible*
259
AccGroupInfo::NextItemTo(Accessible* aItem)
260
0
{
261
0
  AccGroupInfo* groupInfo = aItem->GetGroupInfo();
262
0
  if (!groupInfo)
263
0
    return nullptr;
264
0
265
0
  // If the item in middle of the group then search next item in siblings.
266
0
  if (groupInfo->PosInSet() >= groupInfo->SetSize())
267
0
    return nullptr;
268
0
269
0
  Accessible* parent = aItem->Parent();
270
0
  uint32_t childCount = parent->ChildCount();
271
0
  for (uint32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) {
272
0
    Accessible* nextItem = parent->GetChildAt(idx);
273
0
    AccGroupInfo* nextGroupInfo = nextItem->GetGroupInfo();
274
0
    if (nextGroupInfo &&
275
0
        nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) {
276
0
      return nextItem;
277
0
    }
278
0
  }
279
0
280
0
  MOZ_ASSERT_UNREACHABLE("Item in the middle of the group but there's no next item!");
281
0
  return nullptr;
282
0
}
283
284
bool
285
AccGroupInfo::ShouldReportRelations(role aRole, role aParentRole)
286
0
{
287
0
  // We only want to report hierarchy-based node relations for items in tree or
288
0
  // list form.  ARIA level/owns relations are always reported.
289
0
  if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM)
290
0
    return true;
291
0
  if (aParentRole == roles::TREE_TABLE && aRole == roles::ROW)
292
0
    return true;
293
0
  if (aParentRole == roles::LIST && aRole == roles::LISTITEM)
294
0
    return true;
295
0
296
0
  return false;
297
0
}