Backed out changeset 58dbd2146e24 (bug 944961) for bustage.
[gecko.git] / content / base / src / NodeIterator.cpp
bloba5590f5d241fedc6746bc4025bc60d1044efd1db
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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/. */
7 /*
8 * Implementation of DOM Traversal's nsIDOMNodeIterator
9 */
11 #include "mozilla/dom/NodeIterator.h"
13 #include "nsIDOMNode.h"
14 #include "nsError.h"
16 #include "nsIContent.h"
17 #include "nsIDocument.h"
18 #include "nsContentUtils.h"
19 #include "nsCOMPtr.h"
20 #include "mozilla/dom/NodeIteratorBinding.h"
22 namespace mozilla {
23 namespace dom {
26 * NodePointer implementation
28 NodeIterator::NodePointer::NodePointer(nsINode *aNode, bool aBeforeNode) :
29 mNode(aNode),
30 mBeforeNode(aBeforeNode)
34 bool NodeIterator::NodePointer::MoveToNext(nsINode *aRoot)
36 if (!mNode)
37 return false;
39 if (mBeforeNode) {
40 mBeforeNode = false;
41 return true;
44 nsINode* child = mNode->GetFirstChild();
45 if (child) {
46 mNode = child;
47 return true;
50 return MoveForward(aRoot, mNode);
53 bool NodeIterator::NodePointer::MoveToPrevious(nsINode *aRoot)
55 if (!mNode)
56 return false;
58 if (!mBeforeNode) {
59 mBeforeNode = true;
60 return true;
63 if (mNode == aRoot)
64 return false;
66 MoveBackward(mNode->GetParentNode(), mNode->GetPreviousSibling());
68 return true;
71 void NodeIterator::NodePointer::AdjustAfterRemoval(nsINode *aRoot,
72 nsINode *aContainer,
73 nsIContent *aChild,
74 nsIContent *aPreviousSibling)
76 // If mNode is null or the root there is nothing to do.
77 if (!mNode || mNode == aRoot)
78 return;
80 // check if ancestor was removed
81 if (!nsContentUtils::ContentIsDescendantOf(mNode, aChild))
82 return;
84 if (mBeforeNode) {
86 // Try the next sibling
87 nsINode *nextSibling = aPreviousSibling ? aPreviousSibling->GetNextSibling()
88 : aContainer->GetFirstChild();
90 if (nextSibling) {
91 mNode = nextSibling;
92 return;
95 // Next try siblings of ancestors
96 if (MoveForward(aRoot, aContainer))
97 return;
99 // No suitable node was found so try going backwards
100 mBeforeNode = false;
103 MoveBackward(aContainer, aPreviousSibling);
106 bool NodeIterator::NodePointer::MoveForward(nsINode *aRoot, nsINode *aNode)
108 while (1) {
109 if (aNode == aRoot)
110 break;
112 nsINode *sibling = aNode->GetNextSibling();
113 if (sibling) {
114 mNode = sibling;
115 return true;
117 aNode = aNode->GetParentNode();
120 return false;
123 void NodeIterator::NodePointer::MoveBackward(nsINode *aParent, nsINode *aNode)
125 if (aNode) {
126 do {
127 mNode = aNode;
128 aNode = aNode->GetLastChild();
129 } while (aNode);
130 } else {
131 mNode = aParent;
136 * Factories, constructors and destructors
139 NodeIterator::NodeIterator(nsINode *aRoot,
140 uint32_t aWhatToShow,
141 const NodeFilterHolder &aFilter) :
142 nsTraversal(aRoot, aWhatToShow, aFilter),
143 mPointer(mRoot, true)
145 aRoot->AddMutationObserver(this);
148 NodeIterator::~NodeIterator()
150 /* destructor code */
151 if (mRoot)
152 mRoot->RemoveMutationObserver(this);
156 * nsISupports and cycle collection stuff
159 NS_IMPL_CYCLE_COLLECTION_CLASS(NodeIterator)
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
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
179 NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator)
180 NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator)
182 /* readonly attribute nsIDOMNode root; */
183 NS_IMETHODIMP NodeIterator::GetRoot(nsIDOMNode * *aRoot)
185 NS_ADDREF(*aRoot = Root()->AsDOMNode());
186 return NS_OK;
189 /* readonly attribute unsigned long whatToShow; */
190 NS_IMETHODIMP NodeIterator::GetWhatToShow(uint32_t *aWhatToShow)
192 *aWhatToShow = WhatToShow();
193 return NS_OK;
196 /* readonly attribute nsIDOMNodeFilter filter; */
197 NS_IMETHODIMP NodeIterator::GetFilter(nsIDOMNodeFilter **aFilter)
199 NS_ENSURE_ARG_POINTER(aFilter);
201 *aFilter = mFilter.ToXPCOMCallback().get();
203 return NS_OK;
206 /* nsIDOMNode nextNode () raises (DOMException); */
207 NS_IMETHODIMP NodeIterator::NextNode(nsIDOMNode **_retval)
209 return ImplNodeGetter(&NodeIterator::NextNode, _retval);
212 /* nsIDOMNode previousNode () raises (DOMException); */
213 NS_IMETHODIMP NodeIterator::PreviousNode(nsIDOMNode **_retval)
215 return ImplNodeGetter(&NodeIterator::PreviousNode, _retval);
218 already_AddRefed<nsINode>
219 NodeIterator::NextOrPrevNode(NodePointer::MoveToMethodType aMove,
220 ErrorResult& aResult)
222 if (mInAcceptNode) {
223 aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
224 return nullptr;
227 mWorkingPointer = mPointer;
229 struct AutoClear {
230 NodePointer* mPtr;
231 AutoClear(NodePointer* ptr) : mPtr(ptr) {}
232 ~AutoClear() { mPtr->Clear(); }
233 } ac(&mWorkingPointer);
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;
242 if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
243 mPointer = mWorkingPointer;
244 return testNode.forget();
248 return nullptr;
251 /* void detach (); */
252 NS_IMETHODIMP NodeIterator::Detach(void)
254 if (mRoot) {
255 mRoot->OwnerDoc()->WarnOnceAbout(nsIDocument::eNodeIteratorDetach);
257 return NS_OK;
260 /* readonly attribute nsIDOMNode referenceNode; */
261 NS_IMETHODIMP NodeIterator::GetReferenceNode(nsIDOMNode * *aRefNode)
263 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(GetReferenceNode()));
264 node.forget(aRefNode);
265 return NS_OK;
268 /* readonly attribute boolean pointerBeforeReferenceNode; */
269 NS_IMETHODIMP NodeIterator::GetPointerBeforeReferenceNode(bool *aBeforeNode)
271 *aBeforeNode = PointerBeforeReferenceNode();
272 return NS_OK;
276 * nsIMutationObserver interface
279 void NodeIterator::ContentRemoved(nsIDocument *aDocument,
280 nsIContent *aContainer,
281 nsIContent *aChild,
282 int32_t aIndexInContainer,
283 nsIContent *aPreviousSibling)
285 nsINode *container = NODE_FROM(aContainer, aDocument);
287 mPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling);
288 mWorkingPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling);
291 JSObject*
292 NodeIterator::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope)
294 return NodeIteratorBinding::Wrap(cx, scope, this);
297 } // namespace dom
298 } // namespace mozilla