|
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 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "inDeepTreeWalker.h" |
|
8 #include "inLayoutUtils.h" |
|
9 |
|
10 #include "nsString.h" |
|
11 #include "nsIDOMDocument.h" |
|
12 #include "nsIDOMNodeFilter.h" |
|
13 #include "nsIDOMNodeList.h" |
|
14 #include "nsServiceManagerUtils.h" |
|
15 #include "inIDOMUtils.h" |
|
16 #include "nsIContent.h" |
|
17 #include "nsContentList.h" |
|
18 #include "ChildIterator.h" |
|
19 #include "mozilla/dom/Element.h" |
|
20 |
|
21 /***************************************************************************** |
|
22 * This implementation does not currently operaate according to the W3C spec. |
|
23 * In particular it does NOT handle DOM mutations during the walk. It also |
|
24 * ignores whatToShow and the filter. |
|
25 *****************************************************************************/ |
|
26 |
|
27 //////////////////////////////////////////////////// |
|
28 |
|
29 inDeepTreeWalker::inDeepTreeWalker() |
|
30 : mShowAnonymousContent(false), |
|
31 mShowSubDocuments(false), |
|
32 mWhatToShow(nsIDOMNodeFilter::SHOW_ALL) |
|
33 { |
|
34 } |
|
35 |
|
36 inDeepTreeWalker::~inDeepTreeWalker() |
|
37 { |
|
38 } |
|
39 |
|
40 NS_IMPL_ISUPPORTS(inDeepTreeWalker, |
|
41 inIDeepTreeWalker, |
|
42 nsIDOMTreeWalker) |
|
43 |
|
44 //////////////////////////////////////////////////// |
|
45 // inIDeepTreeWalker |
|
46 |
|
47 NS_IMETHODIMP |
|
48 inDeepTreeWalker::GetShowAnonymousContent(bool *aShowAnonymousContent) |
|
49 { |
|
50 *aShowAnonymousContent = mShowAnonymousContent; |
|
51 return NS_OK; |
|
52 } |
|
53 |
|
54 NS_IMETHODIMP |
|
55 inDeepTreeWalker::SetShowAnonymousContent(bool aShowAnonymousContent) |
|
56 { |
|
57 mShowAnonymousContent = aShowAnonymousContent; |
|
58 return NS_OK; |
|
59 } |
|
60 |
|
61 NS_IMETHODIMP |
|
62 inDeepTreeWalker::GetShowSubDocuments(bool *aShowSubDocuments) |
|
63 { |
|
64 *aShowSubDocuments = mShowSubDocuments; |
|
65 return NS_OK; |
|
66 } |
|
67 |
|
68 NS_IMETHODIMP |
|
69 inDeepTreeWalker::SetShowSubDocuments(bool aShowSubDocuments) |
|
70 { |
|
71 mShowSubDocuments = aShowSubDocuments; |
|
72 return NS_OK; |
|
73 } |
|
74 |
|
75 NS_IMETHODIMP |
|
76 inDeepTreeWalker::Init(nsIDOMNode* aRoot, uint32_t aWhatToShow) |
|
77 { |
|
78 mRoot = aRoot; |
|
79 mWhatToShow = aWhatToShow; |
|
80 |
|
81 PushNode(aRoot); |
|
82 |
|
83 return NS_OK; |
|
84 } |
|
85 |
|
86 //////////////////////////////////////////////////// |
|
87 // nsIDOMTreeWalker |
|
88 |
|
89 NS_IMETHODIMP |
|
90 inDeepTreeWalker::GetRoot(nsIDOMNode** aRoot) |
|
91 { |
|
92 *aRoot = mRoot; |
|
93 NS_IF_ADDREF(*aRoot); |
|
94 |
|
95 return NS_OK; |
|
96 } |
|
97 |
|
98 NS_IMETHODIMP |
|
99 inDeepTreeWalker::GetWhatToShow(uint32_t* aWhatToShow) |
|
100 { |
|
101 *aWhatToShow = mWhatToShow; |
|
102 return NS_OK; |
|
103 } |
|
104 |
|
105 NS_IMETHODIMP |
|
106 inDeepTreeWalker::GetFilter(nsIDOMNodeFilter** aFilter) |
|
107 { |
|
108 return NS_ERROR_NOT_IMPLEMENTED; |
|
109 } |
|
110 |
|
111 NS_IMETHODIMP |
|
112 inDeepTreeWalker::GetCurrentNode(nsIDOMNode** aCurrentNode) |
|
113 { |
|
114 *aCurrentNode = mCurrentNode; |
|
115 NS_IF_ADDREF(*aCurrentNode); |
|
116 |
|
117 return NS_OK; |
|
118 } |
|
119 |
|
120 NS_IMETHODIMP |
|
121 inDeepTreeWalker::SetCurrentNode(nsIDOMNode* aCurrentNode) |
|
122 { |
|
123 return NS_ERROR_NOT_IMPLEMENTED; |
|
124 } |
|
125 |
|
126 NS_IMETHODIMP |
|
127 inDeepTreeWalker::ParentNode(nsIDOMNode** _retval) |
|
128 { |
|
129 *_retval = nullptr; |
|
130 if (!mCurrentNode) return NS_OK; |
|
131 |
|
132 if (mStack.Length() == 1) { |
|
133 // No parent |
|
134 return NS_OK; |
|
135 } |
|
136 |
|
137 // Pop off the current node, and push the new one |
|
138 mStack.RemoveElementAt(mStack.Length()-1); |
|
139 DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1); |
|
140 mCurrentNode = top.node; |
|
141 top.lastIndex = 0; |
|
142 NS_ADDREF(*_retval = mCurrentNode); |
|
143 return NS_OK; |
|
144 } |
|
145 |
|
146 NS_IMETHODIMP |
|
147 inDeepTreeWalker::FirstChild(nsIDOMNode **_retval) |
|
148 { |
|
149 *_retval = nullptr; |
|
150 if (!mCurrentNode) { |
|
151 return NS_OK; |
|
152 } |
|
153 |
|
154 DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1); |
|
155 nsCOMPtr<nsIDOMNode> kid; |
|
156 top.kids->Item(0, getter_AddRefs(kid)); |
|
157 if (!kid) { |
|
158 return NS_OK; |
|
159 } |
|
160 top.lastIndex = 1; |
|
161 PushNode(kid); |
|
162 kid.forget(_retval); |
|
163 return NS_OK; |
|
164 } |
|
165 |
|
166 NS_IMETHODIMP |
|
167 inDeepTreeWalker::LastChild(nsIDOMNode **_retval) |
|
168 { |
|
169 *_retval = nullptr; |
|
170 if (!mCurrentNode) { |
|
171 return NS_OK; |
|
172 } |
|
173 |
|
174 DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1); |
|
175 nsCOMPtr<nsIDOMNode> kid; |
|
176 uint32_t length; |
|
177 top.kids->GetLength(&length); |
|
178 top.kids->Item(length - 1, getter_AddRefs(kid)); |
|
179 if (!kid) { |
|
180 return NS_OK; |
|
181 } |
|
182 top.lastIndex = length; |
|
183 PushNode(kid); |
|
184 kid.forget(_retval); |
|
185 return NS_OK; |
|
186 } |
|
187 |
|
188 NS_IMETHODIMP |
|
189 inDeepTreeWalker::PreviousSibling(nsIDOMNode **_retval) |
|
190 { |
|
191 *_retval = nullptr; |
|
192 if (!mCurrentNode) { |
|
193 return NS_OK; |
|
194 } |
|
195 |
|
196 NS_ASSERTION(mStack.Length() > 0, "Should have things in mStack"); |
|
197 |
|
198 if (mStack.Length() == 1) { |
|
199 // No previous sibling |
|
200 return NS_OK; |
|
201 } |
|
202 |
|
203 DeepTreeStackItem& parent = mStack.ElementAt(mStack.Length()-2); |
|
204 nsCOMPtr<nsIDOMNode> previousSibling; |
|
205 parent.kids->Item(parent.lastIndex-2, getter_AddRefs(previousSibling)); |
|
206 if (!previousSibling) { |
|
207 return NS_OK; |
|
208 } |
|
209 |
|
210 // Our mStack's topmost element is our current node. Since we're trying to |
|
211 // change that to the previous sibling, pop off the current node, and push |
|
212 // the new one. |
|
213 mStack.RemoveElementAt(mStack.Length() - 1); |
|
214 parent.lastIndex--; |
|
215 PushNode(previousSibling); |
|
216 previousSibling.forget(_retval); |
|
217 return NS_OK; |
|
218 } |
|
219 |
|
220 NS_IMETHODIMP |
|
221 inDeepTreeWalker::NextSibling(nsIDOMNode **_retval) |
|
222 { |
|
223 *_retval = nullptr; |
|
224 if (!mCurrentNode) { |
|
225 return NS_OK; |
|
226 } |
|
227 |
|
228 NS_ASSERTION(mStack.Length() > 0, "Should have things in mStack"); |
|
229 |
|
230 if (mStack.Length() == 1) { |
|
231 // No next sibling |
|
232 return NS_OK; |
|
233 } |
|
234 |
|
235 DeepTreeStackItem& parent = mStack.ElementAt(mStack.Length()-2); |
|
236 nsCOMPtr<nsIDOMNode> nextSibling; |
|
237 parent.kids->Item(parent.lastIndex, getter_AddRefs(nextSibling)); |
|
238 if (!nextSibling) { |
|
239 return NS_OK; |
|
240 } |
|
241 |
|
242 // Our mStack's topmost element is our current node. Since we're trying to |
|
243 // change that to the next sibling, pop off the current node, and push |
|
244 // the new one. |
|
245 mStack.RemoveElementAt(mStack.Length() - 1); |
|
246 parent.lastIndex++; |
|
247 PushNode(nextSibling); |
|
248 nextSibling.forget(_retval); |
|
249 return NS_OK; |
|
250 } |
|
251 |
|
252 NS_IMETHODIMP |
|
253 inDeepTreeWalker::PreviousNode(nsIDOMNode **_retval) |
|
254 { |
|
255 if (!mCurrentNode || mStack.Length() == 1) { |
|
256 // Nowhere to go from here |
|
257 *_retval = nullptr; |
|
258 return NS_OK; |
|
259 } |
|
260 |
|
261 nsCOMPtr<nsIDOMNode> node; |
|
262 PreviousSibling(getter_AddRefs(node)); |
|
263 |
|
264 if (!node) { |
|
265 return ParentNode(_retval); |
|
266 } |
|
267 |
|
268 // Now we're positioned at our previous sibling. But since the DOM tree |
|
269 // traversal is depth-first, the previous node is its most deeply nested last |
|
270 // child. Just loop until LastChild() returns null; since the LastChild() |
|
271 // call that returns null won't affect our position, we will then be |
|
272 // positioned at the correct node. |
|
273 while (node) { |
|
274 LastChild(getter_AddRefs(node)); |
|
275 } |
|
276 |
|
277 NS_ADDREF(*_retval = mCurrentNode); |
|
278 return NS_OK; |
|
279 } |
|
280 |
|
281 NS_IMETHODIMP |
|
282 inDeepTreeWalker::NextNode(nsIDOMNode **_retval) |
|
283 { |
|
284 // First try our kids |
|
285 FirstChild(_retval); |
|
286 |
|
287 if (*_retval) { |
|
288 return NS_OK; |
|
289 } |
|
290 |
|
291 // Now keep trying next siblings up the parent chain, but if we |
|
292 // discover there's nothing else restore our state. |
|
293 #ifdef DEBUG |
|
294 nsIDOMNode* origCurrentNode = mCurrentNode; |
|
295 #endif |
|
296 uint32_t lastChildCallsToMake = 0; |
|
297 while (1) { |
|
298 NextSibling(_retval); |
|
299 |
|
300 if (*_retval) { |
|
301 return NS_OK; |
|
302 } |
|
303 |
|
304 nsCOMPtr<nsIDOMNode> parent; |
|
305 ParentNode(getter_AddRefs(parent)); |
|
306 if (!parent) { |
|
307 // Nowhere else to go; we're done. Restore our state. |
|
308 while (lastChildCallsToMake--) { |
|
309 nsCOMPtr<nsIDOMNode> dummy; |
|
310 LastChild(getter_AddRefs(dummy)); |
|
311 } |
|
312 NS_ASSERTION(mCurrentNode == origCurrentNode, |
|
313 "Didn't go back to the right node?"); |
|
314 *_retval = nullptr; |
|
315 return NS_OK; |
|
316 } |
|
317 ++lastChildCallsToMake; |
|
318 } |
|
319 |
|
320 NS_NOTREACHED("how did we get here?"); |
|
321 return NS_OK; |
|
322 } |
|
323 |
|
324 void |
|
325 inDeepTreeWalker::PushNode(nsIDOMNode* aNode) |
|
326 { |
|
327 mCurrentNode = aNode; |
|
328 if (!aNode) return; |
|
329 |
|
330 DeepTreeStackItem item; |
|
331 item.node = aNode; |
|
332 |
|
333 nsCOMPtr<nsIDOMNodeList> kids; |
|
334 if (mShowSubDocuments) { |
|
335 nsCOMPtr<nsIDOMDocument> domdoc = inLayoutUtils::GetSubDocumentFor(aNode); |
|
336 if (domdoc) { |
|
337 domdoc->GetChildNodes(getter_AddRefs(kids)); |
|
338 } |
|
339 } |
|
340 |
|
341 if (!kids) { |
|
342 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode); |
|
343 if (content && mShowAnonymousContent) { |
|
344 kids = content->GetChildren(nsIContent::eAllChildren); |
|
345 } |
|
346 } |
|
347 if (!kids) { |
|
348 aNode->GetChildNodes(getter_AddRefs(kids)); |
|
349 } |
|
350 |
|
351 item.kids = kids; |
|
352 item.lastIndex = 0; |
|
353 mStack.AppendElement(item); |
|
354 } |
|
355 |
|
356 /* |
|
357 // This NextNode implementation does not require the use of stacks, |
|
358 // as does the one above. However, it does not handle anonymous |
|
359 // content and sub-documents. |
|
360 NS_IMETHODIMP |
|
361 inDeepTreeWalker::NextNode(nsIDOMNode **_retval) |
|
362 { |
|
363 if (!mCurrentNode) return NS_OK; |
|
364 |
|
365 // walk down the tree first |
|
366 nsCOMPtr<nsIDOMNode> next; |
|
367 mCurrentNode->GetFirstChild(getter_AddRefs(next)); |
|
368 if (!next) { |
|
369 mCurrentNode->GetNextSibling(getter_AddRefs(next)); |
|
370 if (!next) { |
|
371 // we've hit the end, so walk back up the tree until another |
|
372 // downward opening is found, or the top of the tree |
|
373 nsCOMPtr<nsIDOMNode> subject = mCurrentNode; |
|
374 nsCOMPtr<nsIDOMNode> parent; |
|
375 while (1) { |
|
376 subject->GetParentNode(getter_AddRefs(parent)); |
|
377 if (!parent) // hit the top of the tree |
|
378 break; |
|
379 parent->GetNextSibling(getter_AddRefs(subject)); |
|
380 if (subject) { // found a downward opening |
|
381 next = subject; |
|
382 break; |
|
383 } else // walk up another level |
|
384 subject = parent; |
|
385 } |
|
386 } |
|
387 } |
|
388 |
|
389 mCurrentNode = next; |
|
390 |
|
391 *_retval = next; |
|
392 NS_IF_ADDREF(*_retval); |
|
393 |
|
394 return NS_OK; |
|
395 } |
|
396 |
|
397 |
|
398 char* getURL(nsIDOMDocument* aDoc) |
|
399 { |
|
400 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc); |
|
401 nsIURI *uri = doc->GetDocumentURI(); |
|
402 char* s; |
|
403 uri->GetSpec(&s); |
|
404 return s; |
|
405 } |
|
406 */ |