1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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 NodeIterator
11 #include "mozilla/dom/NodeIterator.h"
15 #include "nsIContent.h"
16 #include "mozilla/dom/Document.h"
17 #include "nsContentUtils.h"
19 #include "mozilla/dom/NodeFilterBinding.h"
20 #include "mozilla/dom/NodeIteratorBinding.h"
22 namespace mozilla::dom
{
25 * NodePointer implementation
27 NodeIterator::NodePointer::NodePointer(nsINode
* aNode
, bool aBeforeNode
)
28 : mNode(aNode
), mBeforeNode(aBeforeNode
) {}
30 bool NodeIterator::NodePointer::MoveToNext(nsINode
* aRoot
) {
31 if (!mNode
) return false;
38 nsINode
* child
= mNode
->GetFirstChild();
44 return MoveForward(aRoot
, mNode
);
47 bool NodeIterator::NodePointer::MoveToPrevious(nsINode
* aRoot
) {
48 if (!mNode
) return false;
55 if (mNode
== aRoot
) return false;
57 MoveBackward(mNode
->GetParentNode(), mNode
->GetPreviousSibling());
62 void NodeIterator::NodePointer::AdjustAfterRemoval(
63 nsINode
* aRoot
, nsINode
* aContainer
, nsIContent
* aChild
,
64 nsIContent
* aPreviousSibling
) {
65 // If mNode is null or the root there is nothing to do.
66 if (!mNode
|| mNode
== aRoot
) return;
68 // check if ancestor was removed
69 if (!mNode
->IsInclusiveDescendantOf(aChild
)) return;
72 // Try the next sibling
73 nsINode
* nextSibling
= aPreviousSibling
? aPreviousSibling
->GetNextSibling()
74 : aContainer
->GetFirstChild();
81 // Next try siblings of ancestors
82 if (MoveForward(aRoot
, aContainer
)) return;
84 // No suitable node was found so try going backwards
88 MoveBackward(aContainer
, aPreviousSibling
);
91 bool NodeIterator::NodePointer::MoveForward(nsINode
* aRoot
, nsINode
* aNode
) {
93 if (aNode
== aRoot
) break;
95 nsINode
* sibling
= aNode
->GetNextSibling();
100 aNode
= aNode
->GetParentNode();
106 void NodeIterator::NodePointer::MoveBackward(nsINode
* aParent
, nsINode
* aNode
) {
110 aNode
= aNode
->GetLastChild();
118 * Factories, constructors and destructors
121 NodeIterator::NodeIterator(nsINode
* aRoot
, uint32_t aWhatToShow
,
123 : nsTraversal(aRoot
, aWhatToShow
, aFilter
), mPointer(mRoot
, true) {
124 aRoot
->AddMutationObserver(this);
127 NodeIterator::~NodeIterator() {
128 /* destructor code */
129 if (mRoot
) mRoot
->RemoveMutationObserver(this);
133 * nsISupports and cycle collection stuff
136 NS_IMPL_CYCLE_COLLECTION_CLASS(NodeIterator
)
138 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NodeIterator
)
139 if (tmp
->mRoot
) tmp
->mRoot
->RemoveMutationObserver(tmp
);
140 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot
)
141 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilter
)
142 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
143 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NodeIterator
)
144 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot
)
145 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilter
)
146 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
148 // QueryInterface implementation for NodeIterator
149 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NodeIterator
)
150 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
151 NS_INTERFACE_MAP_ENTRY(nsISupports
)
154 NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator
)
155 NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator
)
157 already_AddRefed
<nsINode
> NodeIterator::NextOrPrevNode(
158 NodePointer::MoveToMethodType aMove
, ErrorResult
& aResult
) {
160 aResult
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
164 mWorkingPointer
= mPointer
;
168 explicit AutoClear(NodePointer
* ptr
) : mPtr(ptr
) {}
169 ~AutoClear() { mPtr
->Clear(); }
170 } ac(&mWorkingPointer
);
172 while ((mWorkingPointer
.*aMove
)(mRoot
)) {
173 nsCOMPtr
<nsINode
> testNode
;
174 int16_t filtered
= TestNode(mWorkingPointer
.mNode
, aResult
, &testNode
);
175 if (aResult
.Failed()) {
179 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
180 mPointer
= mWorkingPointer
;
181 return testNode
.forget();
188 void NodeIterator::Detach() {
190 mRoot
->OwnerDoc()->WarnOnceAbout(DeprecatedOperations::eNodeIteratorDetach
);
195 * nsIMutationObserver interface
198 void NodeIterator::ContentRemoved(nsIContent
* aChild
,
199 nsIContent
* aPreviousSibling
) {
200 nsINode
* container
= aChild
->GetParentNode();
202 mPointer
.AdjustAfterRemoval(mRoot
, container
, aChild
, aPreviousSibling
);
203 mWorkingPointer
.AdjustAfterRemoval(mRoot
, container
, aChild
,
207 bool NodeIterator::WrapObject(JSContext
* cx
, JS::Handle
<JSObject
*> aGivenProto
,
208 JS::MutableHandle
<JSObject
*> aReflector
) {
209 return NodeIterator_Binding::Wrap(cx
, this, aGivenProto
, aReflector
);
212 } // namespace mozilla::dom