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 "AccIterator.h"
7 #include "AccGroupInfo.h"
8 #include "DocAccessible-inl.h"
9 #include "LocalAccessible-inl.h"
10 #include "XULTreeAccessible.h"
12 #include "mozilla/a11y/DocAccessibleParent.h"
13 #include "mozilla/dom/DocumentOrShadowRoot.h"
14 #include "mozilla/dom/HTMLLabelElement.h"
16 using namespace mozilla
;
17 using namespace mozilla::a11y
;
19 ////////////////////////////////////////////////////////////////////////////////
21 ////////////////////////////////////////////////////////////////////////////////
23 AccIterator::AccIterator(const LocalAccessible
* aAccessible
,
24 filters::FilterFuncPtr aFilterFunc
)
25 : mFilterFunc(aFilterFunc
) {
26 mState
= new IteratorState(aAccessible
);
29 AccIterator::~AccIterator() {
31 IteratorState
* tmp
= mState
;
32 mState
= tmp
->mParentState
;
37 LocalAccessible
* AccIterator::Next() {
39 LocalAccessible
* child
= mState
->mParent
->LocalChildAt(mState
->mIndex
++);
41 IteratorState
* tmp
= mState
;
42 mState
= mState
->mParentState
;
48 uint32_t result
= mFilterFunc(child
);
49 if (result
& filters::eMatch
) return child
;
51 if (!(result
& filters::eSkipSubtree
)) {
52 IteratorState
* childState
= new IteratorState(child
, mState
);
60 ////////////////////////////////////////////////////////////////////////////////
61 // nsAccIterator::IteratorState
63 AccIterator::IteratorState::IteratorState(const LocalAccessible
* aParent
,
64 IteratorState
* mParentState
)
65 : mParent(aParent
), mIndex(0), mParentState(mParentState
) {}
67 ////////////////////////////////////////////////////////////////////////////////
69 ////////////////////////////////////////////////////////////////////////////////
71 RelatedAccIterator::RelatedAccIterator(DocAccessible
* aDocument
,
72 nsIContent
* aDependentContent
,
74 : mDocument(aDocument
),
75 mDependentContent(aDependentContent
),
79 mIsWalkingDependentElements(false) {
81 if (aDependentContent
->IsElement() &&
82 aDependentContent
->AsElement()->GetAttr(nsGkAtoms::id
, id
)) {
83 mProviders
= mDocument
->GetRelProviders(aDependentContent
->AsElement(), id
);
87 LocalAccessible
* RelatedAccIterator::Next() {
88 if (!mProviders
|| mIndex
== mProviders
->Length()) {
89 if (mIsWalkingDependentElements
) {
90 // We've walked both dependent ids and dependent elements, so there are
94 // We've returned all dependent ids, but there might be dependent elements
95 // too. Walk those next.
96 mIsWalkingDependentElements
= true;
99 mDocument
->mDependentElementsMap
.Lookup(mDependentContent
)) {
100 mProviders
= &providers
.Data();
102 mProviders
= nullptr;
107 while (mIndex
< mProviders
->Length()) {
108 const auto& provider
= (*mProviders
)[mIndex
++];
110 // Return related accessible for the given attribute.
111 if (mRelAttr
&& provider
->mRelAttr
!= mRelAttr
) {
114 // If we're walking elements (not ids), the explicitly set attr-element
115 // `mDependentContent` must be a descendant of any of the refering element
116 // `mProvider->mContent`'s shadow-including ancestors.
117 if (mIsWalkingDependentElements
&&
118 !nsCoreUtils::IsDescendantOfAnyShadowIncludingAncestor(
119 mDependentContent
, provider
->mContent
)) {
122 LocalAccessible
* related
= mDocument
->GetAccessible(provider
->mContent
);
127 // If the document content is pointed by relation then return the
129 if (provider
->mContent
== mDocument
->GetContent()) {
134 // We exhausted mProviders without returning anything.
135 if (!mIsWalkingDependentElements
) {
136 // Call this function again to start walking the dependent elements.
142 ////////////////////////////////////////////////////////////////////////////////
144 ////////////////////////////////////////////////////////////////////////////////
146 HTMLLabelIterator::HTMLLabelIterator(DocAccessible
* aDocument
,
147 const LocalAccessible
* aAccessible
,
149 : mRelIter(aDocument
, aAccessible
->GetContent(), nsGkAtoms::_for
),
151 mLabelFilter(aFilter
) {}
153 bool HTMLLabelIterator::IsLabel(LocalAccessible
* aLabel
) {
154 dom::HTMLLabelElement
* labelEl
=
155 dom::HTMLLabelElement::FromNode(aLabel
->GetContent());
156 return labelEl
&& labelEl
->GetControl() == mAcc
->GetContent();
159 LocalAccessible
* HTMLLabelIterator::Next() {
160 // Get either <label for="[id]"> element which explicitly points to given
161 // element, or <label> ancestor which implicitly point to it.
162 LocalAccessible
* label
= nullptr;
163 while ((label
= mRelIter
.Next())) {
164 if (IsLabel(label
)) {
169 // Ignore ancestor label on not widget accessible.
170 if (mLabelFilter
== eSkipAncestorLabel
|| !mAcc
->IsWidget()) return nullptr;
172 // Go up tree to get a name of ancestor label if there is one (an ancestor
173 // <label> implicitly points to us). Don't go up farther than form or
175 LocalAccessible
* walkUp
= mAcc
->LocalParent();
176 while (walkUp
&& !walkUp
->IsDoc()) {
177 nsIContent
* walkUpEl
= walkUp
->GetContent();
178 if (IsLabel(walkUp
) && !walkUpEl
->AsElement()->HasAttr(nsGkAtoms::_for
)) {
179 mLabelFilter
= eSkipAncestorLabel
; // prevent infinite loop
183 if (walkUpEl
->IsHTMLElement(nsGkAtoms::form
)) break;
185 walkUp
= walkUp
->LocalParent();
191 ////////////////////////////////////////////////////////////////////////////////
192 // HTMLOutputIterator
193 ////////////////////////////////////////////////////////////////////////////////
195 HTMLOutputIterator::HTMLOutputIterator(DocAccessible
* aDocument
,
196 nsIContent
* aElement
)
197 : mRelIter(aDocument
, aElement
, nsGkAtoms::_for
) {}
199 LocalAccessible
* HTMLOutputIterator::Next() {
200 LocalAccessible
* output
= nullptr;
201 while ((output
= mRelIter
.Next())) {
202 if (output
->GetContent()->IsHTMLElement(nsGkAtoms::output
)) return output
;
208 ////////////////////////////////////////////////////////////////////////////////
210 ////////////////////////////////////////////////////////////////////////////////
212 XULLabelIterator::XULLabelIterator(DocAccessible
* aDocument
,
213 nsIContent
* aElement
)
214 : mRelIter(aDocument
, aElement
, nsGkAtoms::control
) {}
216 LocalAccessible
* XULLabelIterator::Next() {
217 LocalAccessible
* label
= nullptr;
218 while ((label
= mRelIter
.Next())) {
219 if (label
->GetContent()->IsXULElement(nsGkAtoms::label
)) return label
;
225 ////////////////////////////////////////////////////////////////////////////////
226 // XULDescriptionIterator
227 ////////////////////////////////////////////////////////////////////////////////
229 XULDescriptionIterator::XULDescriptionIterator(DocAccessible
* aDocument
,
230 nsIContent
* aElement
)
231 : mRelIter(aDocument
, aElement
, nsGkAtoms::control
) {}
233 LocalAccessible
* XULDescriptionIterator::Next() {
234 LocalAccessible
* descr
= nullptr;
235 while ((descr
= mRelIter
.Next())) {
236 if (descr
->GetContent()->IsXULElement(nsGkAtoms::description
)) return descr
;
242 ////////////////////////////////////////////////////////////////////////////////
244 ////////////////////////////////////////////////////////////////////////////////
246 IDRefsIterator::IDRefsIterator(DocAccessible
* aDoc
, nsIContent
* aContent
,
248 : mContent(aContent
), mDoc(aDoc
), mCurrIdx(0) {
249 if (mContent
->IsElement()) {
250 mContent
->AsElement()->GetAttr(aIDRefsAttr
, mIDs
);
254 const nsDependentSubstring
IDRefsIterator::NextID() {
255 for (; mCurrIdx
< mIDs
.Length(); mCurrIdx
++) {
256 if (!NS_IsAsciiWhitespace(mIDs
[mCurrIdx
])) break;
259 if (mCurrIdx
>= mIDs
.Length()) return nsDependentSubstring();
261 nsAString::index_type idStartIdx
= mCurrIdx
;
262 while (++mCurrIdx
< mIDs
.Length()) {
263 if (NS_IsAsciiWhitespace(mIDs
[mCurrIdx
])) break;
266 return Substring(mIDs
, idStartIdx
, mCurrIdx
++ - idStartIdx
);
269 nsIContent
* IDRefsIterator::NextElem() {
271 const nsDependentSubstring id
= NextID();
272 if (id
.IsEmpty()) break;
274 nsIContent
* refContent
= GetElem(id
);
275 if (refContent
) return refContent
;
281 dom::Element
* IDRefsIterator::GetElem(nsIContent
* aContent
,
282 const nsAString
& aID
) {
283 // Get elements in DOM tree by ID attribute if this is an explicit content.
284 // In case of bound element check its anonymous subtree.
285 if (!aContent
->IsInNativeAnonymousSubtree()) {
286 dom::DocumentOrShadowRoot
* docOrShadowRoot
=
287 aContent
->GetUncomposedDocOrConnectedShadowRoot();
288 if (docOrShadowRoot
) {
289 dom::Element
* refElm
= docOrShadowRoot
->GetElementById(aID
);
298 dom::Element
* IDRefsIterator::GetElem(const nsDependentSubstring
& aID
) {
299 return GetElem(mContent
, aID
);
302 LocalAccessible
* IDRefsIterator::Next() {
303 nsIContent
* nextEl
= nullptr;
304 while ((nextEl
= NextElem())) {
305 LocalAccessible
* acc
= mDoc
->GetAccessible(nextEl
);
313 ////////////////////////////////////////////////////////////////////////////////
315 ////////////////////////////////////////////////////////////////////////////////
317 Accessible
* SingleAccIterator::Next() {
318 Accessible
* nextAcc
= mAcc
;
324 MOZ_ASSERT(!nextAcc
->IsLocal() || !nextAcc
->AsLocal()->IsDefunct(),
325 "Iterator references defunct accessible?");
329 ////////////////////////////////////////////////////////////////////////////////
331 ////////////////////////////////////////////////////////////////////////////////
333 Accessible
* ItemIterator::Next() {
335 mAnchor
= AccGroupInfo::FirstItemOf(mContainer
);
336 mContainer
= nullptr;
341 mAnchor
= AccGroupInfo::NextItemTo(mAnchor
);
347 ////////////////////////////////////////////////////////////////////////////////
348 // XULTreeItemIterator
349 ////////////////////////////////////////////////////////////////////////////////
351 XULTreeItemIterator::XULTreeItemIterator(const XULTreeAccessible
* aXULTree
,
352 nsITreeView
* aTreeView
,
354 : mXULTree(aXULTree
),
355 mTreeView(aTreeView
),
358 mCurrRowIdx(aRowIdx
+ 1) {
359 mTreeView
->GetRowCount(&mRowCount
);
360 if (aRowIdx
!= -1) mTreeView
->GetLevel(aRowIdx
, &mContainerLevel
);
363 LocalAccessible
* XULTreeItemIterator::Next() {
364 while (mCurrRowIdx
< mRowCount
) {
366 mTreeView
->GetLevel(mCurrRowIdx
, &level
);
368 if (level
== mContainerLevel
+ 1) {
369 return mXULTree
->GetTreeItemAccessible(mCurrRowIdx
++);
372 if (level
<= mContainerLevel
) { // got level up
373 mCurrRowIdx
= mRowCount
;
383 ////////////////////////////////////////////////////////////////////////////////
385 ////////////////////////////////////////////////////////////////////////////////
387 Accessible
* RemoteAccIterator::Next() {
388 while (mIndex
< mIds
.Length()) {
389 uint64_t id
= mIds
[mIndex
++];
390 Accessible
* acc
= mDoc
->GetAccessible(id
);