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"
26 * NodePointer implementation
28 NodeIterator::NodePointer::NodePointer(nsINode
* aNode
, bool aBeforeNode
)
29 : mNode(aNode
), mBeforeNode(aBeforeNode
) {}
31 bool NodeIterator::NodePointer::MoveToNext(nsINode
* aRoot
) {
32 if (!mNode
) return false;
39 nsINode
* child
= mNode
->GetFirstChild();
45 return MoveForward(aRoot
, mNode
);
48 bool NodeIterator::NodePointer::MoveToPrevious(nsINode
* aRoot
) {
49 if (!mNode
) return false;
56 if (mNode
== aRoot
) return false;
58 MoveBackward(mNode
->GetParentNode(), mNode
->GetPreviousSibling());
63 void NodeIterator::NodePointer::AdjustAfterRemoval(
64 nsINode
* aRoot
, nsINode
* aContainer
, nsIContent
* aChild
,
65 nsIContent
* aPreviousSibling
) {
66 // If mNode is null or the root there is nothing to do.
67 if (!mNode
|| mNode
== aRoot
) return;
69 // check if ancestor was removed
70 if (!mNode
->IsInclusiveDescendantOf(aChild
)) return;
73 // Try the next sibling
74 nsINode
* nextSibling
= aPreviousSibling
? aPreviousSibling
->GetNextSibling()
75 : aContainer
->GetFirstChild();
82 // Next try siblings of ancestors
83 if (MoveForward(aRoot
, aContainer
)) return;
85 // No suitable node was found so try going backwards
89 MoveBackward(aContainer
, aPreviousSibling
);
92 bool NodeIterator::NodePointer::MoveForward(nsINode
* aRoot
, nsINode
* aNode
) {
94 if (aNode
== aRoot
) break;
96 nsINode
* sibling
= aNode
->GetNextSibling();
101 aNode
= aNode
->GetParentNode();
107 void NodeIterator::NodePointer::MoveBackward(nsINode
* aParent
, nsINode
* aNode
) {
111 aNode
= aNode
->GetLastChild();
119 * Factories, constructors and destructors
122 NodeIterator::NodeIterator(nsINode
* aRoot
, uint32_t aWhatToShow
,
124 : nsTraversal(aRoot
, aWhatToShow
, aFilter
), mPointer(mRoot
, true) {
125 aRoot
->AddMutationObserver(this);
128 NodeIterator::~NodeIterator() {
129 /* destructor code */
130 if (mRoot
) mRoot
->RemoveMutationObserver(this);
134 * nsISupports and cycle collection stuff
137 NS_IMPL_CYCLE_COLLECTION_CLASS(NodeIterator
)
139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NodeIterator
)
140 if (tmp
->mRoot
) tmp
->mRoot
->RemoveMutationObserver(tmp
);
141 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot
)
142 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilter
)
143 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
144 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NodeIterator
)
145 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot
)
146 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilter
)
147 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
149 // QueryInterface implementation for NodeIterator
150 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NodeIterator
)
151 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
152 NS_INTERFACE_MAP_ENTRY(nsISupports
)
155 NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator
)
156 NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator
)
158 already_AddRefed
<nsINode
> NodeIterator::NextOrPrevNode(
159 NodePointer::MoveToMethodType aMove
, ErrorResult
& aResult
) {
161 aResult
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
165 mWorkingPointer
= mPointer
;
169 explicit AutoClear(NodePointer
* ptr
) : mPtr(ptr
) {}
170 ~AutoClear() { mPtr
->Clear(); }
171 } ac(&mWorkingPointer
);
173 while ((mWorkingPointer
.*aMove
)(mRoot
)) {
174 nsCOMPtr
<nsINode
> testNode
;
175 int16_t filtered
= TestNode(mWorkingPointer
.mNode
, aResult
, &testNode
);
176 if (aResult
.Failed()) {
180 if (filtered
== NodeFilter_Binding::FILTER_ACCEPT
) {
181 mPointer
= mWorkingPointer
;
182 return testNode
.forget();
189 void NodeIterator::Detach() {
191 mRoot
->OwnerDoc()->WarnOnceAbout(Document::eNodeIteratorDetach
);
196 * nsIMutationObserver interface
199 void NodeIterator::ContentRemoved(nsIContent
* aChild
,
200 nsIContent
* aPreviousSibling
) {
201 nsINode
* container
= aChild
->GetParentNode();
203 mPointer
.AdjustAfterRemoval(mRoot
, container
, aChild
, aPreviousSibling
);
204 mWorkingPointer
.AdjustAfterRemoval(mRoot
, container
, aChild
,
208 bool NodeIterator::WrapObject(JSContext
* cx
, JS::Handle
<JSObject
*> aGivenProto
,
209 JS::MutableHandle
<JSObject
*> aReflector
) {
210 return NodeIterator_Binding::Wrap(cx
, this, aGivenProto
, aReflector
);
214 } // namespace mozilla