content/base/src/TreeWalker.cpp

branch
TOR_BUG_9701
changeset 11
deefc01c0e14
equal deleted inserted replaced
-1:000000000000 0:6ee4ac0c2450
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 /* 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 /*
8 * Implementation of DOM Traversal's nsIDOMTreeWalker
9 */
10
11 #include "mozilla/dom/TreeWalker.h"
12
13 #include "nsIContent.h"
14 #include "nsIDOMNode.h"
15 #include "nsError.h"
16 #include "nsINode.h"
17 #include "nsContentUtils.h"
18 #include "mozilla/dom/TreeWalkerBinding.h"
19
20 namespace mozilla {
21 namespace dom {
22
23 /*
24 * Factories, constructors and destructors
25 */
26
27 TreeWalker::TreeWalker(nsINode *aRoot,
28 uint32_t aWhatToShow,
29 const NodeFilterHolder &aFilter) :
30 nsTraversal(aRoot, aWhatToShow, aFilter),
31 mCurrentNode(aRoot)
32 {
33 }
34
35 TreeWalker::~TreeWalker()
36 {
37 /* destructor code */
38 }
39
40 /*
41 * nsISupports and cycle collection stuff
42 */
43
44 NS_IMPL_CYCLE_COLLECTION(TreeWalker, mFilter, mCurrentNode, mRoot)
45
46 // QueryInterface implementation for TreeWalker
47 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TreeWalker)
48 NS_INTERFACE_MAP_ENTRY(nsIDOMTreeWalker)
49 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMTreeWalker)
50 NS_INTERFACE_MAP_END
51
52 // Have to pass in dom::TreeWalker because a11y has an a11y::TreeWalker that
53 // passes TreeWalker so refcount logging would get confused on the name
54 // collision.
55 NS_IMPL_CYCLE_COLLECTING_ADDREF(dom::TreeWalker)
56 NS_IMPL_CYCLE_COLLECTING_RELEASE(dom::TreeWalker)
57
58
59
60 /*
61 * nsIDOMTreeWalker Getters/Setters
62 */
63
64 /* readonly attribute nsIDOMNode root; */
65 NS_IMETHODIMP TreeWalker::GetRoot(nsIDOMNode * *aRoot)
66 {
67 NS_ADDREF(*aRoot = Root()->AsDOMNode());
68 return NS_OK;
69 }
70
71 /* readonly attribute unsigned long whatToShow; */
72 NS_IMETHODIMP TreeWalker::GetWhatToShow(uint32_t *aWhatToShow)
73 {
74 *aWhatToShow = WhatToShow();
75 return NS_OK;
76 }
77
78 /* readonly attribute nsIDOMNodeFilter filter; */
79 NS_IMETHODIMP TreeWalker::GetFilter(nsIDOMNodeFilter * *aFilter)
80 {
81 NS_ENSURE_ARG_POINTER(aFilter);
82
83 *aFilter = mFilter.ToXPCOMCallback().take();
84
85 return NS_OK;
86 }
87
88 /* attribute nsIDOMNode currentNode; */
89 NS_IMETHODIMP TreeWalker::GetCurrentNode(nsIDOMNode * *aCurrentNode)
90 {
91 if (mCurrentNode) {
92 return CallQueryInterface(mCurrentNode, aCurrentNode);
93 }
94
95 *aCurrentNode = nullptr;
96
97 return NS_OK;
98 }
99 NS_IMETHODIMP TreeWalker::SetCurrentNode(nsIDOMNode * aCurrentNode)
100 {
101 NS_ENSURE_TRUE(aCurrentNode, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
102 NS_ENSURE_TRUE(mRoot, NS_ERROR_UNEXPECTED);
103
104 nsCOMPtr<nsINode> node = do_QueryInterface(aCurrentNode);
105 NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
106
107 ErrorResult rv;
108 SetCurrentNode(*node, rv);
109 return rv.ErrorCode();
110 }
111
112 void
113 TreeWalker::SetCurrentNode(nsINode& aNode, ErrorResult& aResult)
114 {
115 aResult = nsContentUtils::CheckSameOrigin(mRoot, &aNode);
116 if (aResult.Failed()) {
117 return;
118 }
119
120 mCurrentNode = &aNode;
121 }
122
123 /*
124 * nsIDOMTreeWalker functions
125 */
126
127 /* nsIDOMNode parentNode (); */
128 NS_IMETHODIMP TreeWalker::ParentNode(nsIDOMNode **_retval)
129 {
130 return ImplNodeGetter(&TreeWalker::ParentNode, _retval);
131 }
132
133 already_AddRefed<nsINode>
134 TreeWalker::ParentNode(ErrorResult& aResult)
135 {
136 nsCOMPtr<nsINode> node = mCurrentNode;
137
138 while (node && node != mRoot) {
139 node = node->GetParentNode();
140
141 if (node) {
142 int16_t filtered = TestNode(node, aResult);
143 if (aResult.Failed()) {
144 return nullptr;
145 }
146 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
147 mCurrentNode = node;
148 return node.forget();
149 }
150 }
151 }
152
153 return nullptr;
154 }
155
156 /* nsIDOMNode firstChild (); */
157 NS_IMETHODIMP TreeWalker::FirstChild(nsIDOMNode **_retval)
158 {
159 return ImplNodeGetter(&TreeWalker::FirstChild, _retval);
160 }
161
162 already_AddRefed<nsINode>
163 TreeWalker::FirstChild(ErrorResult& aResult)
164 {
165 return FirstChildInternal(false, aResult);
166 }
167
168 /* nsIDOMNode lastChild (); */
169 NS_IMETHODIMP TreeWalker::LastChild(nsIDOMNode **_retval)
170 {
171 return ImplNodeGetter(&TreeWalker::LastChild, _retval);
172 }
173
174 already_AddRefed<nsINode>
175 TreeWalker::LastChild(ErrorResult& aResult)
176 {
177 return FirstChildInternal(true, aResult);
178 }
179
180 /* nsIDOMNode previousSibling (); */
181 NS_IMETHODIMP TreeWalker::PreviousSibling(nsIDOMNode **_retval)
182 {
183 return ImplNodeGetter(&TreeWalker::PreviousSibling, _retval);
184 }
185
186 already_AddRefed<nsINode>
187 TreeWalker::PreviousSibling(ErrorResult& aResult)
188 {
189 return NextSiblingInternal(true, aResult);
190 }
191
192 /* nsIDOMNode nextSibling (); */
193 NS_IMETHODIMP TreeWalker::NextSibling(nsIDOMNode **_retval)
194 {
195 return ImplNodeGetter(&TreeWalker::NextSibling, _retval);
196 }
197
198 already_AddRefed<nsINode>
199 TreeWalker::NextSibling(ErrorResult& aResult)
200 {
201 return NextSiblingInternal(false, aResult);
202 }
203
204 /* nsIDOMNode previousNode (); */
205 NS_IMETHODIMP TreeWalker::PreviousNode(nsIDOMNode **_retval)
206 {
207 return ImplNodeGetter(&TreeWalker::PreviousNode, _retval);
208 }
209
210 already_AddRefed<nsINode>
211 TreeWalker::PreviousNode(ErrorResult& aResult)
212 {
213 nsCOMPtr<nsINode> node = mCurrentNode;
214
215 while (node != mRoot) {
216 while (nsINode *previousSibling = node->GetPreviousSibling()) {
217 node = previousSibling;
218
219 int16_t filtered = TestNode(node, aResult);
220 if (aResult.Failed()) {
221 return nullptr;
222 }
223
224 nsINode *lastChild;
225 while (filtered != nsIDOMNodeFilter::FILTER_REJECT &&
226 (lastChild = node->GetLastChild())) {
227 node = lastChild;
228 filtered = TestNode(node, aResult);
229 if (aResult.Failed()) {
230 return nullptr;
231 }
232 }
233
234 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
235 mCurrentNode = node;
236 return node.forget();
237 }
238 }
239
240 if (node == mRoot) {
241 break;
242 }
243
244 node = node->GetParentNode();
245 if (!node) {
246 break;
247 }
248
249 int16_t filtered = TestNode(node, aResult);
250 if (aResult.Failed()) {
251 return nullptr;
252 }
253
254 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
255 mCurrentNode = node;
256 return node.forget();
257 }
258 }
259
260 return nullptr;
261 }
262
263 /* nsIDOMNode nextNode (); */
264 NS_IMETHODIMP TreeWalker::NextNode(nsIDOMNode **_retval)
265 {
266 return ImplNodeGetter(&TreeWalker::NextNode, _retval);
267 }
268
269 already_AddRefed<nsINode>
270 TreeWalker::NextNode(ErrorResult& aResult)
271 {
272 int16_t filtered = nsIDOMNodeFilter::FILTER_ACCEPT; // pre-init for inner loop
273
274 nsCOMPtr<nsINode> node = mCurrentNode;
275
276 while (1) {
277
278 nsINode *firstChild;
279 while (filtered != nsIDOMNodeFilter::FILTER_REJECT &&
280 (firstChild = node->GetFirstChild())) {
281 node = firstChild;
282
283 filtered = TestNode(node, aResult);
284 if (aResult.Failed()) {
285 return nullptr;
286 }
287
288 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
289 // Node found
290 mCurrentNode = node;
291 return node.forget();
292 }
293 }
294
295 nsINode *sibling = nullptr;
296 nsINode *temp = node;
297 do {
298 if (temp == mRoot)
299 break;
300
301 sibling = temp->GetNextSibling();
302 if (sibling)
303 break;
304
305 temp = temp->GetParentNode();
306 } while (temp);
307
308 if (!sibling)
309 break;
310
311 node = sibling;
312
313 // Found a sibling. Either ours or ancestor's
314 filtered = TestNode(node, aResult);
315 if (aResult.Failed()) {
316 return nullptr;
317 }
318
319 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
320 // Node found
321 mCurrentNode = node;
322 return node.forget();
323 }
324 }
325
326 return nullptr;
327 }
328
329 /*
330 * TreeWalker helper functions
331 */
332
333 /*
334 * Implements FirstChild and LastChild which only vary in which direction
335 * they search.
336 * @param aReversed Controls whether we search forwards or backwards
337 * @param aResult Whether we threw or not.
338 * @returns The desired node. Null if no child is found
339 */
340 already_AddRefed<nsINode>
341 TreeWalker::FirstChildInternal(bool aReversed, ErrorResult& aResult)
342 {
343 nsCOMPtr<nsINode> node = aReversed ? mCurrentNode->GetLastChild()
344 : mCurrentNode->GetFirstChild();
345
346 while (node) {
347 int16_t filtered = TestNode(node, aResult);
348 if (aResult.Failed()) {
349 return nullptr;
350 }
351
352 switch (filtered) {
353 case nsIDOMNodeFilter::FILTER_ACCEPT:
354 // Node found
355 mCurrentNode = node;
356 return node.forget();
357 case nsIDOMNodeFilter::FILTER_SKIP: {
358 nsINode *child = aReversed ? node->GetLastChild()
359 : node->GetFirstChild();
360 if (child) {
361 node = child;
362 continue;
363 }
364 break;
365 }
366 case nsIDOMNodeFilter::FILTER_REJECT:
367 // Keep searching
368 break;
369 }
370
371 do {
372 nsINode *sibling = aReversed ? node->GetPreviousSibling()
373 : node->GetNextSibling();
374 if (sibling) {
375 node = sibling;
376 break;
377 }
378
379 nsINode *parent = node->GetParentNode();
380
381 if (!parent || parent == mRoot || parent == mCurrentNode) {
382 return nullptr;
383 }
384
385 node = parent;
386
387 } while (node);
388 }
389
390 return nullptr;
391 }
392
393 /*
394 * Implements NextSibling and PreviousSibling which only vary in which
395 * direction they search.
396 * @param aReversed Controls whether we search forwards or backwards
397 * @param aResult Whether we threw or not.
398 * @returns The desired node. Null if no child is found
399 */
400 already_AddRefed<nsINode>
401 TreeWalker::NextSiblingInternal(bool aReversed, ErrorResult& aResult)
402 {
403 nsCOMPtr<nsINode> node = mCurrentNode;
404
405 if (node == mRoot) {
406 return nullptr;
407 }
408
409 while (1) {
410 nsINode* sibling = aReversed ? node->GetPreviousSibling()
411 : node->GetNextSibling();
412
413 while (sibling) {
414 node = sibling;
415
416 int16_t filtered = TestNode(node, aResult);
417 if (aResult.Failed()) {
418 return nullptr;
419 }
420
421 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
422 // Node found
423 mCurrentNode = node;
424 return node.forget();
425 }
426
427 // If rejected or no children, try a sibling
428 if (filtered == nsIDOMNodeFilter::FILTER_REJECT ||
429 !(sibling = aReversed ? node->GetLastChild()
430 : node->GetFirstChild())) {
431 sibling = aReversed ? node->GetPreviousSibling()
432 : node->GetNextSibling();
433 }
434 }
435
436 node = node->GetParentNode();
437
438 if (!node || node == mRoot) {
439 return nullptr;
440 }
441
442 // Is parent transparent in filtered view?
443 int16_t filtered = TestNode(node, aResult);
444 if (aResult.Failed()) {
445 return nullptr;
446 }
447 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
448 return nullptr;
449 }
450 }
451 }
452
453 JSObject*
454 TreeWalker::WrapObject(JSContext *cx)
455 {
456 return TreeWalkerBinding::Wrap(cx, this);
457 }
458
459 } // namespace dom
460 } // namespace mozilla

mercurial