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 */ }
33 * nsISupports and cycle collection stuff
36 NS_IMPL_CYCLE_COLLECTION(TreeWalker
, mFilter
, mCurrentNode
, mRoot
)
38 // QueryInterface implementation for TreeWalker
39 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TreeWalker
)
40 NS_INTERFACE_MAP_ENTRY(nsISupports
)
43 // Have to pass in dom::TreeWalker because a11y has an a11y::TreeWalker that
44 // passes TreeWalker so refcount logging would get confused on the name
46 NS_IMPL_CYCLE_COLLECTING_ADDREF(dom::TreeWalker
)
47 NS_IMPL_CYCLE_COLLECTING_RELEASE(dom::TreeWalker
)
49 void TreeWalker::SetCurrentNode(nsINode
& aNode
, ErrorResult
& aResult
) {
50 aResult
= nsContentUtils::CheckSameOrigin(mRoot
, &aNode
);
51 if (aResult
.Failed()) {
55 mCurrentNode
= &aNode
;
58 already_AddRefed
<nsINode
> TreeWalker::ParentNode(ErrorResult
& aResult
) {
59 nsCOMPtr
<nsINode
> node
= mCurrentNode
;
61 while (node
&& node
!= mRoot
) {
62 node
= node
->GetParentNode();
65 int16_t filtered
= TestNode(node
, aResult
);
66 if (aResult
.Failed()) {
69 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
79 already_AddRefed
<nsINode
> TreeWalker::FirstChild(ErrorResult
& aResult
) {
80 return FirstChildInternal(false, aResult
);
83 already_AddRefed
<nsINode
> TreeWalker::LastChild(ErrorResult
& aResult
) {
84 return FirstChildInternal(true, aResult
);
87 already_AddRefed
<nsINode
> TreeWalker::PreviousSibling(ErrorResult
& aResult
) {
88 return NextSiblingInternal(true, aResult
);
91 already_AddRefed
<nsINode
> TreeWalker::NextSibling(ErrorResult
& aResult
) {
92 return NextSiblingInternal(false, aResult
);
95 already_AddRefed
<nsINode
> TreeWalker::PreviousNode(ErrorResult
& aResult
) {
96 nsCOMPtr
<nsINode
> node
= mCurrentNode
;
98 while (node
!= mRoot
) {
99 while (nsINode
* previousSibling
= node
->GetPreviousSibling()) {
100 node
= previousSibling
;
102 int16_t filtered
= TestNode(node
, aResult
);
103 if (aResult
.Failed()) {
108 while (filtered
!= NodeFilter_Binding::FILTER_REJECT
&&
109 (lastChild
= node
->GetLastChild())) {
111 filtered
= TestNode(node
, aResult
);
112 if (aResult
.Failed()) {
117 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
119 return node
.forget();
127 node
= node
->GetParentNode();
132 int16_t filtered
= TestNode(node
, aResult
);
133 if (aResult
.Failed()) {
137 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
139 return node
.forget();
146 already_AddRefed
<nsINode
> TreeWalker::NextNode(ErrorResult
& aResult
) {
148 NodeFilter_Binding::FILTER_ACCEPT
; // pre-init for inner loop
150 nsCOMPtr
<nsINode
> node
= mCurrentNode
;
154 while (filtered
!= NodeFilter_Binding::FILTER_REJECT
&&
155 (firstChild
= node
->GetFirstChild())) {
158 filtered
= TestNode(node
, aResult
);
159 if (aResult
.Failed()) {
163 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
166 return node
.forget();
170 nsINode
* sibling
= nullptr;
171 nsINode
* temp
= node
;
173 if (temp
== mRoot
) break;
175 sibling
= temp
->GetNextSibling();
178 temp
= temp
->GetParentNode();
185 // Found a sibling. Either ours or ancestor's
186 filtered
= TestNode(node
, aResult
);
187 if (aResult
.Failed()) {
191 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
194 return node
.forget();
202 * TreeWalker helper functions
206 * Implements FirstChild and LastChild which only vary in which direction
208 * @param aReversed Controls whether we search forwards or backwards
209 * @param aResult Whether we threw or not.
210 * @returns The desired node. Null if no child is found
212 already_AddRefed
<nsINode
> TreeWalker::FirstChildInternal(bool aReversed
,
213 ErrorResult
& aResult
) {
214 nsCOMPtr
<nsINode
> node
=
215 aReversed
? mCurrentNode
->GetLastChild() : mCurrentNode
->GetFirstChild();
218 int16_t filtered
= TestNode(node
, aResult
);
219 if (aResult
.Failed()) {
224 case NodeFilter_Binding::FILTER_ACCEPT
:
227 return node
.forget();
228 case NodeFilter_Binding::FILTER_SKIP
: {
230 aReversed
? node
->GetLastChild() : node
->GetFirstChild();
237 case NodeFilter_Binding::FILTER_REJECT
:
244 aReversed
? node
->GetPreviousSibling() : node
->GetNextSibling();
250 nsINode
* parent
= node
->GetParentNode();
252 if (!parent
|| parent
== mRoot
|| parent
== mCurrentNode
) {
265 * Implements NextSibling and PreviousSibling which only vary in which
266 * direction they search.
267 * @param aReversed Controls whether we search forwards or backwards
268 * @param aResult Whether we threw or not.
269 * @returns The desired node. Null if no child is found
271 already_AddRefed
<nsINode
> TreeWalker::NextSiblingInternal(
272 bool aReversed
, ErrorResult
& aResult
) {
273 nsCOMPtr
<nsINode
> node
= mCurrentNode
;
281 aReversed
? node
->GetPreviousSibling() : node
->GetNextSibling();
286 int16_t filtered
= TestNode(node
, aResult
);
287 if (aResult
.Failed()) {
291 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
294 return node
.forget();
297 // If rejected or no children, try a sibling
298 if (filtered
== NodeFilter_Binding::FILTER_REJECT
||
300 aReversed
? node
->GetLastChild() : node
->GetFirstChild())) {
302 aReversed
? node
->GetPreviousSibling() : node
->GetNextSibling();
306 node
= node
->GetParentNode();
308 if (!node
|| node
== mRoot
) {
312 // Is parent transparent in filtered view?
313 int16_t filtered
= TestNode(node
, aResult
);
314 if (aResult
.Failed()) {
317 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
323 bool TreeWalker::WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
,
324 JS::MutableHandle
<JSObject
*> aReflector
) {
325 return TreeWalker_Binding::Wrap(aCx
, this, aGivenProto
, aReflector
);
328 } // namespace mozilla::dom