Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / NodeIterator.cpp
blob146eae1da94f29d4432c96a6d52a1abf9a9a53ec
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 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/. */
7 /*
8 * Implementation of DOM Traversal's NodeIterator
9 */
11 #include "mozilla/dom/NodeIterator.h"
13 #include "nsError.h"
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"
22 namespace mozilla::dom {
25 * NodePointer implementation
27 NodeIterator::NodePointer::NodePointer(nsINode* aNode, bool aBeforeNode)
28 : mNode(aNode), mBeforeNode(aBeforeNode) {}
30 bool NodeIterator::NodePointer::MoveToNext(nsINode* aRoot) {
31 if (!mNode) return false;
33 if (mBeforeNode) {
34 mBeforeNode = false;
35 return true;
38 nsINode* child = mNode->GetFirstChild();
39 if (child) {
40 mNode = child;
41 return true;
44 return MoveForward(aRoot, mNode);
47 bool NodeIterator::NodePointer::MoveToPrevious(nsINode* aRoot) {
48 if (!mNode) return false;
50 if (!mBeforeNode) {
51 mBeforeNode = true;
52 return true;
55 if (mNode == aRoot) return false;
57 MoveBackward(mNode->GetParentNode(), mNode->GetPreviousSibling());
59 return true;
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;
68 // check if ancestor was removed
69 if (!mNode->IsInclusiveDescendantOf(aChild)) return;
71 if (mBeforeNode) {
72 // Try the next sibling
73 nsINode* nextSibling = aPreviousSibling ? aPreviousSibling->GetNextSibling()
74 : aContainer->GetFirstChild();
76 if (nextSibling) {
77 mNode = nextSibling;
78 return;
81 // Next try siblings of ancestors
82 if (MoveForward(aRoot, aContainer)) return;
84 // No suitable node was found so try going backwards
85 mBeforeNode = false;
88 MoveBackward(aContainer, aPreviousSibling);
91 bool NodeIterator::NodePointer::MoveForward(nsINode* aRoot, nsINode* aNode) {
92 while (1) {
93 if (aNode == aRoot) break;
95 nsINode* sibling = aNode->GetNextSibling();
96 if (sibling) {
97 mNode = sibling;
98 return true;
100 aNode = aNode->GetParentNode();
103 return false;
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;
118 * Factories, constructors and destructors
121 NodeIterator::NodeIterator(nsINode* aRoot, uint32_t aWhatToShow,
122 NodeFilter* aFilter)
123 : nsTraversal(aRoot, aWhatToShow, aFilter), mPointer(mRoot, true) {
124 aRoot->AddMutationObserver(this);
127 NodeIterator::~NodeIterator() {
128 /* destructor code */
129 if (mRoot) mRoot->RemoveMutationObserver(this);
133 * nsISupports and cycle collection stuff
136 NS_IMPL_CYCLE_COLLECTION_CLASS(NodeIterator)
138 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NodeIterator)
139 if (tmp->mRoot) tmp->mRoot->RemoveMutationObserver(tmp);
140 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
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
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
154 NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator)
155 NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator)
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;
164 mWorkingPointer = mPointer;
166 struct AutoClear {
167 NodePointer* mPtr;
168 explicit AutoClear(NodePointer* ptr) : mPtr(ptr) {}
169 ~AutoClear() { mPtr->Clear(); }
170 } ac(&mWorkingPointer);
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;
179 if (filtered == NodeFilter_Binding::FILTER_ACCEPT) {
180 mPointer = mWorkingPointer;
181 return testNode.forget();
185 return nullptr;
188 void NodeIterator::Detach() {
189 if (mRoot) {
190 mRoot->OwnerDoc()->WarnOnceAbout(DeprecatedOperations::eNodeIteratorDetach);
195 * nsIMutationObserver interface
198 void NodeIterator::ContentRemoved(nsIContent* aChild,
199 nsIContent* aPreviousSibling) {
200 nsINode* container = aChild->GetParentNode();
202 mPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling);
203 mWorkingPointer.AdjustAfterRemoval(mRoot, container, aChild,
204 aPreviousSibling);
207 bool NodeIterator::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto,
208 JS::MutableHandle<JSObject*> aReflector) {
209 return NodeIterator_Binding::Wrap(cx, this, aGivenProto, aReflector);
212 } // namespace mozilla::dom