Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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/. */
7 /*
8 * Implementation of DOM Traversal's nsIDOMTreeWalker
9 */
11 #include "mozilla/dom/TreeWalker.h"
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"
20 namespace mozilla {
21 namespace dom {
23 /*
24 * Factories, constructors and destructors
25 */
27 TreeWalker::TreeWalker(nsINode *aRoot,
28 uint32_t aWhatToShow,
29 const NodeFilterHolder &aFilter) :
30 nsTraversal(aRoot, aWhatToShow, aFilter),
31 mCurrentNode(aRoot)
32 {
33 }
35 TreeWalker::~TreeWalker()
36 {
37 /* destructor code */
38 }
40 /*
41 * nsISupports and cycle collection stuff
42 */
44 NS_IMPL_CYCLE_COLLECTION(TreeWalker, mFilter, mCurrentNode, mRoot)
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
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)
60 /*
61 * nsIDOMTreeWalker Getters/Setters
62 */
64 /* readonly attribute nsIDOMNode root; */
65 NS_IMETHODIMP TreeWalker::GetRoot(nsIDOMNode * *aRoot)
66 {
67 NS_ADDREF(*aRoot = Root()->AsDOMNode());
68 return NS_OK;
69 }
71 /* readonly attribute unsigned long whatToShow; */
72 NS_IMETHODIMP TreeWalker::GetWhatToShow(uint32_t *aWhatToShow)
73 {
74 *aWhatToShow = WhatToShow();
75 return NS_OK;
76 }
78 /* readonly attribute nsIDOMNodeFilter filter; */
79 NS_IMETHODIMP TreeWalker::GetFilter(nsIDOMNodeFilter * *aFilter)
80 {
81 NS_ENSURE_ARG_POINTER(aFilter);
83 *aFilter = mFilter.ToXPCOMCallback().take();
85 return NS_OK;
86 }
88 /* attribute nsIDOMNode currentNode; */
89 NS_IMETHODIMP TreeWalker::GetCurrentNode(nsIDOMNode * *aCurrentNode)
90 {
91 if (mCurrentNode) {
92 return CallQueryInterface(mCurrentNode, aCurrentNode);
93 }
95 *aCurrentNode = nullptr;
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);
104 nsCOMPtr<nsINode> node = do_QueryInterface(aCurrentNode);
105 NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
107 ErrorResult rv;
108 SetCurrentNode(*node, rv);
109 return rv.ErrorCode();
110 }
112 void
113 TreeWalker::SetCurrentNode(nsINode& aNode, ErrorResult& aResult)
114 {
115 aResult = nsContentUtils::CheckSameOrigin(mRoot, &aNode);
116 if (aResult.Failed()) {
117 return;
118 }
120 mCurrentNode = &aNode;
121 }
123 /*
124 * nsIDOMTreeWalker functions
125 */
127 /* nsIDOMNode parentNode (); */
128 NS_IMETHODIMP TreeWalker::ParentNode(nsIDOMNode **_retval)
129 {
130 return ImplNodeGetter(&TreeWalker::ParentNode, _retval);
131 }
133 already_AddRefed<nsINode>
134 TreeWalker::ParentNode(ErrorResult& aResult)
135 {
136 nsCOMPtr<nsINode> node = mCurrentNode;
138 while (node && node != mRoot) {
139 node = node->GetParentNode();
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 }
153 return nullptr;
154 }
156 /* nsIDOMNode firstChild (); */
157 NS_IMETHODIMP TreeWalker::FirstChild(nsIDOMNode **_retval)
158 {
159 return ImplNodeGetter(&TreeWalker::FirstChild, _retval);
160 }
162 already_AddRefed<nsINode>
163 TreeWalker::FirstChild(ErrorResult& aResult)
164 {
165 return FirstChildInternal(false, aResult);
166 }
168 /* nsIDOMNode lastChild (); */
169 NS_IMETHODIMP TreeWalker::LastChild(nsIDOMNode **_retval)
170 {
171 return ImplNodeGetter(&TreeWalker::LastChild, _retval);
172 }
174 already_AddRefed<nsINode>
175 TreeWalker::LastChild(ErrorResult& aResult)
176 {
177 return FirstChildInternal(true, aResult);
178 }
180 /* nsIDOMNode previousSibling (); */
181 NS_IMETHODIMP TreeWalker::PreviousSibling(nsIDOMNode **_retval)
182 {
183 return ImplNodeGetter(&TreeWalker::PreviousSibling, _retval);
184 }
186 already_AddRefed<nsINode>
187 TreeWalker::PreviousSibling(ErrorResult& aResult)
188 {
189 return NextSiblingInternal(true, aResult);
190 }
192 /* nsIDOMNode nextSibling (); */
193 NS_IMETHODIMP TreeWalker::NextSibling(nsIDOMNode **_retval)
194 {
195 return ImplNodeGetter(&TreeWalker::NextSibling, _retval);
196 }
198 already_AddRefed<nsINode>
199 TreeWalker::NextSibling(ErrorResult& aResult)
200 {
201 return NextSiblingInternal(false, aResult);
202 }
204 /* nsIDOMNode previousNode (); */
205 NS_IMETHODIMP TreeWalker::PreviousNode(nsIDOMNode **_retval)
206 {
207 return ImplNodeGetter(&TreeWalker::PreviousNode, _retval);
208 }
210 already_AddRefed<nsINode>
211 TreeWalker::PreviousNode(ErrorResult& aResult)
212 {
213 nsCOMPtr<nsINode> node = mCurrentNode;
215 while (node != mRoot) {
216 while (nsINode *previousSibling = node->GetPreviousSibling()) {
217 node = previousSibling;
219 int16_t filtered = TestNode(node, aResult);
220 if (aResult.Failed()) {
221 return nullptr;
222 }
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 }
234 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
235 mCurrentNode = node;
236 return node.forget();
237 }
238 }
240 if (node == mRoot) {
241 break;
242 }
244 node = node->GetParentNode();
245 if (!node) {
246 break;
247 }
249 int16_t filtered = TestNode(node, aResult);
250 if (aResult.Failed()) {
251 return nullptr;
252 }
254 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
255 mCurrentNode = node;
256 return node.forget();
257 }
258 }
260 return nullptr;
261 }
263 /* nsIDOMNode nextNode (); */
264 NS_IMETHODIMP TreeWalker::NextNode(nsIDOMNode **_retval)
265 {
266 return ImplNodeGetter(&TreeWalker::NextNode, _retval);
267 }
269 already_AddRefed<nsINode>
270 TreeWalker::NextNode(ErrorResult& aResult)
271 {
272 int16_t filtered = nsIDOMNodeFilter::FILTER_ACCEPT; // pre-init for inner loop
274 nsCOMPtr<nsINode> node = mCurrentNode;
276 while (1) {
278 nsINode *firstChild;
279 while (filtered != nsIDOMNodeFilter::FILTER_REJECT &&
280 (firstChild = node->GetFirstChild())) {
281 node = firstChild;
283 filtered = TestNode(node, aResult);
284 if (aResult.Failed()) {
285 return nullptr;
286 }
288 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
289 // Node found
290 mCurrentNode = node;
291 return node.forget();
292 }
293 }
295 nsINode *sibling = nullptr;
296 nsINode *temp = node;
297 do {
298 if (temp == mRoot)
299 break;
301 sibling = temp->GetNextSibling();
302 if (sibling)
303 break;
305 temp = temp->GetParentNode();
306 } while (temp);
308 if (!sibling)
309 break;
311 node = sibling;
313 // Found a sibling. Either ours or ancestor's
314 filtered = TestNode(node, aResult);
315 if (aResult.Failed()) {
316 return nullptr;
317 }
319 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
320 // Node found
321 mCurrentNode = node;
322 return node.forget();
323 }
324 }
326 return nullptr;
327 }
329 /*
330 * TreeWalker helper functions
331 */
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();
346 while (node) {
347 int16_t filtered = TestNode(node, aResult);
348 if (aResult.Failed()) {
349 return nullptr;
350 }
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 }
371 do {
372 nsINode *sibling = aReversed ? node->GetPreviousSibling()
373 : node->GetNextSibling();
374 if (sibling) {
375 node = sibling;
376 break;
377 }
379 nsINode *parent = node->GetParentNode();
381 if (!parent || parent == mRoot || parent == mCurrentNode) {
382 return nullptr;
383 }
385 node = parent;
387 } while (node);
388 }
390 return nullptr;
391 }
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;
405 if (node == mRoot) {
406 return nullptr;
407 }
409 while (1) {
410 nsINode* sibling = aReversed ? node->GetPreviousSibling()
411 : node->GetNextSibling();
413 while (sibling) {
414 node = sibling;
416 int16_t filtered = TestNode(node, aResult);
417 if (aResult.Failed()) {
418 return nullptr;
419 }
421 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
422 // Node found
423 mCurrentNode = node;
424 return node.forget();
425 }
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 }
436 node = node->GetParentNode();
438 if (!node || node == mRoot) {
439 return nullptr;
440 }
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 }
453 JSObject*
454 TreeWalker::WrapObject(JSContext *cx)
455 {
456 return TreeWalkerBinding::Wrap(cx, this);
457 }
459 } // namespace dom
460 } // namespace mozilla