Bumping manifests a=b2g-bump
[gecko.git] / dom / base / TreeWalker.cpp
bloba75fd99c2b3c64612d344e2b07232433ee4c5a8b
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 {
24 * Factories, constructors and destructors
27 TreeWalker::TreeWalker(nsINode *aRoot,
28 uint32_t aWhatToShow,
29 const NodeFilterHolder &aFilter) :
30 nsTraversal(aRoot, aWhatToShow, aFilter),
31 mCurrentNode(aRoot)
35 TreeWalker::~TreeWalker()
37 /* destructor code */
41 * nsISupports and cycle collection stuff
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)
61 * nsIDOMTreeWalker Getters/Setters
64 /* readonly attribute nsIDOMNode root; */
65 NS_IMETHODIMP TreeWalker::GetRoot(nsIDOMNode * *aRoot)
67 NS_ADDREF(*aRoot = Root()->AsDOMNode());
68 return NS_OK;
71 /* readonly attribute unsigned long whatToShow; */
72 NS_IMETHODIMP TreeWalker::GetWhatToShow(uint32_t *aWhatToShow)
74 *aWhatToShow = WhatToShow();
75 return NS_OK;
78 /* readonly attribute nsIDOMNodeFilter filter; */
79 NS_IMETHODIMP TreeWalker::GetFilter(nsIDOMNodeFilter * *aFilter)
81 NS_ENSURE_ARG_POINTER(aFilter);
83 *aFilter = mFilter.ToXPCOMCallback().take();
85 return NS_OK;
88 /* attribute nsIDOMNode currentNode; */
89 NS_IMETHODIMP TreeWalker::GetCurrentNode(nsIDOMNode * *aCurrentNode)
91 if (mCurrentNode) {
92 return CallQueryInterface(mCurrentNode, aCurrentNode);
95 *aCurrentNode = nullptr;
97 return NS_OK;
99 NS_IMETHODIMP TreeWalker::SetCurrentNode(nsIDOMNode * aCurrentNode)
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();
112 void
113 TreeWalker::SetCurrentNode(nsINode& aNode, ErrorResult& aResult)
115 aResult = nsContentUtils::CheckSameOrigin(mRoot, &aNode);
116 if (aResult.Failed()) {
117 return;
120 mCurrentNode = &aNode;
124 * nsIDOMTreeWalker functions
127 /* nsIDOMNode parentNode (); */
128 NS_IMETHODIMP TreeWalker::ParentNode(nsIDOMNode **_retval)
130 return ImplNodeGetter(&TreeWalker::ParentNode, _retval);
133 already_AddRefed<nsINode>
134 TreeWalker::ParentNode(ErrorResult& aResult)
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;
146 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
147 mCurrentNode = node;
148 return node.forget();
153 return nullptr;
156 /* nsIDOMNode firstChild (); */
157 NS_IMETHODIMP TreeWalker::FirstChild(nsIDOMNode **_retval)
159 return ImplNodeGetter(&TreeWalker::FirstChild, _retval);
162 already_AddRefed<nsINode>
163 TreeWalker::FirstChild(ErrorResult& aResult)
165 return FirstChildInternal(false, aResult);
168 /* nsIDOMNode lastChild (); */
169 NS_IMETHODIMP TreeWalker::LastChild(nsIDOMNode **_retval)
171 return ImplNodeGetter(&TreeWalker::LastChild, _retval);
174 already_AddRefed<nsINode>
175 TreeWalker::LastChild(ErrorResult& aResult)
177 return FirstChildInternal(true, aResult);
180 /* nsIDOMNode previousSibling (); */
181 NS_IMETHODIMP TreeWalker::PreviousSibling(nsIDOMNode **_retval)
183 return ImplNodeGetter(&TreeWalker::PreviousSibling, _retval);
186 already_AddRefed<nsINode>
187 TreeWalker::PreviousSibling(ErrorResult& aResult)
189 return NextSiblingInternal(true, aResult);
192 /* nsIDOMNode nextSibling (); */
193 NS_IMETHODIMP TreeWalker::NextSibling(nsIDOMNode **_retval)
195 return ImplNodeGetter(&TreeWalker::NextSibling, _retval);
198 already_AddRefed<nsINode>
199 TreeWalker::NextSibling(ErrorResult& aResult)
201 return NextSiblingInternal(false, aResult);
204 /* nsIDOMNode previousNode (); */
205 NS_IMETHODIMP TreeWalker::PreviousNode(nsIDOMNode **_retval)
207 return ImplNodeGetter(&TreeWalker::PreviousNode, _retval);
210 already_AddRefed<nsINode>
211 TreeWalker::PreviousNode(ErrorResult& aResult)
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;
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;
234 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
235 mCurrentNode = node;
236 return node.forget();
240 if (node == mRoot) {
241 break;
244 node = node->GetParentNode();
245 if (!node) {
246 break;
249 int16_t filtered = TestNode(node, aResult);
250 if (aResult.Failed()) {
251 return nullptr;
254 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
255 mCurrentNode = node;
256 return node.forget();
260 return nullptr;
263 /* nsIDOMNode nextNode (); */
264 NS_IMETHODIMP TreeWalker::NextNode(nsIDOMNode **_retval)
266 return ImplNodeGetter(&TreeWalker::NextNode, _retval);
269 already_AddRefed<nsINode>
270 TreeWalker::NextNode(ErrorResult& aResult)
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;
288 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
289 // Node found
290 mCurrentNode = node;
291 return node.forget();
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;
319 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
320 // Node found
321 mCurrentNode = node;
322 return node.forget();
326 return nullptr;
330 * TreeWalker helper functions
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
340 already_AddRefed<nsINode>
341 TreeWalker::FirstChildInternal(bool aReversed, ErrorResult& aResult)
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;
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;
364 break;
366 case nsIDOMNodeFilter::FILTER_REJECT:
367 // Keep searching
368 break;
371 do {
372 nsINode *sibling = aReversed ? node->GetPreviousSibling()
373 : node->GetNextSibling();
374 if (sibling) {
375 node = sibling;
376 break;
379 nsINode *parent = node->GetParentNode();
381 if (!parent || parent == mRoot || parent == mCurrentNode) {
382 return nullptr;
385 node = parent;
387 } while (node);
390 return nullptr;
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
400 already_AddRefed<nsINode>
401 TreeWalker::NextSiblingInternal(bool aReversed, ErrorResult& aResult)
403 nsCOMPtr<nsINode> node = mCurrentNode;
405 if (node == mRoot) {
406 return nullptr;
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;
421 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
422 // Node found
423 mCurrentNode = node;
424 return node.forget();
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();
436 node = node->GetParentNode();
438 if (!node || node == mRoot) {
439 return nullptr;
442 // Is parent transparent in filtered view?
443 int16_t filtered = TestNode(node, aResult);
444 if (aResult.Failed()) {
445 return nullptr;
447 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
448 return nullptr;
453 JSObject*
454 TreeWalker::WrapObject(JSContext *cx)
456 return TreeWalkerBinding::Wrap(cx, this);
459 } // namespace dom
460 } // namespace mozilla