summaryrefslogtreecommitdiffstats
path: root/dom/base/NodeIterator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/NodeIterator.cpp')
-rw-r--r--dom/base/NodeIterator.cpp212
1 files changed, 212 insertions, 0 deletions
diff --git a/dom/base/NodeIterator.cpp b/dom/base/NodeIterator.cpp
new file mode 100644
index 0000000000..146eae1da9
--- /dev/null
+++ b/dom/base/NodeIterator.cpp
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Implementation of DOM Traversal's NodeIterator
+ */
+
+#include "mozilla/dom/NodeIterator.h"
+
+#include "nsError.h"
+
+#include "nsIContent.h"
+#include "mozilla/dom/Document.h"
+#include "nsContentUtils.h"
+#include "nsCOMPtr.h"
+#include "mozilla/dom/NodeFilterBinding.h"
+#include "mozilla/dom/NodeIteratorBinding.h"
+
+namespace mozilla::dom {
+
+/*
+ * NodePointer implementation
+ */
+NodeIterator::NodePointer::NodePointer(nsINode* aNode, bool aBeforeNode)
+ : mNode(aNode), mBeforeNode(aBeforeNode) {}
+
+bool NodeIterator::NodePointer::MoveToNext(nsINode* aRoot) {
+ if (!mNode) return false;
+
+ if (mBeforeNode) {
+ mBeforeNode = false;
+ return true;
+ }
+
+ nsINode* child = mNode->GetFirstChild();
+ if (child) {
+ mNode = child;
+ return true;
+ }
+
+ return MoveForward(aRoot, mNode);
+}
+
+bool NodeIterator::NodePointer::MoveToPrevious(nsINode* aRoot) {
+ if (!mNode) return false;
+
+ if (!mBeforeNode) {
+ mBeforeNode = true;
+ return true;
+ }
+
+ if (mNode == aRoot) return false;
+
+ MoveBackward(mNode->GetParentNode(), mNode->GetPreviousSibling());
+
+ return true;
+}
+
+void NodeIterator::NodePointer::AdjustAfterRemoval(
+ nsINode* aRoot, nsINode* aContainer, nsIContent* aChild,
+ nsIContent* aPreviousSibling) {
+ // If mNode is null or the root there is nothing to do.
+ if (!mNode || mNode == aRoot) return;
+
+ // check if ancestor was removed
+ if (!mNode->IsInclusiveDescendantOf(aChild)) return;
+
+ if (mBeforeNode) {
+ // Try the next sibling
+ nsINode* nextSibling = aPreviousSibling ? aPreviousSibling->GetNextSibling()
+ : aContainer->GetFirstChild();
+
+ if (nextSibling) {
+ mNode = nextSibling;
+ return;
+ }
+
+ // Next try siblings of ancestors
+ if (MoveForward(aRoot, aContainer)) return;
+
+ // No suitable node was found so try going backwards
+ mBeforeNode = false;
+ }
+
+ MoveBackward(aContainer, aPreviousSibling);
+}
+
+bool NodeIterator::NodePointer::MoveForward(nsINode* aRoot, nsINode* aNode) {
+ while (1) {
+ if (aNode == aRoot) break;
+
+ nsINode* sibling = aNode->GetNextSibling();
+ if (sibling) {
+ mNode = sibling;
+ return true;
+ }
+ aNode = aNode->GetParentNode();
+ }
+
+ return false;
+}
+
+void NodeIterator::NodePointer::MoveBackward(nsINode* aParent, nsINode* aNode) {
+ if (aNode) {
+ do {
+ mNode = aNode;
+ aNode = aNode->GetLastChild();
+ } while (aNode);
+ } else {
+ mNode = aParent;
+ }
+}
+
+/*
+ * Factories, constructors and destructors
+ */
+
+NodeIterator::NodeIterator(nsINode* aRoot, uint32_t aWhatToShow,
+ NodeFilter* aFilter)
+ : nsTraversal(aRoot, aWhatToShow, aFilter), mPointer(mRoot, true) {
+ aRoot->AddMutationObserver(this);
+}
+
+NodeIterator::~NodeIterator() {
+ /* destructor code */
+ if (mRoot) mRoot->RemoveMutationObserver(this);
+}
+
+/*
+ * nsISupports and cycle collection stuff
+ */
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(NodeIterator)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NodeIterator)
+ if (tmp->mRoot) tmp->mRoot->RemoveMutationObserver(tmp);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilter)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NodeIterator)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilter)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+// QueryInterface implementation for NodeIterator
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NodeIterator)
+ NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator)
+
+already_AddRefed<nsINode> NodeIterator::NextOrPrevNode(
+ NodePointer::MoveToMethodType aMove, ErrorResult& aResult) {
+ if (mInAcceptNode) {
+ aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return nullptr;
+ }
+
+ mWorkingPointer = mPointer;
+
+ struct AutoClear {
+ NodePointer* mPtr;
+ explicit AutoClear(NodePointer* ptr) : mPtr(ptr) {}
+ ~AutoClear() { mPtr->Clear(); }
+ } ac(&mWorkingPointer);
+
+ while ((mWorkingPointer.*aMove)(mRoot)) {
+ nsCOMPtr<nsINode> testNode;
+ int16_t filtered = TestNode(mWorkingPointer.mNode, aResult, &testNode);
+ if (aResult.Failed()) {
+ return nullptr;
+ }
+
+ if (filtered == NodeFilter_Binding::FILTER_ACCEPT) {
+ mPointer = mWorkingPointer;
+ return testNode.forget();
+ }
+ }
+
+ return nullptr;
+}
+
+void NodeIterator::Detach() {
+ if (mRoot) {
+ mRoot->OwnerDoc()->WarnOnceAbout(DeprecatedOperations::eNodeIteratorDetach);
+ }
+}
+
+/*
+ * nsIMutationObserver interface
+ */
+
+void NodeIterator::ContentRemoved(nsIContent* aChild,
+ nsIContent* aPreviousSibling) {
+ nsINode* container = aChild->GetParentNode();
+
+ mPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling);
+ mWorkingPointer.AdjustAfterRemoval(mRoot, container, aChild,
+ aPreviousSibling);
+}
+
+bool NodeIterator::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto,
+ JS::MutableHandle<JSObject*> aReflector) {
+ return NodeIterator_Binding::Wrap(cx, this, aGivenProto, aReflector);
+}
+
+} // namespace mozilla::dom