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