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