/src/mozilla-central/accessible/base/AccIterator.cpp
Line | Count | Source (jump to first uncovered line) |
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 "AccIterator.h" |
6 | | |
7 | | #include "AccGroupInfo.h" |
8 | | #ifdef MOZ_XUL |
9 | | #include "XULTreeAccessible.h" |
10 | | #endif |
11 | | |
12 | | #include "mozilla/dom/HTMLLabelElement.h" |
13 | | |
14 | | using namespace mozilla; |
15 | | using namespace mozilla::a11y; |
16 | | |
17 | | //////////////////////////////////////////////////////////////////////////////// |
18 | | // AccIterator |
19 | | //////////////////////////////////////////////////////////////////////////////// |
20 | | |
21 | | AccIterator::AccIterator(const Accessible* aAccessible, |
22 | | filters::FilterFuncPtr aFilterFunc) : |
23 | | mFilterFunc(aFilterFunc) |
24 | 0 | { |
25 | 0 | mState = new IteratorState(aAccessible); |
26 | 0 | } |
27 | | |
28 | | AccIterator::~AccIterator() |
29 | 0 | { |
30 | 0 | while (mState) { |
31 | 0 | IteratorState *tmp = mState; |
32 | 0 | mState = tmp->mParentState; |
33 | 0 | delete tmp; |
34 | 0 | } |
35 | 0 | } |
36 | | |
37 | | Accessible* |
38 | | AccIterator::Next() |
39 | 0 | { |
40 | 0 | while (mState) { |
41 | 0 | Accessible* child = mState->mParent->GetChildAt(mState->mIndex++); |
42 | 0 | if (!child) { |
43 | 0 | IteratorState* tmp = mState; |
44 | 0 | mState = mState->mParentState; |
45 | 0 | delete tmp; |
46 | 0 |
|
47 | 0 | continue; |
48 | 0 | } |
49 | 0 | |
50 | 0 | uint32_t result = mFilterFunc(child); |
51 | 0 | if (result & filters::eMatch) |
52 | 0 | return child; |
53 | 0 | |
54 | 0 | if (!(result & filters::eSkipSubtree)) { |
55 | 0 | IteratorState* childState = new IteratorState(child, mState); |
56 | 0 | mState = childState; |
57 | 0 | } |
58 | 0 | } |
59 | 0 |
|
60 | 0 | return nullptr; |
61 | 0 | } |
62 | | |
63 | | //////////////////////////////////////////////////////////////////////////////// |
64 | | // nsAccIterator::IteratorState |
65 | | |
66 | | AccIterator::IteratorState::IteratorState(const Accessible* aParent, |
67 | | IteratorState *mParentState) : |
68 | | mParent(aParent), mIndex(0), mParentState(mParentState) |
69 | 0 | { |
70 | 0 | } |
71 | | |
72 | | |
73 | | //////////////////////////////////////////////////////////////////////////////// |
74 | | // RelatedAccIterator |
75 | | //////////////////////////////////////////////////////////////////////////////// |
76 | | |
77 | | RelatedAccIterator:: |
78 | | RelatedAccIterator(DocAccessible* aDocument, nsIContent* aDependentContent, |
79 | | nsAtom* aRelAttr) : |
80 | | mDocument(aDocument), mRelAttr(aRelAttr), mProviders(nullptr), |
81 | | mBindingParent(nullptr), mIndex(0) |
82 | 0 | { |
83 | 0 | mBindingParent = aDependentContent->GetBindingParent(); |
84 | 0 | nsAtom* IDAttr = mBindingParent ? |
85 | 0 | nsGkAtoms::anonid : nsGkAtoms::id; |
86 | 0 |
|
87 | 0 | nsAutoString id; |
88 | 0 | if (aDependentContent->IsElement() && |
89 | 0 | aDependentContent->AsElement()->GetAttr(kNameSpaceID_None, IDAttr, id)) |
90 | 0 | mProviders = mDocument->mDependentIDsHash.Get(id); |
91 | 0 | } |
92 | | |
93 | | Accessible* |
94 | | RelatedAccIterator::Next() |
95 | 0 | { |
96 | 0 | if (!mProviders) |
97 | 0 | return nullptr; |
98 | 0 | |
99 | 0 | while (mIndex < mProviders->Length()) { |
100 | 0 | DocAccessible::AttrRelProvider* provider = (*mProviders)[mIndex++]; |
101 | 0 |
|
102 | 0 | // Return related accessible for the given attribute and if the provider |
103 | 0 | // content is in the same binding in the case of XBL usage. |
104 | 0 | if (provider->mRelAttr == mRelAttr) { |
105 | 0 | nsIContent* bindingParent = provider->mContent->GetBindingParent(); |
106 | 0 | bool inScope = mBindingParent == bindingParent || |
107 | 0 | mBindingParent == provider->mContent; |
108 | 0 |
|
109 | 0 | if (inScope) { |
110 | 0 | Accessible* related = mDocument->GetAccessible(provider->mContent); |
111 | 0 | if (related) |
112 | 0 | return related; |
113 | 0 | |
114 | 0 | // If the document content is pointed by relation then return the document |
115 | 0 | // itself. |
116 | 0 | if (provider->mContent == mDocument->GetContent()) |
117 | 0 | return mDocument; |
118 | 0 | } |
119 | 0 | } |
120 | 0 | } |
121 | 0 |
|
122 | 0 | return nullptr; |
123 | 0 | } |
124 | | |
125 | | |
126 | | //////////////////////////////////////////////////////////////////////////////// |
127 | | // HTMLLabelIterator |
128 | | //////////////////////////////////////////////////////////////////////////////// |
129 | | |
130 | | HTMLLabelIterator:: |
131 | | HTMLLabelIterator(DocAccessible* aDocument, const Accessible* aAccessible, |
132 | | LabelFilter aFilter) : |
133 | | mRelIter(aDocument, aAccessible->GetContent(), nsGkAtoms::_for), |
134 | | mAcc(aAccessible), mLabelFilter(aFilter) |
135 | 0 | { |
136 | 0 | } |
137 | | |
138 | | bool |
139 | | HTMLLabelIterator::IsLabel(Accessible* aLabel) |
140 | 0 | { |
141 | 0 | dom::HTMLLabelElement* labelEl = |
142 | 0 | dom::HTMLLabelElement::FromNode(aLabel->GetContent()); |
143 | 0 | return labelEl && labelEl->GetControl() == mAcc->GetContent(); |
144 | 0 | } |
145 | | |
146 | | Accessible* |
147 | | HTMLLabelIterator::Next() |
148 | 0 | { |
149 | 0 | // Get either <label for="[id]"> element which explicitly points to given |
150 | 0 | // element, or <label> ancestor which implicitly point to it. |
151 | 0 | Accessible* label = nullptr; |
152 | 0 | while ((label = mRelIter.Next())) { |
153 | 0 | if (IsLabel(label)) { |
154 | 0 | return label; |
155 | 0 | } |
156 | 0 | } |
157 | 0 |
|
158 | 0 | // Ignore ancestor label on not widget accessible. |
159 | 0 | if (mLabelFilter == eSkipAncestorLabel || !mAcc->IsWidget()) |
160 | 0 | return nullptr; |
161 | 0 | |
162 | 0 | // Go up tree to get a name of ancestor label if there is one (an ancestor |
163 | 0 | // <label> implicitly points to us). Don't go up farther than form or |
164 | 0 | // document. |
165 | 0 | Accessible* walkUp = mAcc->Parent(); |
166 | 0 | while (walkUp && !walkUp->IsDoc()) { |
167 | 0 | nsIContent* walkUpEl = walkUp->GetContent(); |
168 | 0 | if (IsLabel(walkUp) && |
169 | 0 | !walkUpEl->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::_for)) { |
170 | 0 | mLabelFilter = eSkipAncestorLabel; // prevent infinite loop |
171 | 0 | return walkUp; |
172 | 0 | } |
173 | 0 | |
174 | 0 | if (walkUpEl->IsHTMLElement(nsGkAtoms::form)) |
175 | 0 | break; |
176 | 0 | |
177 | 0 | walkUp = walkUp->Parent(); |
178 | 0 | } |
179 | 0 |
|
180 | 0 | return nullptr; |
181 | 0 | } |
182 | | |
183 | | |
184 | | //////////////////////////////////////////////////////////////////////////////// |
185 | | // HTMLOutputIterator |
186 | | //////////////////////////////////////////////////////////////////////////////// |
187 | | |
188 | | HTMLOutputIterator:: |
189 | | HTMLOutputIterator(DocAccessible* aDocument, nsIContent* aElement) : |
190 | | mRelIter(aDocument, aElement, nsGkAtoms::_for) |
191 | 0 | { |
192 | 0 | } |
193 | | |
194 | | Accessible* |
195 | | HTMLOutputIterator::Next() |
196 | 0 | { |
197 | 0 | Accessible* output = nullptr; |
198 | 0 | while ((output = mRelIter.Next())) { |
199 | 0 | if (output->GetContent()->IsHTMLElement(nsGkAtoms::output)) |
200 | 0 | return output; |
201 | 0 | } |
202 | 0 |
|
203 | 0 | return nullptr; |
204 | 0 | } |
205 | | |
206 | | |
207 | | //////////////////////////////////////////////////////////////////////////////// |
208 | | // XULLabelIterator |
209 | | //////////////////////////////////////////////////////////////////////////////// |
210 | | |
211 | | XULLabelIterator:: |
212 | | XULLabelIterator(DocAccessible* aDocument, nsIContent* aElement) : |
213 | | mRelIter(aDocument, aElement, nsGkAtoms::control) |
214 | 0 | { |
215 | 0 | } |
216 | | |
217 | | Accessible* |
218 | | XULLabelIterator::Next() |
219 | 0 | { |
220 | 0 | Accessible* label = nullptr; |
221 | 0 | while ((label = mRelIter.Next())) { |
222 | 0 | if (label->GetContent()->IsXULElement(nsGkAtoms::label)) |
223 | 0 | return label; |
224 | 0 | } |
225 | 0 |
|
226 | 0 | return nullptr; |
227 | 0 | } |
228 | | |
229 | | |
230 | | //////////////////////////////////////////////////////////////////////////////// |
231 | | // XULDescriptionIterator |
232 | | //////////////////////////////////////////////////////////////////////////////// |
233 | | |
234 | | XULDescriptionIterator:: |
235 | | XULDescriptionIterator(DocAccessible* aDocument, nsIContent* aElement) : |
236 | | mRelIter(aDocument, aElement, nsGkAtoms::control) |
237 | 0 | { |
238 | 0 | } |
239 | | |
240 | | Accessible* |
241 | | XULDescriptionIterator::Next() |
242 | 0 | { |
243 | 0 | Accessible* descr = nullptr; |
244 | 0 | while ((descr = mRelIter.Next())) { |
245 | 0 | if (descr->GetContent()->IsXULElement(nsGkAtoms::description)) |
246 | 0 | return descr; |
247 | 0 | } |
248 | 0 |
|
249 | 0 | return nullptr; |
250 | 0 | } |
251 | | |
252 | | //////////////////////////////////////////////////////////////////////////////// |
253 | | // IDRefsIterator |
254 | | //////////////////////////////////////////////////////////////////////////////// |
255 | | |
256 | | IDRefsIterator:: |
257 | | IDRefsIterator(DocAccessible* aDoc, nsIContent* aContent, |
258 | | nsAtom* aIDRefsAttr) : |
259 | | mContent(aContent), mDoc(aDoc), mCurrIdx(0) |
260 | 0 | { |
261 | 0 | if (mContent->IsInUncomposedDoc() && mContent->IsElement()) |
262 | 0 | mContent->AsElement()->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs); |
263 | 0 | } |
264 | | |
265 | | const nsDependentSubstring |
266 | | IDRefsIterator::NextID() |
267 | 0 | { |
268 | 0 | for (; mCurrIdx < mIDs.Length(); mCurrIdx++) { |
269 | 0 | if (!NS_IsAsciiWhitespace(mIDs[mCurrIdx])) |
270 | 0 | break; |
271 | 0 | } |
272 | 0 |
|
273 | 0 | if (mCurrIdx >= mIDs.Length()) |
274 | 0 | return nsDependentSubstring(); |
275 | 0 | |
276 | 0 | nsAString::index_type idStartIdx = mCurrIdx; |
277 | 0 | while (++mCurrIdx < mIDs.Length()) { |
278 | 0 | if (NS_IsAsciiWhitespace(mIDs[mCurrIdx])) |
279 | 0 | break; |
280 | 0 | } |
281 | 0 |
|
282 | 0 | return Substring(mIDs, idStartIdx, mCurrIdx++ - idStartIdx); |
283 | 0 | } |
284 | | |
285 | | nsIContent* |
286 | | IDRefsIterator::NextElem() |
287 | 0 | { |
288 | 0 | while (true) { |
289 | 0 | const nsDependentSubstring id = NextID(); |
290 | 0 | if (id.IsEmpty()) |
291 | 0 | break; |
292 | 0 | |
293 | 0 | nsIContent* refContent = GetElem(id); |
294 | 0 | if (refContent) |
295 | 0 | return refContent; |
296 | 0 | } |
297 | 0 |
|
298 | 0 | return nullptr; |
299 | 0 | } |
300 | | |
301 | | nsIContent* |
302 | | IDRefsIterator::GetElem(const nsDependentSubstring& aID) |
303 | 0 | { |
304 | 0 | // Get elements in DOM tree by ID attribute if this is an explicit content. |
305 | 0 | // In case of bound element check its anonymous subtree. |
306 | 0 | if (!mContent->IsInAnonymousSubtree()) { |
307 | 0 | dom::Element* refElm = mContent->OwnerDoc()->GetElementById(aID); |
308 | 0 | if (refElm || !mContent->GetXBLBinding()) |
309 | 0 | return refElm; |
310 | 0 | } |
311 | 0 | |
312 | 0 | // If content is in anonymous subtree or an element having anonymous subtree |
313 | 0 | // then use "anonid" attribute to get elements in anonymous subtree. |
314 | 0 | |
315 | 0 | // Check inside the binding the element is contained in. |
316 | 0 | nsIContent* bindingParent = mContent->GetBindingParent(); |
317 | 0 | if (bindingParent) { |
318 | 0 | nsIContent* refElm = bindingParent->OwnerDoc()-> |
319 | 0 | GetAnonymousElementByAttribute(bindingParent, nsGkAtoms::anonid, aID); |
320 | 0 |
|
321 | 0 | if (refElm) |
322 | 0 | return refElm; |
323 | 0 | } |
324 | 0 | |
325 | 0 | // Check inside the binding of the element. |
326 | 0 | if (mContent->GetXBLBinding()) { |
327 | 0 | return mContent->OwnerDoc()-> |
328 | 0 | GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid, aID); |
329 | 0 | } |
330 | 0 | |
331 | 0 | return nullptr; |
332 | 0 | } |
333 | | |
334 | | Accessible* |
335 | | IDRefsIterator::Next() |
336 | 0 | { |
337 | 0 | nsIContent* nextEl = nullptr; |
338 | 0 | while ((nextEl = NextElem())) { |
339 | 0 | Accessible* acc = mDoc->GetAccessible(nextEl); |
340 | 0 | if (acc) { |
341 | 0 | return acc; |
342 | 0 | } |
343 | 0 | } |
344 | 0 | return nullptr; |
345 | 0 | } |
346 | | |
347 | | |
348 | | //////////////////////////////////////////////////////////////////////////////// |
349 | | // SingleAccIterator |
350 | | //////////////////////////////////////////////////////////////////////////////// |
351 | | |
352 | | Accessible* |
353 | | SingleAccIterator::Next() |
354 | 0 | { |
355 | 0 | RefPtr<Accessible> nextAcc; |
356 | 0 | mAcc.swap(nextAcc); |
357 | 0 | if (!nextAcc || nextAcc->IsDefunct()) { |
358 | 0 | return nullptr; |
359 | 0 | } |
360 | 0 | return nextAcc; |
361 | 0 | } |
362 | | |
363 | | |
364 | | //////////////////////////////////////////////////////////////////////////////// |
365 | | // ItemIterator |
366 | | //////////////////////////////////////////////////////////////////////////////// |
367 | | |
368 | | Accessible* |
369 | | ItemIterator::Next() |
370 | 0 | { |
371 | 0 | if (mContainer) { |
372 | 0 | mAnchor = AccGroupInfo::FirstItemOf(mContainer); |
373 | 0 | mContainer = nullptr; |
374 | 0 | return mAnchor; |
375 | 0 | } |
376 | 0 | |
377 | 0 | return mAnchor ? (mAnchor = AccGroupInfo::NextItemTo(mAnchor)) : nullptr; |
378 | 0 | } |
379 | | |
380 | | |
381 | | //////////////////////////////////////////////////////////////////////////////// |
382 | | // XULTreeItemIterator |
383 | | //////////////////////////////////////////////////////////////////////////////// |
384 | | |
385 | | XULTreeItemIterator::XULTreeItemIterator(const XULTreeAccessible* aXULTree, |
386 | | nsITreeView* aTreeView, |
387 | | int32_t aRowIdx) : |
388 | | mXULTree(aXULTree), mTreeView(aTreeView), mRowCount(-1), |
389 | | mContainerLevel(-1), mCurrRowIdx(aRowIdx + 1) |
390 | 0 | { |
391 | 0 | mTreeView->GetRowCount(&mRowCount); |
392 | 0 | if (aRowIdx != -1) |
393 | 0 | mTreeView->GetLevel(aRowIdx, &mContainerLevel); |
394 | 0 | } |
395 | | |
396 | | Accessible* |
397 | | XULTreeItemIterator::Next() |
398 | 0 | { |
399 | 0 | while (mCurrRowIdx < mRowCount) { |
400 | 0 | int32_t level = 0; |
401 | 0 | mTreeView->GetLevel(mCurrRowIdx, &level); |
402 | 0 |
|
403 | 0 | if (level == mContainerLevel + 1) |
404 | 0 | return mXULTree->GetTreeItemAccessible(mCurrRowIdx++); |
405 | 0 | |
406 | 0 | if (level <= mContainerLevel) { // got level up |
407 | 0 | mCurrRowIdx = mRowCount; |
408 | 0 | break; |
409 | 0 | } |
410 | 0 | |
411 | 0 | mCurrRowIdx++; |
412 | 0 | } |
413 | 0 |
|
414 | 0 | return nullptr; |
415 | 0 | } |