Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / accessible / base / AccGroupInfo.cpp
blobf26df1d8b695be0a178781784b44cec26623e179
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/. */
5 #include "AccGroupInfo.h"
6 #include "nsAccUtils.h"
8 #include "Role.h"
9 #include "States.h"
11 using namespace mozilla::a11y;
13 AccGroupInfo::AccGroupInfo(Accessible* aItem, role aRole) :
14 mPosInSet(0), mSetSize(0), mParent(nullptr), mItem(aItem), mRole(aRole)
16 MOZ_COUNT_CTOR(AccGroupInfo);
17 Update();
20 void
21 AccGroupInfo::Update()
23 Accessible* parent = mItem->Parent();
24 if (!parent)
25 return;
27 int32_t indexInParent = mItem->IndexInParent();
28 uint32_t siblingCount = parent->ChildCount();
29 if (indexInParent == -1 ||
30 indexInParent >= static_cast<int32_t>(siblingCount)) {
31 NS_ERROR("Wrong index in parent! Tree invalidation problem.");
32 return;
35 int32_t level = nsAccUtils::GetARIAOrDefaultLevel(mItem);
37 // Compute position in set.
38 mPosInSet = 1;
39 for (int32_t idx = indexInParent - 1; idx >= 0 ; idx--) {
40 Accessible* sibling = parent->GetChildAt(idx);
41 roles::Role siblingRole = sibling->Role();
43 // If the sibling is separator then the group is ended.
44 if (siblingRole == roles::SEPARATOR)
45 break;
47 // If sibling is not visible and hasn't the same base role.
48 if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE)
49 continue;
51 // Check if it's hierarchical flatten structure, i.e. if the sibling
52 // level is lesser than this one then group is ended, if the sibling level
53 // is greater than this one then the group is split by some child elements
54 // (group will be continued).
55 int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
56 if (siblingLevel < level) {
57 mParent = sibling;
58 break;
61 // Skip subset.
62 if (siblingLevel > level)
63 continue;
65 // If the previous item in the group has calculated group information then
66 // build group information for this item based on found one.
67 if (sibling->mGroupInfo) {
68 mPosInSet += sibling->mGroupInfo->mPosInSet;
69 mParent = sibling->mGroupInfo->mParent;
70 mSetSize = sibling->mGroupInfo->mSetSize;
71 return;
74 mPosInSet++;
77 // Compute set size.
78 mSetSize = mPosInSet;
80 for (uint32_t idx = indexInParent + 1; idx < siblingCount; idx++) {
81 Accessible* sibling = parent->GetChildAt(idx);
83 roles::Role siblingRole = sibling->Role();
85 // If the sibling is separator then the group is ended.
86 if (siblingRole == roles::SEPARATOR)
87 break;
89 // If sibling is visible and has the same base role
90 if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE)
91 continue;
93 // and check if it's hierarchical flatten structure.
94 int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
95 if (siblingLevel < level)
96 break;
98 // Skip subset.
99 if (siblingLevel > level)
100 continue;
102 // If the next item in the group has calculated group information then
103 // build group information for this item based on found one.
104 if (sibling->mGroupInfo) {
105 mParent = sibling->mGroupInfo->mParent;
106 mSetSize = sibling->mGroupInfo->mSetSize;
107 return;
110 mSetSize++;
113 if (mParent)
114 return;
116 roles::Role parentRole = parent->Role();
117 if (ShouldReportRelations(mRole, parentRole))
118 mParent = parent;
120 // ARIA tree and list can be arranged by using ARIA groups to organize levels.
121 if (parentRole != roles::GROUPING)
122 return;
124 // Way #1 for ARIA tree (not ARIA treegrid): previous sibling of a group is a
125 // parent. In other words the parent of the tree item will be a group and
126 // the previous tree item of the group is a conceptual parent of the tree
127 // item.
128 if (mRole == roles::OUTLINEITEM) {
129 Accessible* parentPrevSibling = parent->PrevSibling();
130 if (parentPrevSibling && parentPrevSibling->Role() == mRole) {
131 mParent = parentPrevSibling;
132 return;
136 // Way #2 for ARIA list and tree: group is a child of an item. In other words
137 // the parent of the item will be a group and containing item of the group is
138 // a conceptual parent of the item.
139 if (mRole == roles::LISTITEM || mRole == roles::OUTLINEITEM) {
140 Accessible* grandParent = parent->Parent();
141 if (grandParent && grandParent->Role() == mRole)
142 mParent = grandParent;
146 Accessible*
147 AccGroupInfo::FirstItemOf(Accessible* aContainer)
149 // ARIA tree can be arranged by ARIA groups case #1 (previous sibling of a
150 // group is a parent) or by aria-level.
151 a11y::role containerRole = aContainer->Role();
152 Accessible* item = aContainer->NextSibling();
153 if (item) {
154 if (containerRole == roles::OUTLINEITEM && item->Role() == roles::GROUPING)
155 item = item->FirstChild();
157 if (item) {
158 AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
159 if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
160 return item;
164 // ARIA list and tree can be arranged by ARIA groups case #2 (group is
165 // a child of an item).
166 item = aContainer->LastChild();
167 if (!item)
168 return nullptr;
170 if (item->Role() == roles::GROUPING &&
171 (containerRole == roles::LISTITEM || containerRole == roles::OUTLINEITEM)) {
172 item = item->FirstChild();
173 if (item) {
174 AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
175 if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
176 return item;
180 // Otherwise, it can be a direct child if the container is a list or tree.
181 item = aContainer->FirstChild();
182 if (ShouldReportRelations(item->Role(), containerRole))
183 return item;
185 return nullptr;
188 Accessible*
189 AccGroupInfo::NextItemTo(Accessible* aItem)
191 AccGroupInfo* groupInfo = aItem->GetGroupInfo();
192 if (!groupInfo)
193 return nullptr;
195 // If the item in middle of the group then search next item in siblings.
196 if (groupInfo->PosInSet() >= groupInfo->SetSize())
197 return nullptr;
199 Accessible* parent = aItem->Parent();
200 uint32_t childCount = parent->ChildCount();
201 for (uint32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) {
202 Accessible* nextItem = parent->GetChildAt(idx);
203 AccGroupInfo* nextGroupInfo = nextItem->GetGroupInfo();
204 if (nextGroupInfo &&
205 nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) {
206 return nextItem;
210 NS_NOTREACHED("Item in the middle of the group but there's no next item!");
211 return nullptr;
214 bool
215 AccGroupInfo::ShouldReportRelations(role aRole, role aParentRole)
217 // We only want to report hierarchy-based node relations for items in tree or
218 // list form. ARIA level/owns relations are always reported.
219 if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM)
220 return true;
221 if (aParentRole == roles::TREE_TABLE && aRole == roles::ROW)
222 return true;
223 if (aParentRole == roles::LIST && aRole == roles::LISTITEM)
224 return true;
226 return false;