|
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 nsIDOMNodeIterator |
|
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/NodeIteratorBinding.h" |
|
21 |
|
22 namespace mozilla { |
|
23 namespace dom { |
|
24 |
|
25 /* |
|
26 * NodePointer implementation |
|
27 */ |
|
28 NodeIterator::NodePointer::NodePointer(nsINode *aNode, bool aBeforeNode) : |
|
29 mNode(aNode), |
|
30 mBeforeNode(aBeforeNode) |
|
31 { |
|
32 } |
|
33 |
|
34 bool NodeIterator::NodePointer::MoveToNext(nsINode *aRoot) |
|
35 { |
|
36 if (!mNode) |
|
37 return false; |
|
38 |
|
39 if (mBeforeNode) { |
|
40 mBeforeNode = false; |
|
41 return true; |
|
42 } |
|
43 |
|
44 nsINode* child = mNode->GetFirstChild(); |
|
45 if (child) { |
|
46 mNode = child; |
|
47 return true; |
|
48 } |
|
49 |
|
50 return MoveForward(aRoot, mNode); |
|
51 } |
|
52 |
|
53 bool NodeIterator::NodePointer::MoveToPrevious(nsINode *aRoot) |
|
54 { |
|
55 if (!mNode) |
|
56 return false; |
|
57 |
|
58 if (!mBeforeNode) { |
|
59 mBeforeNode = true; |
|
60 return true; |
|
61 } |
|
62 |
|
63 if (mNode == aRoot) |
|
64 return false; |
|
65 |
|
66 MoveBackward(mNode->GetParentNode(), mNode->GetPreviousSibling()); |
|
67 |
|
68 return true; |
|
69 } |
|
70 |
|
71 void NodeIterator::NodePointer::AdjustAfterRemoval(nsINode *aRoot, |
|
72 nsINode *aContainer, |
|
73 nsIContent *aChild, |
|
74 nsIContent *aPreviousSibling) |
|
75 { |
|
76 // If mNode is null or the root there is nothing to do. |
|
77 if (!mNode || mNode == aRoot) |
|
78 return; |
|
79 |
|
80 // check if ancestor was removed |
|
81 if (!nsContentUtils::ContentIsDescendantOf(mNode, aChild)) |
|
82 return; |
|
83 |
|
84 if (mBeforeNode) { |
|
85 |
|
86 // Try the next sibling |
|
87 nsINode *nextSibling = aPreviousSibling ? aPreviousSibling->GetNextSibling() |
|
88 : aContainer->GetFirstChild(); |
|
89 |
|
90 if (nextSibling) { |
|
91 mNode = nextSibling; |
|
92 return; |
|
93 } |
|
94 |
|
95 // Next try siblings of ancestors |
|
96 if (MoveForward(aRoot, aContainer)) |
|
97 return; |
|
98 |
|
99 // No suitable node was found so try going backwards |
|
100 mBeforeNode = false; |
|
101 } |
|
102 |
|
103 MoveBackward(aContainer, aPreviousSibling); |
|
104 } |
|
105 |
|
106 bool NodeIterator::NodePointer::MoveForward(nsINode *aRoot, nsINode *aNode) |
|
107 { |
|
108 while (1) { |
|
109 if (aNode == aRoot) |
|
110 break; |
|
111 |
|
112 nsINode *sibling = aNode->GetNextSibling(); |
|
113 if (sibling) { |
|
114 mNode = sibling; |
|
115 return true; |
|
116 } |
|
117 aNode = aNode->GetParentNode(); |
|
118 } |
|
119 |
|
120 return false; |
|
121 } |
|
122 |
|
123 void NodeIterator::NodePointer::MoveBackward(nsINode *aParent, nsINode *aNode) |
|
124 { |
|
125 if (aNode) { |
|
126 do { |
|
127 mNode = aNode; |
|
128 aNode = aNode->GetLastChild(); |
|
129 } while (aNode); |
|
130 } else { |
|
131 mNode = aParent; |
|
132 } |
|
133 } |
|
134 |
|
135 /* |
|
136 * Factories, constructors and destructors |
|
137 */ |
|
138 |
|
139 NodeIterator::NodeIterator(nsINode *aRoot, |
|
140 uint32_t aWhatToShow, |
|
141 const NodeFilterHolder &aFilter) : |
|
142 nsTraversal(aRoot, aWhatToShow, aFilter), |
|
143 mPointer(mRoot, true) |
|
144 { |
|
145 aRoot->AddMutationObserver(this); |
|
146 } |
|
147 |
|
148 NodeIterator::~NodeIterator() |
|
149 { |
|
150 /* destructor code */ |
|
151 if (mRoot) |
|
152 mRoot->RemoveMutationObserver(this); |
|
153 } |
|
154 |
|
155 /* |
|
156 * nsISupports and cycle collection stuff |
|
157 */ |
|
158 |
|
159 NS_IMPL_CYCLE_COLLECTION_CLASS(NodeIterator) |
|
160 |
|
161 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NodeIterator) |
|
162 if (tmp->mRoot) |
|
163 tmp->mRoot->RemoveMutationObserver(tmp); |
|
164 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) |
|
165 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilter) |
|
166 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
167 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NodeIterator) |
|
168 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) |
|
169 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilter) |
|
170 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
171 |
|
172 // QueryInterface implementation for NodeIterator |
|
173 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NodeIterator) |
|
174 NS_INTERFACE_MAP_ENTRY(nsIDOMNodeIterator) |
|
175 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) |
|
176 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNodeIterator) |
|
177 NS_INTERFACE_MAP_END |
|
178 |
|
179 NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator) |
|
180 NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator) |
|
181 |
|
182 /* readonly attribute nsIDOMNode root; */ |
|
183 NS_IMETHODIMP NodeIterator::GetRoot(nsIDOMNode * *aRoot) |
|
184 { |
|
185 NS_ADDREF(*aRoot = Root()->AsDOMNode()); |
|
186 return NS_OK; |
|
187 } |
|
188 |
|
189 /* readonly attribute unsigned long whatToShow; */ |
|
190 NS_IMETHODIMP NodeIterator::GetWhatToShow(uint32_t *aWhatToShow) |
|
191 { |
|
192 *aWhatToShow = WhatToShow(); |
|
193 return NS_OK; |
|
194 } |
|
195 |
|
196 /* readonly attribute nsIDOMNodeFilter filter; */ |
|
197 NS_IMETHODIMP NodeIterator::GetFilter(nsIDOMNodeFilter **aFilter) |
|
198 { |
|
199 NS_ENSURE_ARG_POINTER(aFilter); |
|
200 |
|
201 *aFilter = mFilter.ToXPCOMCallback().take(); |
|
202 |
|
203 return NS_OK; |
|
204 } |
|
205 |
|
206 /* nsIDOMNode nextNode () raises (DOMException); */ |
|
207 NS_IMETHODIMP NodeIterator::NextNode(nsIDOMNode **_retval) |
|
208 { |
|
209 return ImplNodeGetter(&NodeIterator::NextNode, _retval); |
|
210 } |
|
211 |
|
212 /* nsIDOMNode previousNode () raises (DOMException); */ |
|
213 NS_IMETHODIMP NodeIterator::PreviousNode(nsIDOMNode **_retval) |
|
214 { |
|
215 return ImplNodeGetter(&NodeIterator::PreviousNode, _retval); |
|
216 } |
|
217 |
|
218 already_AddRefed<nsINode> |
|
219 NodeIterator::NextOrPrevNode(NodePointer::MoveToMethodType aMove, |
|
220 ErrorResult& aResult) |
|
221 { |
|
222 if (mInAcceptNode) { |
|
223 aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
|
224 return nullptr; |
|
225 } |
|
226 |
|
227 mWorkingPointer = mPointer; |
|
228 |
|
229 struct AutoClear { |
|
230 NodePointer* mPtr; |
|
231 AutoClear(NodePointer* ptr) : mPtr(ptr) {} |
|
232 ~AutoClear() { mPtr->Clear(); } |
|
233 } ac(&mWorkingPointer); |
|
234 |
|
235 while ((mWorkingPointer.*aMove)(mRoot)) { |
|
236 nsCOMPtr<nsINode> testNode = mWorkingPointer.mNode; |
|
237 int16_t filtered = TestNode(testNode, aResult); |
|
238 if (aResult.Failed()) { |
|
239 return nullptr; |
|
240 } |
|
241 |
|
242 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) { |
|
243 mPointer = mWorkingPointer; |
|
244 return testNode.forget(); |
|
245 } |
|
246 } |
|
247 |
|
248 return nullptr; |
|
249 } |
|
250 |
|
251 /* void detach (); */ |
|
252 NS_IMETHODIMP NodeIterator::Detach(void) |
|
253 { |
|
254 if (mRoot) { |
|
255 mRoot->OwnerDoc()->WarnOnceAbout(nsIDocument::eNodeIteratorDetach); |
|
256 } |
|
257 return NS_OK; |
|
258 } |
|
259 |
|
260 /* readonly attribute nsIDOMNode referenceNode; */ |
|
261 NS_IMETHODIMP NodeIterator::GetReferenceNode(nsIDOMNode * *aRefNode) |
|
262 { |
|
263 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(GetReferenceNode())); |
|
264 node.forget(aRefNode); |
|
265 return NS_OK; |
|
266 } |
|
267 |
|
268 /* readonly attribute boolean pointerBeforeReferenceNode; */ |
|
269 NS_IMETHODIMP NodeIterator::GetPointerBeforeReferenceNode(bool *aBeforeNode) |
|
270 { |
|
271 *aBeforeNode = PointerBeforeReferenceNode(); |
|
272 return NS_OK; |
|
273 } |
|
274 |
|
275 /* |
|
276 * nsIMutationObserver interface |
|
277 */ |
|
278 |
|
279 void NodeIterator::ContentRemoved(nsIDocument *aDocument, |
|
280 nsIContent *aContainer, |
|
281 nsIContent *aChild, |
|
282 int32_t aIndexInContainer, |
|
283 nsIContent *aPreviousSibling) |
|
284 { |
|
285 nsINode *container = NODE_FROM(aContainer, aDocument); |
|
286 |
|
287 mPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling); |
|
288 mWorkingPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling); |
|
289 } |
|
290 |
|
291 JSObject* |
|
292 NodeIterator::WrapObject(JSContext *cx) |
|
293 { |
|
294 return NodeIteratorBinding::Wrap(cx, this); |
|
295 } |
|
296 |
|
297 } // namespace dom |
|
298 } // namespace mozilla |