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