1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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/. */
6
7 /*
8 * Implementation of DOM Traversal's NodeIterator
9 */
10
11 #include "mozilla/dom/NodeIterator.h"
12
13 #include "nsError.h"
14
15 #include "nsIContent.h"
16 #include "mozilla/dom/Document.h"
17 #include "nsContentUtils.h"
18 #include "nsCOMPtr.h"
19 #include "mozilla/dom/NodeFilterBinding.h"
20 #include "mozilla/dom/NodeIteratorBinding.h"
21
22 namespace mozilla {
23 namespace dom {
24
25 /*
26 * NodePointer implementation
27 */
NodePointer(nsINode * aNode,bool aBeforeNode)28 NodeIterator::NodePointer::NodePointer(nsINode* aNode, bool aBeforeNode)
29 : mNode(aNode), mBeforeNode(aBeforeNode) {}
30
MoveToNext(nsINode * aRoot)31 bool NodeIterator::NodePointer::MoveToNext(nsINode* aRoot) {
32 if (!mNode) return false;
33
34 if (mBeforeNode) {
35 mBeforeNode = false;
36 return true;
37 }
38
39 nsINode* child = mNode->GetFirstChild();
40 if (child) {
41 mNode = child;
42 return true;
43 }
44
45 return MoveForward(aRoot, mNode);
46 }
47
MoveToPrevious(nsINode * aRoot)48 bool NodeIterator::NodePointer::MoveToPrevious(nsINode* aRoot) {
49 if (!mNode) return false;
50
51 if (!mBeforeNode) {
52 mBeforeNode = true;
53 return true;
54 }
55
56 if (mNode == aRoot) return false;
57
58 MoveBackward(mNode->GetParentNode(), mNode->GetPreviousSibling());
59
60 return true;
61 }
62
AdjustAfterRemoval(nsINode * aRoot,nsINode * aContainer,nsIContent * aChild,nsIContent * aPreviousSibling)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;
68
69 // check if ancestor was removed
70 if (!mNode->IsInclusiveDescendantOf(aChild)) return;
71
72 if (mBeforeNode) {
73 // Try the next sibling
74 nsINode* nextSibling = aPreviousSibling ? aPreviousSibling->GetNextSibling()
75 : aContainer->GetFirstChild();
76
77 if (nextSibling) {
78 mNode = nextSibling;
79 return;
80 }
81
82 // Next try siblings of ancestors
83 if (MoveForward(aRoot, aContainer)) return;
84
85 // No suitable node was found so try going backwards
86 mBeforeNode = false;
87 }
88
89 MoveBackward(aContainer, aPreviousSibling);
90 }
91
MoveForward(nsINode * aRoot,nsINode * aNode)92 bool NodeIterator::NodePointer::MoveForward(nsINode* aRoot, nsINode* aNode) {
93 while (1) {
94 if (aNode == aRoot) break;
95
96 nsINode* sibling = aNode->GetNextSibling();
97 if (sibling) {
98 mNode = sibling;
99 return true;
100 }
101 aNode = aNode->GetParentNode();
102 }
103
104 return false;
105 }
106
MoveBackward(nsINode * aParent,nsINode * aNode)107 void NodeIterator::NodePointer::MoveBackward(nsINode* aParent, nsINode* aNode) {
108 if (aNode) {
109 do {
110 mNode = aNode;
111 aNode = aNode->GetLastChild();
112 } while (aNode);
113 } else {
114 mNode = aParent;
115 }
116 }
117
118 /*
119 * Factories, constructors and destructors
120 */
121
NodeIterator(nsINode * aRoot,uint32_t aWhatToShow,NodeFilter * aFilter)122 NodeIterator::NodeIterator(nsINode* aRoot, uint32_t aWhatToShow,
123 NodeFilter* aFilter)
124 : nsTraversal(aRoot, aWhatToShow, aFilter), mPointer(mRoot, true) {
125 aRoot->AddMutationObserver(this);
126 }
127
~NodeIterator()128 NodeIterator::~NodeIterator() {
129 /* destructor code */
130 if (mRoot) mRoot->RemoveMutationObserver(this);
131 }
132
133 /*
134 * nsISupports and cycle collection stuff
135 */
136
137 NS_IMPL_CYCLE_COLLECTION_CLASS(NodeIterator)
138
139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NodeIterator)
140 if (tmp->mRoot) tmp->mRoot->RemoveMutationObserver(tmp);
141 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilter)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
148
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)
153 NS_INTERFACE_MAP_END
154
155 NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator)
156 NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator)
157
158 already_AddRefed<nsINode> NodeIterator::NextOrPrevNode(
159 NodePointer::MoveToMethodType aMove, ErrorResult& aResult) {
160 if (mInAcceptNode) {
161 aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
162 return nullptr;
163 }
164
165 mWorkingPointer = mPointer;
166
167 struct AutoClear {
168 NodePointer* mPtr;
169 explicit AutoClear(NodePointer* ptr) : mPtr(ptr) {}
170 ~AutoClear() { mPtr->Clear(); }
171 } ac(&mWorkingPointer);
172
173 while ((mWorkingPointer.*aMove)(mRoot)) {
174 nsCOMPtr<nsINode> testNode;
175 int16_t filtered = TestNode(mWorkingPointer.mNode, aResult, &testNode);
176 if (aResult.Failed()) {
177 return nullptr;
178 }
179
180 if (filtered == NodeFilter_Binding::FILTER_ACCEPT) {
181 mPointer = mWorkingPointer;
182 return testNode.forget();
183 }
184 }
185
186 return nullptr;
187 }
188
Detach()189 void NodeIterator::Detach() {
190 if (mRoot) {
191 mRoot->OwnerDoc()->WarnOnceAbout(Document::eNodeIteratorDetach);
192 }
193 }
194
195 /*
196 * nsIMutationObserver interface
197 */
198
ContentRemoved(nsIContent * aChild,nsIContent * aPreviousSibling)199 void NodeIterator::ContentRemoved(nsIContent* aChild,
200 nsIContent* aPreviousSibling) {
201 nsINode* container = aChild->GetParentNode();
202
203 mPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling);
204 mWorkingPointer.AdjustAfterRemoval(mRoot, container, aChild,
205 aPreviousSibling);
206 }
207
WrapObject(JSContext * cx,JS::Handle<JSObject * > aGivenProto,JS::MutableHandle<JSObject * > aReflector)208 bool NodeIterator::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto,
209 JS::MutableHandle<JSObject*> aReflector) {
210 return NodeIterator_Binding::Wrap(cx, this, aGivenProto, aReflector);
211 }
212
213 } // namespace dom
214 } // namespace mozilla
215