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