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