Line data Source code
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 :
8 : #include "Role.h"
9 : #include "States.h"
10 :
11 : using namespace mozilla::a11y;
12 :
13 0 : AccGroupInfo::AccGroupInfo(Accessible* aItem, role aRole) :
14 0 : mPosInSet(0), mSetSize(0), mParent(nullptr), mItem(aItem), mRole(aRole)
15 : {
16 0 : MOZ_COUNT_CTOR(AccGroupInfo);
17 0 : Update();
18 0 : }
19 :
20 : void
21 0 : AccGroupInfo::Update()
22 : {
23 0 : Accessible* parent = mItem->Parent();
24 0 : if (!parent)
25 0 : return;
26 :
27 0 : int32_t indexInParent = mItem->IndexInParent();
28 0 : uint32_t siblingCount = parent->ChildCount();
29 0 : if (indexInParent == -1 ||
30 0 : indexInParent >= static_cast<int32_t>(siblingCount)) {
31 0 : NS_ERROR("Wrong index in parent! Tree invalidation problem.");
32 0 : return;
33 : }
34 :
35 0 : int32_t level = nsAccUtils::GetARIAOrDefaultLevel(mItem);
36 :
37 : // Compute position in set.
38 0 : mPosInSet = 1;
39 0 : for (int32_t idx = indexInParent - 1; idx >= 0 ; idx--) {
40 0 : Accessible* sibling = parent->GetChildAt(idx);
41 0 : roles::Role siblingRole = sibling->Role();
42 :
43 : // If the sibling is separator then the group is ended.
44 0 : if (siblingRole == roles::SEPARATOR)
45 0 : break;
46 :
47 : // If sibling is not visible and hasn't the same base role.
48 0 : if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE)
49 0 : continue;
50 :
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 0 : int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
56 0 : if (siblingLevel < level) {
57 0 : mParent = sibling;
58 0 : break;
59 : }
60 :
61 : // Skip subset.
62 0 : if (siblingLevel > level)
63 0 : continue;
64 :
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 0 : if (sibling->mBits.groupInfo) {
68 0 : mPosInSet += sibling->mBits.groupInfo->mPosInSet;
69 0 : mParent = sibling->mBits.groupInfo->mParent;
70 0 : mSetSize = sibling->mBits.groupInfo->mSetSize;
71 0 : return;
72 : }
73 :
74 0 : mPosInSet++;
75 : }
76 :
77 : // Compute set size.
78 0 : mSetSize = mPosInSet;
79 :
80 0 : for (uint32_t idx = indexInParent + 1; idx < siblingCount; idx++) {
81 0 : Accessible* sibling = parent->GetChildAt(idx);
82 :
83 0 : roles::Role siblingRole = sibling->Role();
84 :
85 : // If the sibling is separator then the group is ended.
86 0 : if (siblingRole == roles::SEPARATOR)
87 0 : break;
88 :
89 : // If sibling is visible and has the same base role
90 0 : if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE)
91 0 : continue;
92 :
93 : // and check if it's hierarchical flatten structure.
94 0 : int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
95 0 : if (siblingLevel < level)
96 0 : break;
97 :
98 : // Skip subset.
99 0 : if (siblingLevel > level)
100 0 : continue;
101 :
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 0 : if (sibling->mBits.groupInfo) {
105 0 : mParent = sibling->mBits.groupInfo->mParent;
106 0 : mSetSize = sibling->mBits.groupInfo->mSetSize;
107 0 : return;
108 : }
109 :
110 0 : mSetSize++;
111 : }
112 :
113 0 : if (mParent)
114 0 : return;
115 :
116 0 : roles::Role parentRole = parent->Role();
117 0 : if (ShouldReportRelations(mRole, parentRole))
118 0 : mParent = parent;
119 :
120 : // ARIA tree and list can be arranged by using ARIA groups to organize levels.
121 0 : if (parentRole != roles::GROUPING)
122 0 : return;
123 :
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 0 : if (mRole == roles::OUTLINEITEM) {
129 0 : Accessible* parentPrevSibling = parent->PrevSibling();
130 0 : if (parentPrevSibling && parentPrevSibling->Role() == mRole) {
131 0 : mParent = parentPrevSibling;
132 0 : return;
133 : }
134 : }
135 :
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 0 : if (mRole == roles::LISTITEM || mRole == roles::OUTLINEITEM) {
140 0 : Accessible* grandParent = parent->Parent();
141 0 : if (grandParent && grandParent->Role() == mRole)
142 0 : mParent = grandParent;
143 : }
144 : }
145 :
146 : Accessible*
147 0 : AccGroupInfo::FirstItemOf(Accessible* aContainer)
148 : {
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 0 : a11y::role containerRole = aContainer->Role();
152 0 : Accessible* item = aContainer->NextSibling();
153 0 : if (item) {
154 0 : if (containerRole == roles::OUTLINEITEM && item->Role() == roles::GROUPING)
155 0 : item = item->FirstChild();
156 :
157 0 : if (item) {
158 0 : AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
159 0 : if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
160 0 : return item;
161 : }
162 : }
163 :
164 : // ARIA list and tree can be arranged by ARIA groups case #2 (group is
165 : // a child of an item).
166 0 : item = aContainer->LastChild();
167 0 : if (!item)
168 0 : return nullptr;
169 :
170 0 : if (item->Role() == roles::GROUPING &&
171 0 : (containerRole == roles::LISTITEM || containerRole == roles::OUTLINEITEM)) {
172 0 : item = item->FirstChild();
173 0 : if (item) {
174 0 : AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
175 0 : if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
176 0 : return item;
177 : }
178 : }
179 :
180 : // Otherwise, it can be a direct child if the container is a list or tree.
181 0 : item = aContainer->FirstChild();
182 0 : if (ShouldReportRelations(item->Role(), containerRole))
183 0 : return item;
184 :
185 0 : return nullptr;
186 : }
187 :
188 : Accessible*
189 0 : AccGroupInfo::NextItemTo(Accessible* aItem)
190 : {
191 0 : AccGroupInfo* groupInfo = aItem->GetGroupInfo();
192 0 : if (!groupInfo)
193 0 : return nullptr;
194 :
195 : // If the item in middle of the group then search next item in siblings.
196 0 : if (groupInfo->PosInSet() >= groupInfo->SetSize())
197 0 : return nullptr;
198 :
199 0 : Accessible* parent = aItem->Parent();
200 0 : uint32_t childCount = parent->ChildCount();
201 0 : for (uint32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) {
202 0 : Accessible* nextItem = parent->GetChildAt(idx);
203 0 : AccGroupInfo* nextGroupInfo = nextItem->GetGroupInfo();
204 0 : if (nextGroupInfo &&
205 0 : nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) {
206 0 : return nextItem;
207 : }
208 : }
209 :
210 0 : NS_NOTREACHED("Item in the middle of the group but there's no next item!");
211 0 : return nullptr;
212 : }
213 :
214 : bool
215 0 : AccGroupInfo::ShouldReportRelations(role aRole, role aParentRole)
216 : {
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 0 : if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM)
220 0 : return true;
221 0 : if (aParentRole == roles::TREE_TABLE && aRole == roles::ROW)
222 0 : return true;
223 0 : if (aParentRole == roles::LIST && aRole == roles::LISTITEM)
224 0 : return true;
225 :
226 0 : return false;
227 : }
|