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"
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
);
21 AccGroupInfo::Update()
23 Accessible
* parent
= mItem
->Parent();
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.");
35 int32_t level
= nsAccUtils::GetARIAOrDefaultLevel(mItem
);
37 // Compute position in set.
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
)
47 // If sibling is not visible and hasn't the same base role.
48 if (BaseRole(siblingRole
) != mRole
|| sibling
->State() & states::INVISIBLE
)
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
) {
62 if (siblingLevel
> level
)
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
;
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
)
89 // If sibling is visible and has the same base role
90 if (BaseRole(siblingRole
) != mRole
|| sibling
->State() & states::INVISIBLE
)
93 // and check if it's hierarchical flatten structure.
94 int32_t siblingLevel
= nsAccUtils::GetARIAOrDefaultLevel(sibling
);
95 if (siblingLevel
< level
)
99 if (siblingLevel
> level
)
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
;
116 roles::Role parentRole
= parent
->Role();
117 if (ShouldReportRelations(mRole
, parentRole
))
120 // ARIA tree and list can be arranged by using ARIA groups to organize levels.
121 if (parentRole
!= roles::GROUPING
)
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
128 if (mRole
== roles::OUTLINEITEM
) {
129 Accessible
* parentPrevSibling
= parent
->PrevSibling();
130 if (parentPrevSibling
&& parentPrevSibling
->Role() == mRole
) {
131 mParent
= parentPrevSibling
;
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
;
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();
154 if (containerRole
== roles::OUTLINEITEM
&& item
->Role() == roles::GROUPING
)
155 item
= item
->FirstChild();
158 AccGroupInfo
* itemGroupInfo
= item
->GetGroupInfo();
159 if (itemGroupInfo
&& itemGroupInfo
->ConceptualParent() == aContainer
)
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();
170 if (item
->Role() == roles::GROUPING
&&
171 (containerRole
== roles::LISTITEM
|| containerRole
== roles::OUTLINEITEM
)) {
172 item
= item
->FirstChild();
174 AccGroupInfo
* itemGroupInfo
= item
->GetGroupInfo();
175 if (itemGroupInfo
&& itemGroupInfo
->ConceptualParent() == aContainer
)
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
))
189 AccGroupInfo::NextItemTo(Accessible
* aItem
)
191 AccGroupInfo
* groupInfo
= aItem
->GetGroupInfo();
195 // If the item in middle of the group then search next item in siblings.
196 if (groupInfo
->PosInSet() >= groupInfo
->SetSize())
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();
205 nextGroupInfo
->ConceptualParent() == groupInfo
->ConceptualParent()) {
210 NS_NOTREACHED("Item in the middle of the group but there's no next item!");
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
)
221 if (aParentRole
== roles::TREE_TABLE
&& aRole
== roles::ROW
)
223 if (aParentRole
== roles::LIST
&& aRole
== roles::LISTITEM
)