1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set ts=4 et sw=4 tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is this file as it was released on May 1 2001.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Jonas Sicking.
20 : * Portions created by the Initial Developer are Copyright (C) 2001
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Jonas Sicking <sicking@bigfoot.com> (Original Author)
25 : * Craig Topper <craig.topper@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * Implementation of DOM Traversal's nsIDOMTreeWalker
43 : */
44 :
45 : #include "nsTreeWalker.h"
46 :
47 : #include "nsIDOMNode.h"
48 : #include "nsIDOMNodeFilter.h"
49 : #include "nsDOMError.h"
50 : #include "nsINode.h"
51 : #include "nsIContent.h"
52 :
53 : #include "nsContentUtils.h"
54 :
55 : /*
56 : * Factories, constructors and destructors
57 : */
58 :
59 65 : nsTreeWalker::nsTreeWalker(nsINode *aRoot,
60 : PRUint32 aWhatToShow,
61 : nsIDOMNodeFilter *aFilter) :
62 : nsTraversal(aRoot, aWhatToShow, aFilter),
63 65 : mCurrentNode(aRoot)
64 : {
65 65 : }
66 :
67 130 : nsTreeWalker::~nsTreeWalker()
68 : {
69 : /* destructor code */
70 260 : }
71 :
72 : /*
73 : * nsISupports and cycle collection stuff
74 : */
75 :
76 1516 : NS_IMPL_CYCLE_COLLECTION_3(nsTreeWalker, mFilter, mCurrentNode, mRoot)
77 :
78 : DOMCI_DATA(TreeWalker, nsTreeWalker)
79 :
80 : // QueryInterface implementation for nsTreeWalker
81 637 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeWalker)
82 390 : NS_INTERFACE_MAP_ENTRY(nsIDOMTreeWalker)
83 325 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMTreeWalker)
84 260 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TreeWalker)
85 195 : NS_INTERFACE_MAP_END
86 :
87 221 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeWalker)
88 221 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeWalker)
89 :
90 :
91 :
92 : /*
93 : * nsIDOMTreeWalker Getters/Setters
94 : */
95 :
96 : /* readonly attribute nsIDOMNode root; */
97 0 : NS_IMETHODIMP nsTreeWalker::GetRoot(nsIDOMNode * *aRoot)
98 : {
99 0 : if (mRoot) {
100 0 : return CallQueryInterface(mRoot, aRoot);
101 : }
102 :
103 0 : *aRoot = nsnull;
104 :
105 0 : return NS_OK;
106 : }
107 :
108 : /* readonly attribute unsigned long whatToShow; */
109 0 : NS_IMETHODIMP nsTreeWalker::GetWhatToShow(PRUint32 *aWhatToShow)
110 : {
111 0 : *aWhatToShow = mWhatToShow;
112 0 : return NS_OK;
113 : }
114 :
115 : /* readonly attribute nsIDOMNodeFilter filter; */
116 0 : NS_IMETHODIMP nsTreeWalker::GetFilter(nsIDOMNodeFilter * *aFilter)
117 : {
118 0 : NS_ENSURE_ARG_POINTER(aFilter);
119 :
120 0 : NS_IF_ADDREF(*aFilter = mFilter);
121 :
122 0 : return NS_OK;
123 : }
124 :
125 : /* readonly attribute boolean expandEntityReferences; */
126 : NS_IMETHODIMP
127 0 : nsTreeWalker::GetExpandEntityReferences(bool *aExpandEntityReferences)
128 : {
129 0 : *aExpandEntityReferences = false;
130 0 : return NS_OK;
131 : }
132 :
133 : /* attribute nsIDOMNode currentNode; */
134 690 : NS_IMETHODIMP nsTreeWalker::GetCurrentNode(nsIDOMNode * *aCurrentNode)
135 : {
136 690 : if (mCurrentNode) {
137 690 : return CallQueryInterface(mCurrentNode, aCurrentNode);
138 : }
139 :
140 0 : *aCurrentNode = nsnull;
141 :
142 0 : return NS_OK;
143 : }
144 111 : NS_IMETHODIMP nsTreeWalker::SetCurrentNode(nsIDOMNode * aCurrentNode)
145 : {
146 111 : NS_ENSURE_TRUE(aCurrentNode, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
147 :
148 111 : nsresult rv = nsContentUtils::CheckSameOrigin(mRoot, aCurrentNode);
149 111 : NS_ENSURE_SUCCESS(rv, rv);
150 :
151 222 : nsCOMPtr<nsINode> node = do_QueryInterface(aCurrentNode);
152 111 : NS_ENSURE_TRUE(node, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
153 :
154 111 : mCurrentNode.swap(node);
155 111 : return NS_OK;
156 : }
157 :
158 : /*
159 : * nsIDOMTreeWalker functions
160 : */
161 :
162 : /* nsIDOMNode parentNode (); */
163 0 : NS_IMETHODIMP nsTreeWalker::ParentNode(nsIDOMNode **_retval)
164 : {
165 0 : *_retval = nsnull;
166 :
167 : nsresult rv;
168 :
169 0 : nsCOMPtr<nsINode> node = mCurrentNode;
170 :
171 0 : while (node && node != mRoot) {
172 0 : node = node->GetNodeParent();
173 :
174 0 : if (node) {
175 : PRInt16 filtered;
176 0 : rv = TestNode(node, &filtered);
177 0 : NS_ENSURE_SUCCESS(rv, rv);
178 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
179 0 : mCurrentNode = node;
180 0 : return CallQueryInterface(node, _retval);
181 : }
182 : }
183 : }
184 :
185 0 : return NS_OK;
186 : }
187 :
188 : /* nsIDOMNode firstChild (); */
189 0 : NS_IMETHODIMP nsTreeWalker::FirstChild(nsIDOMNode **_retval)
190 : {
191 0 : return FirstChildInternal(false, _retval);
192 : }
193 :
194 : /* nsIDOMNode lastChild (); */
195 0 : NS_IMETHODIMP nsTreeWalker::LastChild(nsIDOMNode **_retval)
196 : {
197 0 : return FirstChildInternal(true, _retval);
198 : }
199 :
200 : /* nsIDOMNode previousSibling (); */
201 0 : NS_IMETHODIMP nsTreeWalker::PreviousSibling(nsIDOMNode **_retval)
202 : {
203 0 : return NextSiblingInternal(true, _retval);
204 : }
205 :
206 : /* nsIDOMNode nextSibling (); */
207 0 : NS_IMETHODIMP nsTreeWalker::NextSibling(nsIDOMNode **_retval)
208 : {
209 0 : return NextSiblingInternal(false, _retval);
210 : }
211 :
212 : /* nsIDOMNode previousNode (); */
213 0 : NS_IMETHODIMP nsTreeWalker::PreviousNode(nsIDOMNode **_retval)
214 : {
215 : nsresult rv;
216 : PRInt16 filtered;
217 :
218 0 : *_retval = nsnull;
219 :
220 0 : nsCOMPtr<nsINode> node = mCurrentNode;
221 :
222 0 : while (node != mRoot) {
223 0 : while (nsINode *previousSibling = node->GetPreviousSibling()) {
224 0 : node = previousSibling;
225 :
226 0 : rv = TestNode(node, &filtered);
227 0 : NS_ENSURE_SUCCESS(rv, rv);
228 :
229 : nsINode *lastChild;
230 0 : while (filtered != nsIDOMNodeFilter::FILTER_REJECT &&
231 0 : (lastChild = node->GetLastChild())) {
232 0 : node = lastChild;
233 0 : rv = TestNode(node, &filtered);
234 0 : NS_ENSURE_SUCCESS(rv, rv);
235 : }
236 :
237 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
238 0 : mCurrentNode = node;
239 0 : return CallQueryInterface(node, _retval);
240 : }
241 : }
242 :
243 0 : if (node == mRoot)
244 0 : break;
245 :
246 0 : node = node->GetNodeParent();
247 0 : if (!node)
248 0 : break;
249 :
250 0 : rv = TestNode(node, &filtered);
251 0 : NS_ENSURE_SUCCESS(rv, rv);
252 :
253 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
254 0 : mCurrentNode = node;
255 0 : return CallQueryInterface(node, _retval);
256 : }
257 : }
258 :
259 0 : return NS_OK;
260 : }
261 :
262 : /* nsIDOMNode nextNode (); */
263 571 : NS_IMETHODIMP nsTreeWalker::NextNode(nsIDOMNode **_retval)
264 : {
265 : nsresult rv;
266 571 : PRInt16 filtered = nsIDOMNodeFilter::FILTER_ACCEPT; // pre-init for inner loop
267 :
268 571 : *_retval = nsnull;
269 :
270 1142 : nsCOMPtr<nsINode> node = mCurrentNode;
271 :
272 1308 : while (1) {
273 :
274 : nsINode *firstChild;
275 7531 : while (filtered != nsIDOMNodeFilter::FILTER_REJECT &&
276 2826 : (firstChild = node->GetFirstChild())) {
277 1079 : node = firstChild;
278 :
279 1079 : rv = TestNode(node, &filtered);
280 1079 : NS_ENSURE_SUCCESS(rv, rv);
281 :
282 1079 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
283 : // Node found
284 132 : mCurrentNode = node;
285 132 : return CallQueryInterface(node, _retval);
286 : }
287 : }
288 :
289 1747 : nsINode *sibling = nsnull;
290 1747 : nsINode *temp = node;
291 915 : do {
292 2662 : if (temp == mRoot)
293 4 : break;
294 :
295 2658 : sibling = temp->GetNextSibling();
296 2658 : if (sibling)
297 1743 : break;
298 :
299 915 : temp = temp->GetNodeParent();
300 : } while (temp);
301 :
302 1747 : if (!sibling)
303 : break;
304 :
305 1743 : node = sibling;
306 :
307 : // Found a sibling. Either ours or ancestor's
308 1743 : rv = TestNode(node, &filtered);
309 1743 : NS_ENSURE_SUCCESS(rv, rv);
310 :
311 1743 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
312 : // Node found
313 435 : mCurrentNode = node;
314 435 : return CallQueryInterface(node, _retval);
315 : }
316 : }
317 :
318 4 : return NS_OK;
319 : }
320 :
321 : /*
322 : * nsTreeWalker helper functions
323 : */
324 :
325 : /*
326 : * Implements FirstChild and LastChild which only vary in which direction
327 : * they search.
328 : * @param aReversed Controls whether we search forwards or backwards
329 : * @param _retval Returned node. Null if no child is found
330 : * @returns Errorcode
331 : */
332 0 : nsresult nsTreeWalker::FirstChildInternal(bool aReversed, nsIDOMNode **_retval)
333 : {
334 : nsresult rv;
335 : PRInt16 filtered;
336 :
337 0 : *_retval = nsnull;
338 :
339 0 : nsCOMPtr<nsINode> node = aReversed ? mCurrentNode->GetLastChild()
340 0 : : mCurrentNode->GetFirstChild();
341 :
342 0 : while (node) {
343 0 : rv = TestNode(node, &filtered);
344 0 : NS_ENSURE_SUCCESS(rv, rv);
345 :
346 0 : switch (filtered) {
347 : case nsIDOMNodeFilter::FILTER_ACCEPT:
348 : // Node found
349 0 : mCurrentNode = node;
350 0 : return CallQueryInterface(node, _retval);
351 : case nsIDOMNodeFilter::FILTER_SKIP: {
352 0 : nsINode *child = aReversed ? node->GetLastChild()
353 0 : : node->GetFirstChild();
354 0 : if (child) {
355 0 : node = child;
356 0 : continue;
357 : }
358 0 : break;
359 : }
360 : case nsIDOMNodeFilter::FILTER_REJECT:
361 : // Keep searching
362 0 : break;
363 : }
364 :
365 0 : do {
366 0 : nsINode *sibling = aReversed ? node->GetPreviousSibling()
367 0 : : node->GetNextSibling();
368 0 : if (sibling) {
369 0 : node = sibling;
370 0 : break;
371 : }
372 :
373 0 : nsINode *parent = node->GetNodeParent();
374 :
375 0 : if (!parent || parent == mRoot || parent == mCurrentNode) {
376 0 : return NS_OK;
377 : }
378 :
379 0 : node = parent;
380 :
381 0 : } while (node);
382 : }
383 :
384 0 : return NS_OK;
385 : }
386 :
387 : /*
388 : * Implements NextSibling and PreviousSibling which only vary in which
389 : * direction they search.
390 : * @param aReversed Controls whether we search forwards or backwards
391 : * @param _retval Returned node. Null if no child is found
392 : * @returns Errorcode
393 : */
394 0 : nsresult nsTreeWalker::NextSiblingInternal(bool aReversed, nsIDOMNode **_retval)
395 : {
396 : nsresult rv;
397 : PRInt16 filtered;
398 :
399 0 : *_retval = nsnull;
400 :
401 0 : nsCOMPtr<nsINode> node = mCurrentNode;
402 :
403 0 : if (node == mRoot)
404 0 : return NS_OK;
405 :
406 0 : while (1) {
407 0 : nsINode* sibling = aReversed ? node->GetPreviousSibling()
408 0 : : node->GetNextSibling();
409 :
410 0 : while (sibling) {
411 0 : node = sibling;
412 :
413 0 : rv = TestNode(node, &filtered);
414 0 : NS_ENSURE_SUCCESS(rv, rv);
415 :
416 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
417 : // Node found
418 0 : mCurrentNode.swap(node);
419 0 : return CallQueryInterface(mCurrentNode, _retval);
420 : }
421 :
422 : // If rejected or no children, try a sibling
423 0 : if (filtered == nsIDOMNodeFilter::FILTER_REJECT ||
424 0 : !(sibling = aReversed ? node->GetLastChild()
425 0 : : node->GetFirstChild())) {
426 0 : sibling = aReversed ? node->GetPreviousSibling()
427 0 : : node->GetNextSibling();
428 : }
429 : }
430 :
431 0 : node = node->GetNodeParent();
432 :
433 0 : if (!node || node == mRoot)
434 0 : return NS_OK;
435 :
436 : // Is parent transparent in filtered view?
437 0 : rv = TestNode(node, &filtered);
438 0 : NS_ENSURE_SUCCESS(rv, rv);
439 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT)
440 0 : return NS_OK;
441 : }
442 4392 : }
|