1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is mozilla.org code.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Alexander Surkov <surkov.alexander@gmail.com> (original author)
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "AccIterator.h"
39 :
40 : #include "nsAccessibilityService.h"
41 : #include "nsAccessible.h"
42 :
43 : #include "mozilla/dom/Element.h"
44 :
45 : ////////////////////////////////////////////////////////////////////////////////
46 : // AccIterator
47 : ////////////////////////////////////////////////////////////////////////////////
48 :
49 0 : AccIterator::AccIterator(nsAccessible *aAccessible,
50 : filters::FilterFuncPtr aFilterFunc,
51 : IterationType aIterationType) :
52 0 : mFilterFunc(aFilterFunc), mIsDeep(aIterationType != eFlatNav)
53 : {
54 0 : mState = new IteratorState(aAccessible);
55 0 : }
56 :
57 0 : AccIterator::~AccIterator()
58 : {
59 0 : while (mState) {
60 0 : IteratorState *tmp = mState;
61 0 : mState = tmp->mParentState;
62 : delete tmp;
63 : }
64 0 : }
65 :
66 : nsAccessible*
67 0 : AccIterator::Next()
68 : {
69 0 : while (mState) {
70 0 : nsAccessible *child = mState->mParent->GetChildAt(mState->mIndex++);
71 0 : if (!child) {
72 0 : IteratorState *tmp = mState;
73 0 : mState = mState->mParentState;
74 : delete tmp;
75 :
76 0 : continue;
77 : }
78 :
79 0 : bool isComplying = mFilterFunc(child);
80 0 : if (isComplying)
81 0 : return child;
82 :
83 0 : if (mIsDeep) {
84 0 : IteratorState *childState = new IteratorState(child, mState);
85 0 : mState = childState;
86 : }
87 : }
88 :
89 0 : return nsnull;
90 : }
91 :
92 : ////////////////////////////////////////////////////////////////////////////////
93 : // nsAccIterator::IteratorState
94 :
95 0 : AccIterator::IteratorState::IteratorState(nsAccessible *aParent,
96 : IteratorState *mParentState) :
97 0 : mParent(aParent), mIndex(0), mParentState(mParentState)
98 : {
99 0 : }
100 :
101 :
102 : ////////////////////////////////////////////////////////////////////////////////
103 : // RelatedAccIterator
104 : ////////////////////////////////////////////////////////////////////////////////
105 :
106 0 : RelatedAccIterator::
107 : RelatedAccIterator(nsDocAccessible* aDocument, nsIContent* aDependentContent,
108 : nsIAtom* aRelAttr) :
109 : mDocument(aDocument), mRelAttr(aRelAttr), mProviders(nsnull),
110 0 : mBindingParent(nsnull), mIndex(0)
111 : {
112 0 : mBindingParent = aDependentContent->GetBindingParent();
113 : nsIAtom* IDAttr = mBindingParent ?
114 0 : nsGkAtoms::anonid : aDependentContent->GetIDAttributeName();
115 :
116 0 : nsAutoString id;
117 0 : if (aDependentContent->GetAttr(kNameSpaceID_None, IDAttr, id))
118 0 : mProviders = mDocument->mDependentIDsHash.Get(id);
119 0 : }
120 :
121 : nsAccessible*
122 0 : RelatedAccIterator::Next()
123 : {
124 0 : if (!mProviders)
125 0 : return nsnull;
126 :
127 0 : while (mIndex < mProviders->Length()) {
128 0 : nsDocAccessible::AttrRelProvider* provider = (*mProviders)[mIndex++];
129 :
130 : // Return related accessible for the given attribute and if the provider
131 : // content is in the same binding in the case of XBL usage.
132 0 : if (provider->mRelAttr == mRelAttr &&
133 0 : (!mBindingParent ||
134 0 : mBindingParent == provider->mContent->GetBindingParent())) {
135 0 : nsAccessible* related = mDocument->GetAccessible(provider->mContent);
136 0 : if (related)
137 0 : return related;
138 :
139 : // If the document content is pointed by relation then return the document
140 : // itself.
141 0 : if (provider->mContent == mDocument->GetContent())
142 0 : return mDocument;
143 : }
144 : }
145 :
146 0 : return nsnull;
147 : }
148 :
149 :
150 : ////////////////////////////////////////////////////////////////////////////////
151 : // HTMLLabelIterator
152 : ////////////////////////////////////////////////////////////////////////////////
153 :
154 0 : HTMLLabelIterator::
155 : HTMLLabelIterator(nsDocAccessible* aDocument, const nsAccessible* aAccessible,
156 : LabelFilter aFilter) :
157 : mRelIter(aDocument, aAccessible->GetContent(), nsGkAtoms::_for),
158 0 : mAcc(aAccessible), mLabelFilter(aFilter)
159 : {
160 0 : }
161 :
162 : nsAccessible*
163 0 : HTMLLabelIterator::Next()
164 : {
165 : // Get either <label for="[id]"> element which explicitly points to given
166 : // element, or <label> ancestor which implicitly point to it.
167 0 : nsAccessible* label = nsnull;
168 0 : while ((label = mRelIter.Next())) {
169 0 : if (label->GetContent()->Tag() == nsGkAtoms::label)
170 0 : return label;
171 : }
172 :
173 : // Ignore ancestor label on not widget accessible.
174 0 : if (mLabelFilter == eSkipAncestorLabel || !mAcc->IsWidget())
175 0 : return nsnull;
176 :
177 : // Go up tree to get a name of ancestor label if there is one (an ancestor
178 : // <label> implicitly points to us). Don't go up farther than form or
179 : // document.
180 0 : nsAccessible* walkUp = mAcc->Parent();
181 0 : while (walkUp && !walkUp->IsDoc()) {
182 0 : nsIContent* walkUpElm = walkUp->GetContent();
183 0 : if (walkUpElm->IsHTML()) {
184 0 : if (walkUpElm->Tag() == nsGkAtoms::label &&
185 0 : !walkUpElm->HasAttr(kNameSpaceID_None, nsGkAtoms::_for)) {
186 0 : mLabelFilter = eSkipAncestorLabel; // prevent infinite loop
187 0 : return walkUp;
188 : }
189 :
190 0 : if (walkUpElm->Tag() == nsGkAtoms::form)
191 0 : break;
192 : }
193 :
194 0 : walkUp = walkUp->Parent();
195 : }
196 :
197 0 : return nsnull;
198 : }
199 :
200 :
201 : ////////////////////////////////////////////////////////////////////////////////
202 : // HTMLOutputIterator
203 : ////////////////////////////////////////////////////////////////////////////////
204 :
205 0 : HTMLOutputIterator::
206 : HTMLOutputIterator(nsDocAccessible* aDocument, nsIContent* aElement) :
207 0 : mRelIter(aDocument, aElement, nsGkAtoms::_for)
208 : {
209 0 : }
210 :
211 : nsAccessible*
212 0 : HTMLOutputIterator::Next()
213 : {
214 0 : nsAccessible* output = nsnull;
215 0 : while ((output = mRelIter.Next())) {
216 0 : if (output->GetContent()->Tag() == nsGkAtoms::output)
217 0 : return output;
218 : }
219 :
220 0 : return nsnull;
221 : }
222 :
223 :
224 : ////////////////////////////////////////////////////////////////////////////////
225 : // XULLabelIterator
226 : ////////////////////////////////////////////////////////////////////////////////
227 :
228 0 : XULLabelIterator::
229 : XULLabelIterator(nsDocAccessible* aDocument, nsIContent* aElement) :
230 0 : mRelIter(aDocument, aElement, nsGkAtoms::control)
231 : {
232 0 : }
233 :
234 : nsAccessible*
235 0 : XULLabelIterator::Next()
236 : {
237 0 : nsAccessible* label = nsnull;
238 0 : while ((label = mRelIter.Next())) {
239 0 : if (label->GetContent()->Tag() == nsGkAtoms::label)
240 0 : return label;
241 : }
242 :
243 0 : return nsnull;
244 : }
245 :
246 :
247 : ////////////////////////////////////////////////////////////////////////////////
248 : // XULDescriptionIterator
249 : ////////////////////////////////////////////////////////////////////////////////
250 :
251 0 : XULDescriptionIterator::
252 : XULDescriptionIterator(nsDocAccessible* aDocument, nsIContent* aElement) :
253 0 : mRelIter(aDocument, aElement, nsGkAtoms::control)
254 : {
255 0 : }
256 :
257 : nsAccessible*
258 0 : XULDescriptionIterator::Next()
259 : {
260 0 : nsAccessible* descr = nsnull;
261 0 : while ((descr = mRelIter.Next())) {
262 0 : if (descr->GetContent()->Tag() == nsGkAtoms::description)
263 0 : return descr;
264 : }
265 :
266 0 : return nsnull;
267 : }
268 :
269 : ////////////////////////////////////////////////////////////////////////////////
270 : // IDRefsIterator
271 : ////////////////////////////////////////////////////////////////////////////////
272 :
273 0 : IDRefsIterator::IDRefsIterator(nsIContent* aContent, nsIAtom* aIDRefsAttr) :
274 0 : mCurrIdx(0)
275 : {
276 0 : if (!aContent->IsInDoc() ||
277 0 : !aContent->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs))
278 0 : return;
279 :
280 0 : if (aContent->IsInAnonymousSubtree()) {
281 0 : mXBLDocument = do_QueryInterface(aContent->OwnerDoc());
282 0 : mBindingParent = do_QueryInterface(aContent->GetBindingParent());
283 : } else {
284 0 : mDocument = aContent->OwnerDoc();
285 : }
286 : }
287 :
288 : const nsDependentSubstring
289 0 : IDRefsIterator::NextID()
290 : {
291 0 : for (; mCurrIdx < mIDs.Length(); mCurrIdx++) {
292 0 : if (!NS_IsAsciiWhitespace(mIDs[mCurrIdx]))
293 0 : break;
294 : }
295 :
296 0 : if (mCurrIdx >= mIDs.Length())
297 0 : return nsDependentSubstring();
298 :
299 0 : nsAString::index_type idStartIdx = mCurrIdx;
300 0 : while (++mCurrIdx < mIDs.Length()) {
301 0 : if (NS_IsAsciiWhitespace(mIDs[mCurrIdx]))
302 0 : break;
303 : }
304 :
305 0 : return Substring(mIDs, idStartIdx, mCurrIdx++ - idStartIdx);
306 : }
307 :
308 : nsIContent*
309 0 : IDRefsIterator::NextElem()
310 : {
311 0 : while (true) {
312 0 : const nsDependentSubstring id = NextID();
313 0 : if (id.IsEmpty())
314 : break;
315 :
316 0 : nsIContent* refContent = GetElem(id);
317 0 : if (refContent)
318 0 : return refContent;
319 : }
320 :
321 0 : return nsnull;
322 : }
323 :
324 : nsIContent*
325 0 : IDRefsIterator::GetElem(const nsDependentSubstring& aID)
326 : {
327 0 : if (mXBLDocument) {
328 : // If content is anonymous subtree then use "anonid" attribute to get
329 : // elements, otherwise search elements in DOM by ID attribute.
330 :
331 0 : nsCOMPtr<nsIDOMElement> refElm;
332 0 : mXBLDocument->GetAnonymousElementByAttribute(mBindingParent,
333 0 : NS_LITERAL_STRING("anonid"),
334 : aID,
335 0 : getter_AddRefs(refElm));
336 0 : nsCOMPtr<nsIContent> refContent = do_QueryInterface(refElm);
337 0 : return refContent;
338 : }
339 :
340 0 : return mDocument->GetElementById(aID);
341 : }
342 :
343 : nsAccessible*
344 0 : IDRefsIterator::Next()
345 : {
346 0 : nsIContent* nextElm = NextElem();
347 0 : return nextElm ? GetAccService()->GetAccessible(nextElm, nsnull) : nsnull;
348 : }
349 :
350 : nsAccessible*
351 0 : SingleAccIterator::Next()
352 : {
353 0 : nsRefPtr<nsAccessible> nextAcc;
354 0 : mAcc.swap(nextAcc);
355 0 : return (nextAcc && !nextAcc->IsDefunct()) ? nextAcc : nsnull;
356 : }
357 :
|