1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=4 et sw=2 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 TreeWalker
11 #include "mozilla/dom/TreeWalker.h"
13 #include "nsIContent.h"
16 #include "nsContentUtils.h"
17 #include "mozilla/dom/NodeFilterBinding.h"
18 #include "mozilla/dom/TreeWalkerBinding.h"
20 namespace mozilla::dom
{
23 * Factories, constructors and destructors
26 TreeWalker::TreeWalker(nsINode
* aRoot
, uint32_t aWhatToShow
,
28 : nsTraversal(aRoot
, aWhatToShow
, aFilter
), mCurrentNode(aRoot
) {}
30 TreeWalker::~TreeWalker() { /* destructor code */
34 * nsISupports and cycle collection stuff
37 NS_IMPL_CYCLE_COLLECTION(TreeWalker
, mFilter
, mCurrentNode
, mRoot
)
39 // QueryInterface implementation for TreeWalker
40 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TreeWalker
)
41 NS_INTERFACE_MAP_ENTRY(nsISupports
)
44 // Have to pass in dom::TreeWalker because a11y has an a11y::TreeWalker that
45 // passes TreeWalker so refcount logging would get confused on the name
47 NS_IMPL_CYCLE_COLLECTING_ADDREF(dom::TreeWalker
)
48 NS_IMPL_CYCLE_COLLECTING_RELEASE(dom::TreeWalker
)
50 void TreeWalker::SetCurrentNode(nsINode
& aNode
, ErrorResult
& aResult
) {
51 aResult
= nsContentUtils::CheckSameOrigin(mRoot
, &aNode
);
52 if (aResult
.Failed()) {
56 mCurrentNode
= &aNode
;
59 already_AddRefed
<nsINode
> TreeWalker::ParentNode(ErrorResult
& aResult
) {
60 nsCOMPtr
<nsINode
> node
= mCurrentNode
;
62 while (node
&& node
!= mRoot
) {
63 node
= node
->GetParentNode();
66 int16_t filtered
= TestNode(node
, aResult
);
67 if (aResult
.Failed()) {
70 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
80 already_AddRefed
<nsINode
> TreeWalker::FirstChild(ErrorResult
& aResult
) {
81 return FirstChildInternal(false, aResult
);
84 already_AddRefed
<nsINode
> TreeWalker::LastChild(ErrorResult
& aResult
) {
85 return FirstChildInternal(true, aResult
);
88 already_AddRefed
<nsINode
> TreeWalker::PreviousSibling(ErrorResult
& aResult
) {
89 return NextSiblingInternal(true, aResult
);
92 already_AddRefed
<nsINode
> TreeWalker::NextSibling(ErrorResult
& aResult
) {
93 return NextSiblingInternal(false, aResult
);
96 already_AddRefed
<nsINode
> TreeWalker::PreviousNode(ErrorResult
& aResult
) {
97 nsCOMPtr
<nsINode
> node
= mCurrentNode
;
99 while (node
!= mRoot
) {
100 while (nsINode
* previousSibling
= node
->GetPreviousSibling()) {
101 node
= previousSibling
;
103 int16_t filtered
= TestNode(node
, aResult
);
104 if (aResult
.Failed()) {
109 while (filtered
!= NodeFilter_Binding::FILTER_REJECT
&&
110 (lastChild
= node
->GetLastChild())) {
112 filtered
= TestNode(node
, aResult
);
113 if (aResult
.Failed()) {
118 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
120 return node
.forget();
128 node
= node
->GetParentNode();
133 int16_t filtered
= TestNode(node
, aResult
);
134 if (aResult
.Failed()) {
138 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
140 return node
.forget();
147 already_AddRefed
<nsINode
> TreeWalker::NextNode(ErrorResult
& aResult
) {
149 NodeFilter_Binding::FILTER_ACCEPT
; // pre-init for inner loop
151 nsCOMPtr
<nsINode
> node
= mCurrentNode
;
155 while (filtered
!= NodeFilter_Binding::FILTER_REJECT
&&
156 (firstChild
= node
->GetFirstChild())) {
159 filtered
= TestNode(node
, aResult
);
160 if (aResult
.Failed()) {
164 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
167 return node
.forget();
171 nsINode
* sibling
= nullptr;
172 nsINode
* temp
= node
;
174 if (temp
== mRoot
) break;
176 sibling
= temp
->GetNextSibling();
179 temp
= temp
->GetParentNode();
186 // Found a sibling. Either ours or ancestor's
187 filtered
= TestNode(node
, aResult
);
188 if (aResult
.Failed()) {
192 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
195 return node
.forget();
203 * TreeWalker helper functions
207 * Implements FirstChild and LastChild which only vary in which direction
209 * @param aReversed Controls whether we search forwards or backwards
210 * @param aResult Whether we threw or not.
211 * @returns The desired node. Null if no child is found
213 already_AddRefed
<nsINode
> TreeWalker::FirstChildInternal(bool aReversed
,
214 ErrorResult
& aResult
) {
215 nsCOMPtr
<nsINode
> node
=
216 aReversed
? mCurrentNode
->GetLastChild() : mCurrentNode
->GetFirstChild();
219 int16_t filtered
= TestNode(node
, aResult
);
220 if (aResult
.Failed()) {
225 case NodeFilter_Binding::FILTER_ACCEPT
:
228 return node
.forget();
229 case NodeFilter_Binding::FILTER_SKIP
: {
231 aReversed
? node
->GetLastChild() : node
->GetFirstChild();
238 case NodeFilter_Binding::FILTER_REJECT
:
245 aReversed
? node
->GetPreviousSibling() : node
->GetNextSibling();
251 nsINode
* parent
= node
->GetParentNode();
253 if (!parent
|| parent
== mRoot
|| parent
== mCurrentNode
) {
266 * Implements NextSibling and PreviousSibling which only vary in which
267 * direction they search.
268 * @param aReversed Controls whether we search forwards or backwards
269 * @param aResult Whether we threw or not.
270 * @returns The desired node. Null if no child is found
272 already_AddRefed
<nsINode
> TreeWalker::NextSiblingInternal(
273 bool aReversed
, ErrorResult
& aResult
) {
274 nsCOMPtr
<nsINode
> node
= mCurrentNode
;
282 aReversed
? node
->GetPreviousSibling() : node
->GetNextSibling();
287 int16_t filtered
= TestNode(node
, aResult
);
288 if (aResult
.Failed()) {
292 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
295 return node
.forget();
298 // If rejected or no children, try a sibling
299 if (filtered
== NodeFilter_Binding::FILTER_REJECT
||
301 aReversed
? node
->GetLastChild() : node
->GetFirstChild())) {
303 aReversed
? node
->GetPreviousSibling() : node
->GetNextSibling();
307 node
= node
->GetParentNode();
309 if (!node
|| node
== mRoot
) {
313 // Is parent transparent in filtered view?
314 int16_t filtered
= TestNode(node
, aResult
);
315 if (aResult
.Failed()) {
318 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
324 bool TreeWalker::WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
,
325 JS::MutableHandle
<JSObject
*> aReflector
) {
326 return TreeWalker_Binding::Wrap(aCx
, this, aGivenProto
, aReflector
);
329 } // namespace mozilla::dom