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/. */
8 * Implementation of DOM Traversal's nsIDOMTreeWalker
11 #include "mozilla/dom/TreeWalker.h"
13 #include "nsIContent.h"
14 #include "nsIDOMNode.h"
17 #include "nsContentUtils.h"
18 #include "mozilla/dom/TreeWalkerBinding.h"
24 * Factories, constructors and destructors
27 TreeWalker::TreeWalker(nsINode
*aRoot
,
29 const NodeFilterHolder
&aFilter
) :
30 nsTraversal(aRoot
, aWhatToShow
, aFilter
),
35 TreeWalker::~TreeWalker()
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
)
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
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());
71 /* readonly attribute unsigned long whatToShow; */
72 NS_IMETHODIMP
TreeWalker::GetWhatToShow(uint32_t *aWhatToShow
)
74 *aWhatToShow
= WhatToShow();
78 /* readonly attribute nsIDOMNodeFilter filter; */
79 NS_IMETHODIMP
TreeWalker::GetFilter(nsIDOMNodeFilter
* *aFilter
)
81 NS_ENSURE_ARG_POINTER(aFilter
);
83 *aFilter
= mFilter
.ToXPCOMCallback().take();
88 /* attribute nsIDOMNode currentNode; */
89 NS_IMETHODIMP
TreeWalker::GetCurrentNode(nsIDOMNode
* *aCurrentNode
)
92 return CallQueryInterface(mCurrentNode
, aCurrentNode
);
95 *aCurrentNode
= nullptr;
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
);
108 SetCurrentNode(*node
, rv
);
109 return rv
.ErrorCode();
113 TreeWalker::SetCurrentNode(nsINode
& aNode
, ErrorResult
& aResult
)
115 aResult
= nsContentUtils::CheckSameOrigin(mRoot
, &aNode
);
116 if (aResult
.Failed()) {
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();
142 int16_t filtered
= TestNode(node
, aResult
);
143 if (aResult
.Failed()) {
146 if (filtered
== nsIDOMNodeFilter::FILTER_ACCEPT
) {
148 return node
.forget();
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()) {
225 while (filtered
!= nsIDOMNodeFilter::FILTER_REJECT
&&
226 (lastChild
= node
->GetLastChild())) {
228 filtered
= TestNode(node
, aResult
);
229 if (aResult
.Failed()) {
234 if (filtered
== nsIDOMNodeFilter::FILTER_ACCEPT
) {
236 return node
.forget();
244 node
= node
->GetParentNode();
249 int16_t filtered
= TestNode(node
, aResult
);
250 if (aResult
.Failed()) {
254 if (filtered
== nsIDOMNodeFilter::FILTER_ACCEPT
) {
256 return node
.forget();
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
;
279 while (filtered
!= nsIDOMNodeFilter::FILTER_REJECT
&&
280 (firstChild
= node
->GetFirstChild())) {
283 filtered
= TestNode(node
, aResult
);
284 if (aResult
.Failed()) {
288 if (filtered
== nsIDOMNodeFilter::FILTER_ACCEPT
) {
291 return node
.forget();
295 nsINode
*sibling
= nullptr;
296 nsINode
*temp
= node
;
301 sibling
= temp
->GetNextSibling();
305 temp
= temp
->GetParentNode();
313 // Found a sibling. Either ours or ancestor's
314 filtered
= TestNode(node
, aResult
);
315 if (aResult
.Failed()) {
319 if (filtered
== nsIDOMNodeFilter::FILTER_ACCEPT
) {
322 return node
.forget();
330 * TreeWalker helper functions
334 * Implements FirstChild and LastChild which only vary in which direction
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();
347 int16_t filtered
= TestNode(node
, aResult
);
348 if (aResult
.Failed()) {
353 case nsIDOMNodeFilter::FILTER_ACCEPT
:
356 return node
.forget();
357 case nsIDOMNodeFilter::FILTER_SKIP
: {
358 nsINode
*child
= aReversed
? node
->GetLastChild()
359 : node
->GetFirstChild();
366 case nsIDOMNodeFilter::FILTER_REJECT
:
372 nsINode
*sibling
= aReversed
? node
->GetPreviousSibling()
373 : node
->GetNextSibling();
379 nsINode
*parent
= node
->GetParentNode();
381 if (!parent
|| parent
== mRoot
|| parent
== mCurrentNode
) {
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
;
410 nsINode
* sibling
= aReversed
? node
->GetPreviousSibling()
411 : node
->GetNextSibling();
416 int16_t filtered
= TestNode(node
, aResult
);
417 if (aResult
.Failed()) {
421 if (filtered
== nsIDOMNodeFilter::FILTER_ACCEPT
) {
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
) {
442 // Is parent transparent in filtered view?
443 int16_t filtered
= TestNode(node
, aResult
);
444 if (aResult
.Failed()) {
447 if (filtered
== nsIDOMNodeFilter::FILTER_ACCEPT
) {
454 TreeWalker::WrapObject(JSContext
*cx
)
456 return TreeWalkerBinding::Wrap(cx
, this);
460 } // namespace mozilla