1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=79: */
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 mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 2001
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Joe Hewitt <hewitt@netscape.com> (original author)
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "inDeepTreeWalker.h"
41 : #include "inLayoutUtils.h"
42 :
43 : #include "nsString.h"
44 : #include "nsIDOMDocument.h"
45 : #include "nsIDOMNodeFilter.h"
46 : #include "nsIDOMNodeList.h"
47 : #include "nsServiceManagerUtils.h"
48 : #include "inIDOMUtils.h"
49 : #include "nsIContent.h"
50 : #include "nsBindingManager.h"
51 :
52 : /*****************************************************************************
53 : * This implementation does not currently operaate according to the W3C spec.
54 : * In particular it does NOT handle DOM mutations during the walk. It also
55 : * ignores whatToShow and the filter.
56 : *****************************************************************************/
57 :
58 : ////////////////////////////////////////////////////
59 :
60 0 : inDeepTreeWalker::inDeepTreeWalker()
61 : : mShowAnonymousContent(false),
62 : mShowSubDocuments(false),
63 0 : mWhatToShow(nsIDOMNodeFilter::SHOW_ALL)
64 : {
65 0 : }
66 :
67 0 : inDeepTreeWalker::~inDeepTreeWalker()
68 : {
69 0 : }
70 :
71 0 : NS_IMPL_ISUPPORTS2(inDeepTreeWalker,
72 : inIDeepTreeWalker,
73 : nsIDOMTreeWalker)
74 :
75 : ////////////////////////////////////////////////////
76 : // inIDeepTreeWalker
77 :
78 : NS_IMETHODIMP
79 0 : inDeepTreeWalker::GetShowAnonymousContent(bool *aShowAnonymousContent)
80 : {
81 0 : *aShowAnonymousContent = mShowAnonymousContent;
82 0 : return NS_OK;
83 : }
84 :
85 : NS_IMETHODIMP
86 0 : inDeepTreeWalker::SetShowAnonymousContent(bool aShowAnonymousContent)
87 : {
88 0 : mShowAnonymousContent = aShowAnonymousContent;
89 0 : return NS_OK;
90 : }
91 :
92 : NS_IMETHODIMP
93 0 : inDeepTreeWalker::GetShowSubDocuments(bool *aShowSubDocuments)
94 : {
95 0 : *aShowSubDocuments = mShowSubDocuments;
96 0 : return NS_OK;
97 : }
98 :
99 : NS_IMETHODIMP
100 0 : inDeepTreeWalker::SetShowSubDocuments(bool aShowSubDocuments)
101 : {
102 0 : mShowSubDocuments = aShowSubDocuments;
103 0 : return NS_OK;
104 : }
105 :
106 : NS_IMETHODIMP
107 0 : inDeepTreeWalker::Init(nsIDOMNode* aRoot, PRUint32 aWhatToShow)
108 : {
109 0 : mRoot = aRoot;
110 0 : mWhatToShow = aWhatToShow;
111 :
112 0 : PushNode(aRoot);
113 :
114 0 : return NS_OK;
115 : }
116 :
117 : ////////////////////////////////////////////////////
118 : // nsIDOMTreeWalker
119 :
120 : NS_IMETHODIMP
121 0 : inDeepTreeWalker::GetRoot(nsIDOMNode** aRoot)
122 : {
123 0 : *aRoot = mRoot;
124 0 : NS_IF_ADDREF(*aRoot);
125 :
126 0 : return NS_OK;
127 : }
128 :
129 : NS_IMETHODIMP
130 0 : inDeepTreeWalker::GetWhatToShow(PRUint32* aWhatToShow)
131 : {
132 0 : *aWhatToShow = mWhatToShow;
133 0 : return NS_OK;
134 : }
135 :
136 : NS_IMETHODIMP
137 0 : inDeepTreeWalker::GetFilter(nsIDOMNodeFilter** aFilter)
138 : {
139 0 : return NS_ERROR_NOT_IMPLEMENTED;
140 : }
141 :
142 : NS_IMETHODIMP
143 0 : inDeepTreeWalker::GetExpandEntityReferences(bool* aExpandEntityReferences)
144 : {
145 0 : return NS_ERROR_NOT_IMPLEMENTED;
146 : }
147 :
148 : NS_IMETHODIMP
149 0 : inDeepTreeWalker::GetCurrentNode(nsIDOMNode** aCurrentNode)
150 : {
151 0 : *aCurrentNode = mCurrentNode;
152 0 : NS_IF_ADDREF(*aCurrentNode);
153 :
154 0 : return NS_OK;
155 : }
156 :
157 : NS_IMETHODIMP
158 0 : inDeepTreeWalker::SetCurrentNode(nsIDOMNode* aCurrentNode)
159 : {
160 0 : return NS_ERROR_NOT_IMPLEMENTED;
161 : }
162 :
163 : NS_IMETHODIMP
164 0 : inDeepTreeWalker::ParentNode(nsIDOMNode** _retval)
165 : {
166 0 : *_retval = nsnull;
167 0 : if (!mCurrentNode) return NS_OK;
168 :
169 0 : if (mStack.Length() == 1) {
170 : // No parent
171 0 : return NS_OK;
172 : }
173 :
174 : // Pop off the current node, and push the new one
175 0 : mStack.RemoveElementAt(mStack.Length()-1);
176 0 : DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1);
177 0 : mCurrentNode = top.node;
178 0 : top.lastIndex = 0;
179 0 : NS_ADDREF(*_retval = mCurrentNode);
180 0 : return NS_OK;
181 : }
182 :
183 : NS_IMETHODIMP
184 0 : inDeepTreeWalker::FirstChild(nsIDOMNode **_retval)
185 : {
186 0 : *_retval = nsnull;
187 0 : if (!mCurrentNode) {
188 0 : return NS_OK;
189 : }
190 :
191 0 : DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1);
192 0 : nsCOMPtr<nsIDOMNode> kid;
193 0 : top.kids->Item(0, getter_AddRefs(kid));
194 0 : if (!kid) {
195 0 : return NS_OK;
196 : }
197 0 : top.lastIndex = 1;
198 0 : PushNode(kid);
199 0 : kid.forget(_retval);
200 0 : return NS_OK;
201 : }
202 :
203 : NS_IMETHODIMP
204 0 : inDeepTreeWalker::LastChild(nsIDOMNode **_retval)
205 : {
206 0 : *_retval = nsnull;
207 0 : if (!mCurrentNode) {
208 0 : return NS_OK;
209 : }
210 :
211 0 : DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1);
212 0 : nsCOMPtr<nsIDOMNode> kid;
213 : PRUint32 length;
214 0 : top.kids->GetLength(&length);
215 0 : top.kids->Item(length - 1, getter_AddRefs(kid));
216 0 : if (!kid) {
217 0 : return NS_OK;
218 : }
219 0 : top.lastIndex = length;
220 0 : PushNode(kid);
221 0 : kid.forget(_retval);
222 0 : return NS_OK;
223 : }
224 :
225 : NS_IMETHODIMP
226 0 : inDeepTreeWalker::PreviousSibling(nsIDOMNode **_retval)
227 : {
228 0 : *_retval = nsnull;
229 0 : if (!mCurrentNode) {
230 0 : return NS_OK;
231 : }
232 :
233 0 : NS_ASSERTION(mStack.Length() > 0, "Should have things in mStack");
234 :
235 0 : if (mStack.Length() == 1) {
236 : // No previous sibling
237 0 : return NS_OK;
238 : }
239 :
240 0 : DeepTreeStackItem& parent = mStack.ElementAt(mStack.Length()-2);
241 0 : nsCOMPtr<nsIDOMNode> previousSibling;
242 0 : parent.kids->Item(parent.lastIndex-2, getter_AddRefs(previousSibling));
243 0 : if (!previousSibling) {
244 0 : return NS_OK;
245 : }
246 :
247 : // Our mStack's topmost element is our current node. Since we're trying to
248 : // change that to the previous sibling, pop off the current node, and push
249 : // the new one.
250 0 : mStack.RemoveElementAt(mStack.Length() - 1);
251 0 : parent.lastIndex--;
252 0 : PushNode(previousSibling);
253 0 : previousSibling.forget(_retval);
254 0 : return NS_OK;
255 : }
256 :
257 : NS_IMETHODIMP
258 0 : inDeepTreeWalker::NextSibling(nsIDOMNode **_retval)
259 : {
260 0 : *_retval = nsnull;
261 0 : if (!mCurrentNode) {
262 0 : return NS_OK;
263 : }
264 :
265 0 : NS_ASSERTION(mStack.Length() > 0, "Should have things in mStack");
266 :
267 0 : if (mStack.Length() == 1) {
268 : // No next sibling
269 0 : return NS_OK;
270 : }
271 :
272 0 : DeepTreeStackItem& parent = mStack.ElementAt(mStack.Length()-2);
273 0 : nsCOMPtr<nsIDOMNode> nextSibling;
274 0 : parent.kids->Item(parent.lastIndex, getter_AddRefs(nextSibling));
275 0 : if (!nextSibling) {
276 0 : return NS_OK;
277 : }
278 :
279 : // Our mStack's topmost element is our current node. Since we're trying to
280 : // change that to the next sibling, pop off the current node, and push
281 : // the new one.
282 0 : mStack.RemoveElementAt(mStack.Length() - 1);
283 0 : parent.lastIndex++;
284 0 : PushNode(nextSibling);
285 0 : nextSibling.forget(_retval);
286 0 : return NS_OK;
287 : }
288 :
289 : NS_IMETHODIMP
290 0 : inDeepTreeWalker::PreviousNode(nsIDOMNode **_retval)
291 : {
292 0 : if (!mCurrentNode || mStack.Length() == 1) {
293 : // Nowhere to go from here
294 0 : *_retval = nsnull;
295 0 : return NS_OK;
296 : }
297 :
298 0 : nsCOMPtr<nsIDOMNode> node;
299 0 : PreviousSibling(getter_AddRefs(node));
300 :
301 0 : if (!node) {
302 0 : return ParentNode(_retval);
303 : }
304 :
305 : // Now we're positioned at our previous sibling. But since the DOM tree
306 : // traversal is depth-first, the previous node is its most deeply nested last
307 : // child. Just loop until LastChild() returns null; since the LastChild()
308 : // call that returns null won't affect our position, we will then be
309 : // positioned at the correct node.
310 0 : while (node) {
311 0 : LastChild(getter_AddRefs(node));
312 : }
313 :
314 0 : NS_ADDREF(*_retval = mCurrentNode);
315 0 : return NS_OK;
316 : }
317 :
318 : NS_IMETHODIMP
319 0 : inDeepTreeWalker::NextNode(nsIDOMNode **_retval)
320 : {
321 : // First try our kids
322 0 : FirstChild(_retval);
323 :
324 0 : if (*_retval) {
325 0 : return NS_OK;
326 : }
327 :
328 : // Now keep trying next siblings up the parent chain, but if we
329 : // discover there's nothing else restore our state.
330 : #ifdef DEBUG
331 0 : nsIDOMNode* origCurrentNode = mCurrentNode;
332 : #endif
333 0 : PRUint32 lastChildCallsToMake = 0;
334 0 : while (1) {
335 0 : NextSibling(_retval);
336 :
337 0 : if (*_retval) {
338 0 : return NS_OK;
339 : }
340 :
341 0 : nsCOMPtr<nsIDOMNode> parent;
342 0 : ParentNode(getter_AddRefs(parent));
343 0 : if (!parent) {
344 : // Nowhere else to go; we're done. Restore our state.
345 0 : while (lastChildCallsToMake--) {
346 0 : nsCOMPtr<nsIDOMNode> dummy;
347 0 : LastChild(getter_AddRefs(dummy));
348 : }
349 0 : NS_ASSERTION(mCurrentNode == origCurrentNode,
350 : "Didn't go back to the right node?");
351 0 : *_retval = nsnull;
352 0 : return NS_OK;
353 : }
354 0 : ++lastChildCallsToMake;
355 : }
356 :
357 : NS_NOTREACHED("how did we get here?");
358 : return NS_OK;
359 : }
360 :
361 : void
362 0 : inDeepTreeWalker::PushNode(nsIDOMNode* aNode)
363 : {
364 0 : mCurrentNode = aNode;
365 0 : if (!aNode) return;
366 :
367 0 : DeepTreeStackItem item;
368 0 : item.node = aNode;
369 :
370 0 : nsCOMPtr<nsIDOMNodeList> kids;
371 0 : if (mShowSubDocuments) {
372 0 : nsCOMPtr<nsIDOMDocument> domdoc = inLayoutUtils::GetSubDocumentFor(aNode);
373 0 : if (domdoc) {
374 0 : domdoc->GetChildNodes(getter_AddRefs(kids));
375 : }
376 : }
377 :
378 0 : if (!kids) {
379 0 : if (mShowAnonymousContent) {
380 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
381 0 : nsRefPtr<nsBindingManager> bindingManager;
382 0 : if (content &&
383 0 : (bindingManager = inLayoutUtils::GetBindingManagerFor(aNode))) {
384 0 : bindingManager->GetAnonymousNodesFor(content, getter_AddRefs(kids));
385 0 : if (!kids)
386 0 : bindingManager->GetContentListFor(content, getter_AddRefs(kids));
387 : } else {
388 0 : aNode->GetChildNodes(getter_AddRefs(kids));
389 : }
390 : } else
391 0 : aNode->GetChildNodes(getter_AddRefs(kids));
392 : }
393 :
394 0 : item.kids = kids;
395 0 : item.lastIndex = 0;
396 0 : mStack.AppendElement(item);
397 : }
398 :
399 : /*
400 : // This NextNode implementation does not require the use of stacks,
401 : // as does the one above. However, it does not handle anonymous
402 : // content and sub-documents.
403 : NS_IMETHODIMP
404 : inDeepTreeWalker::NextNode(nsIDOMNode **_retval)
405 : {
406 : if (!mCurrentNode) return NS_OK;
407 :
408 : // walk down the tree first
409 : nsCOMPtr<nsIDOMNode> next;
410 : mCurrentNode->GetFirstChild(getter_AddRefs(next));
411 : if (!next) {
412 : mCurrentNode->GetNextSibling(getter_AddRefs(next));
413 : if (!next) {
414 : // we've hit the end, so walk back up the tree until another
415 : // downward opening is found, or the top of the tree
416 : nsCOMPtr<nsIDOMNode> subject = mCurrentNode;
417 : nsCOMPtr<nsIDOMNode> parent;
418 : while (1) {
419 : subject->GetParentNode(getter_AddRefs(parent));
420 : if (!parent) // hit the top of the tree
421 : break;
422 : parent->GetNextSibling(getter_AddRefs(subject));
423 : if (subject) { // found a downward opening
424 : next = subject;
425 : break;
426 : } else // walk up another level
427 : subject = parent;
428 : }
429 : }
430 : }
431 :
432 : mCurrentNode = next;
433 :
434 : *_retval = next;
435 : NS_IF_ADDREF(*_retval);
436 :
437 : return NS_OK;
438 : }
439 :
440 :
441 : char* getURL(nsIDOMDocument* aDoc)
442 : {
443 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
444 : nsIURI *uri = doc->GetDocumentURI();
445 : char* s;
446 : uri->GetSpec(&s);
447 : return s;
448 : }
449 : */
|