Bug 1889091 - Part 4: Remove extra stack pointer move. r=jandem
[gecko.git] / accessible / base / AccIterator.cpp
blobd28d5fcbe962c2d9e82cdbeea9d1f6e02c43d4b8
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 ////////////////////////////////////////////////////////////////////////////////
20 // AccIterator
21 ////////////////////////////////////////////////////////////////////////////////
23 AccIterator::AccIterator(const LocalAccessible* aAccessible,
24 filters::FilterFuncPtr aFilterFunc)
25 : mFilterFunc(aFilterFunc) {
26 mState = new IteratorState(aAccessible);
29 AccIterator::~AccIterator() {
30 while (mState) {
31 IteratorState* tmp = mState;
32 mState = tmp->mParentState;
33 delete tmp;
37 LocalAccessible* AccIterator::Next() {
38 while (mState) {
39 LocalAccessible* child = mState->mParent->LocalChildAt(mState->mIndex++);
40 if (!child) {
41 IteratorState* tmp = mState;
42 mState = mState->mParentState;
43 delete tmp;
45 continue;
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);
53 mState = childState;
57 return nullptr;
60 ////////////////////////////////////////////////////////////////////////////////
61 // nsAccIterator::IteratorState
63 AccIterator::IteratorState::IteratorState(const LocalAccessible* aParent,
64 IteratorState* mParentState)
65 : mParent(aParent), mIndex(0), mParentState(mParentState) {}
67 ////////////////////////////////////////////////////////////////////////////////
68 // RelatedAccIterator
69 ////////////////////////////////////////////////////////////////////////////////
71 RelatedAccIterator::RelatedAccIterator(DocAccessible* aDocument,
72 nsIContent* aDependentContent,
73 nsAtom* aRelAttr)
74 : mDocument(aDocument),
75 mDependentContent(aDependentContent),
76 mRelAttr(aRelAttr),
77 mProviders(nullptr),
78 mIndex(0),
79 mIsWalkingDependentElements(false) {
80 nsAutoString id;
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
91 // no more targets.
92 return nullptr;
94 // We've returned all dependent ids, but there might be dependent elements
95 // too. Walk those next.
96 mIsWalkingDependentElements = true;
97 mIndex = 0;
98 if (auto providers =
99 mDocument->mDependentElementsMap.Lookup(mDependentContent)) {
100 mProviders = &providers.Data();
101 } else {
102 mProviders = nullptr;
103 return 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) {
112 continue;
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)) {
120 continue;
122 LocalAccessible* related = mDocument->GetAccessible(provider->mContent);
123 if (related) {
124 return related;
127 // If the document content is pointed by relation then return the
128 // document itself.
129 if (provider->mContent == mDocument->GetContent()) {
130 return mDocument;
134 // We exhausted mProviders without returning anything.
135 if (!mIsWalkingDependentElements) {
136 // Call this function again to start walking the dependent elements.
137 return Next();
139 return nullptr;
142 ////////////////////////////////////////////////////////////////////////////////
143 // HTMLLabelIterator
144 ////////////////////////////////////////////////////////////////////////////////
146 HTMLLabelIterator::HTMLLabelIterator(DocAccessible* aDocument,
147 const LocalAccessible* aAccessible,
148 LabelFilter aFilter)
149 : mRelIter(aDocument, aAccessible->GetContent(), nsGkAtoms::_for),
150 mAcc(aAccessible),
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)) {
165 return 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
174 // document.
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
180 return walkUp;
183 if (walkUpEl->IsHTMLElement(nsGkAtoms::form)) break;
185 walkUp = walkUp->LocalParent();
188 return nullptr;
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;
205 return nullptr;
208 ////////////////////////////////////////////////////////////////////////////////
209 // XULLabelIterator
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;
222 return nullptr;
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;
239 return nullptr;
242 ////////////////////////////////////////////////////////////////////////////////
243 // IDRefsIterator
244 ////////////////////////////////////////////////////////////////////////////////
246 IDRefsIterator::IDRefsIterator(DocAccessible* aDoc, nsIContent* aContent,
247 nsAtom* aIDRefsAttr)
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() {
270 while (true) {
271 const nsDependentSubstring id = NextID();
272 if (id.IsEmpty()) break;
274 nsIContent* refContent = GetElem(id);
275 if (refContent) return refContent;
278 return nullptr;
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);
290 if (refElm) {
291 return refElm;
295 return nullptr;
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);
306 if (acc) {
307 return acc;
310 return nullptr;
313 ////////////////////////////////////////////////////////////////////////////////
314 // SingleAccIterator
315 ////////////////////////////////////////////////////////////////////////////////
317 Accessible* SingleAccIterator::Next() {
318 Accessible* nextAcc = mAcc;
319 mAcc = nullptr;
320 if (!nextAcc) {
321 return nullptr;
324 MOZ_ASSERT(!nextAcc->IsLocal() || !nextAcc->AsLocal()->IsDefunct(),
325 "Iterator references defunct accessible?");
326 return nextAcc;
329 ////////////////////////////////////////////////////////////////////////////////
330 // ItemIterator
331 ////////////////////////////////////////////////////////////////////////////////
333 Accessible* ItemIterator::Next() {
334 if (mContainer) {
335 mAnchor = AccGroupInfo::FirstItemOf(mContainer);
336 mContainer = nullptr;
337 return mAnchor;
340 if (mAnchor) {
341 mAnchor = AccGroupInfo::NextItemTo(mAnchor);
344 return mAnchor;
347 ////////////////////////////////////////////////////////////////////////////////
348 // XULTreeItemIterator
349 ////////////////////////////////////////////////////////////////////////////////
351 XULTreeItemIterator::XULTreeItemIterator(const XULTreeAccessible* aXULTree,
352 nsITreeView* aTreeView,
353 int32_t aRowIdx)
354 : mXULTree(aXULTree),
355 mTreeView(aTreeView),
356 mRowCount(-1),
357 mContainerLevel(-1),
358 mCurrRowIdx(aRowIdx + 1) {
359 mTreeView->GetRowCount(&mRowCount);
360 if (aRowIdx != -1) mTreeView->GetLevel(aRowIdx, &mContainerLevel);
363 LocalAccessible* XULTreeItemIterator::Next() {
364 while (mCurrRowIdx < mRowCount) {
365 int32_t level = 0;
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;
374 break;
377 mCurrRowIdx++;
380 return nullptr;
383 ////////////////////////////////////////////////////////////////////////////////
384 // RemoteAccIterator
385 ////////////////////////////////////////////////////////////////////////////////
387 Accessible* RemoteAccIterator::Next() {
388 while (mIndex < mIds.Length()) {
389 uint64_t id = mIds[mIndex++];
390 Accessible* acc = mDoc->GetAccessible(id);
391 if (acc) {
392 return acc;
395 return nullptr;