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 "XULTreeAccessible.h"
10 #include "nsAccUtils.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
), mRelAttr(aRelAttr
), mProviders(nullptr), mIndex(0) {
76 if (aDependentContent
->IsElement() &&
77 aDependentContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
,
79 mProviders
= mDocument
->GetRelProviders(aDependentContent
->AsElement(), id
);
83 LocalAccessible
* RelatedAccIterator::Next() {
84 if (!mProviders
) return nullptr;
86 while (mIndex
< mProviders
->Length()) {
87 const auto& provider
= (*mProviders
)[mIndex
++];
89 // Return related accessible for the given attribute.
90 if (provider
->mRelAttr
== mRelAttr
) {
91 LocalAccessible
* related
= mDocument
->GetAccessible(provider
->mContent
);
96 // If the document content is pointed by relation then return the
98 if (provider
->mContent
== mDocument
->GetContent()) {
107 ////////////////////////////////////////////////////////////////////////////////
109 ////////////////////////////////////////////////////////////////////////////////
111 HTMLLabelIterator::HTMLLabelIterator(DocAccessible
* aDocument
,
112 const LocalAccessible
* aAccessible
,
114 : mRelIter(aDocument
, aAccessible
->GetContent(), nsGkAtoms::_for
),
116 mLabelFilter(aFilter
) {}
118 bool HTMLLabelIterator::IsLabel(LocalAccessible
* aLabel
) {
119 dom::HTMLLabelElement
* labelEl
=
120 dom::HTMLLabelElement::FromNode(aLabel
->GetContent());
121 return labelEl
&& labelEl
->GetControl() == mAcc
->GetContent();
124 LocalAccessible
* HTMLLabelIterator::Next() {
125 // Get either <label for="[id]"> element which explicitly points to given
126 // element, or <label> ancestor which implicitly point to it.
127 LocalAccessible
* label
= nullptr;
128 while ((label
= mRelIter
.Next())) {
129 if (IsLabel(label
)) {
134 // Ignore ancestor label on not widget accessible.
135 if (mLabelFilter
== eSkipAncestorLabel
|| !mAcc
->IsWidget()) return nullptr;
137 // Go up tree to get a name of ancestor label if there is one (an ancestor
138 // <label> implicitly points to us). Don't go up farther than form or
140 LocalAccessible
* walkUp
= mAcc
->LocalParent();
141 while (walkUp
&& !walkUp
->IsDoc()) {
142 nsIContent
* walkUpEl
= walkUp
->GetContent();
143 if (IsLabel(walkUp
) &&
144 !walkUpEl
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::_for
)) {
145 mLabelFilter
= eSkipAncestorLabel
; // prevent infinite loop
149 if (walkUpEl
->IsHTMLElement(nsGkAtoms::form
)) break;
151 walkUp
= walkUp
->LocalParent();
157 ////////////////////////////////////////////////////////////////////////////////
158 // HTMLOutputIterator
159 ////////////////////////////////////////////////////////////////////////////////
161 HTMLOutputIterator::HTMLOutputIterator(DocAccessible
* aDocument
,
162 nsIContent
* aElement
)
163 : mRelIter(aDocument
, aElement
, nsGkAtoms::_for
) {}
165 LocalAccessible
* HTMLOutputIterator::Next() {
166 LocalAccessible
* output
= nullptr;
167 while ((output
= mRelIter
.Next())) {
168 if (output
->GetContent()->IsHTMLElement(nsGkAtoms::output
)) return output
;
174 ////////////////////////////////////////////////////////////////////////////////
176 ////////////////////////////////////////////////////////////////////////////////
178 XULLabelIterator::XULLabelIterator(DocAccessible
* aDocument
,
179 nsIContent
* aElement
)
180 : mRelIter(aDocument
, aElement
, nsGkAtoms::control
) {}
182 LocalAccessible
* XULLabelIterator::Next() {
183 LocalAccessible
* label
= nullptr;
184 while ((label
= mRelIter
.Next())) {
185 if (label
->GetContent()->IsXULElement(nsGkAtoms::label
)) return label
;
191 ////////////////////////////////////////////////////////////////////////////////
192 // XULDescriptionIterator
193 ////////////////////////////////////////////////////////////////////////////////
195 XULDescriptionIterator::XULDescriptionIterator(DocAccessible
* aDocument
,
196 nsIContent
* aElement
)
197 : mRelIter(aDocument
, aElement
, nsGkAtoms::control
) {}
199 LocalAccessible
* XULDescriptionIterator::Next() {
200 LocalAccessible
* descr
= nullptr;
201 while ((descr
= mRelIter
.Next())) {
202 if (descr
->GetContent()->IsXULElement(nsGkAtoms::description
)) return descr
;
208 ////////////////////////////////////////////////////////////////////////////////
210 ////////////////////////////////////////////////////////////////////////////////
212 IDRefsIterator::IDRefsIterator(DocAccessible
* aDoc
, nsIContent
* aContent
,
214 : mContent(aContent
), mDoc(aDoc
), mCurrIdx(0) {
215 if (mContent
->IsElement()) {
216 mContent
->AsElement()->GetAttr(aIDRefsAttr
, mIDs
);
220 const nsDependentSubstring
IDRefsIterator::NextID() {
221 for (; mCurrIdx
< mIDs
.Length(); mCurrIdx
++) {
222 if (!NS_IsAsciiWhitespace(mIDs
[mCurrIdx
])) break;
225 if (mCurrIdx
>= mIDs
.Length()) return nsDependentSubstring();
227 nsAString::index_type idStartIdx
= mCurrIdx
;
228 while (++mCurrIdx
< mIDs
.Length()) {
229 if (NS_IsAsciiWhitespace(mIDs
[mCurrIdx
])) break;
232 return Substring(mIDs
, idStartIdx
, mCurrIdx
++ - idStartIdx
);
235 nsIContent
* IDRefsIterator::NextElem() {
237 const nsDependentSubstring id
= NextID();
238 if (id
.IsEmpty()) break;
240 nsIContent
* refContent
= GetElem(id
);
241 if (refContent
) return refContent
;
247 dom::Element
* IDRefsIterator::GetElem(nsIContent
* aContent
,
248 const nsAString
& aID
) {
249 // Get elements in DOM tree by ID attribute if this is an explicit content.
250 // In case of bound element check its anonymous subtree.
251 if (!aContent
->IsInNativeAnonymousSubtree()) {
252 dom::DocumentOrShadowRoot
* docOrShadowRoot
=
253 aContent
->GetUncomposedDocOrConnectedShadowRoot();
254 if (docOrShadowRoot
) {
255 dom::Element
* refElm
= docOrShadowRoot
->GetElementById(aID
);
264 dom::Element
* IDRefsIterator::GetElem(const nsDependentSubstring
& aID
) {
265 return GetElem(mContent
, aID
);
268 LocalAccessible
* IDRefsIterator::Next() {
269 nsIContent
* nextEl
= nullptr;
270 while ((nextEl
= NextElem())) {
271 LocalAccessible
* acc
= mDoc
->GetAccessible(nextEl
);
279 ////////////////////////////////////////////////////////////////////////////////
281 ////////////////////////////////////////////////////////////////////////////////
283 Accessible
* SingleAccIterator::Next() {
284 Accessible
* nextAcc
= mAcc
;
290 MOZ_ASSERT(!nextAcc
->IsLocal() || !nextAcc
->AsLocal()->IsDefunct(),
291 "Iterator references defunct accessible?");
295 ////////////////////////////////////////////////////////////////////////////////
297 ////////////////////////////////////////////////////////////////////////////////
299 Accessible
* ItemIterator::Next() {
301 mAnchor
= AccGroupInfo::FirstItemOf(mContainer
);
302 mContainer
= nullptr;
307 mAnchor
= AccGroupInfo::NextItemTo(mAnchor
);
313 ////////////////////////////////////////////////////////////////////////////////
314 // XULTreeItemIterator
315 ////////////////////////////////////////////////////////////////////////////////
317 XULTreeItemIterator::XULTreeItemIterator(const XULTreeAccessible
* aXULTree
,
318 nsITreeView
* aTreeView
,
320 : mXULTree(aXULTree
),
321 mTreeView(aTreeView
),
324 mCurrRowIdx(aRowIdx
+ 1) {
325 mTreeView
->GetRowCount(&mRowCount
);
326 if (aRowIdx
!= -1) mTreeView
->GetLevel(aRowIdx
, &mContainerLevel
);
329 LocalAccessible
* XULTreeItemIterator::Next() {
330 while (mCurrRowIdx
< mRowCount
) {
332 mTreeView
->GetLevel(mCurrRowIdx
, &level
);
334 if (level
== mContainerLevel
+ 1) {
335 return mXULTree
->GetTreeItemAccessible(mCurrRowIdx
++);
338 if (level
<= mContainerLevel
) { // got level up
339 mCurrRowIdx
= mRowCount
;
349 ////////////////////////////////////////////////////////////////////////////////
351 ////////////////////////////////////////////////////////////////////////////////
353 Accessible
* RemoteAccIterator::Next() {
354 while (mIndex
< mIds
.Length()) {
355 uint64_t id
= mIds
[mIndex
++];
356 Accessible
* acc
= mDoc
->GetAccessible(id
);