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.cpp291
1 files changed, 291 insertions, 0 deletions
diff --git a/dom/base/NodeIterator.cpp b/dom/base/NodeIterator.cpp
new file mode 100644
index 000000000..0973528ca
--- /dev/null
+++ b/dom/base/NodeIterator.cpp
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 nsIDOMNodeIterator
+ */
+
+#include "mozilla/dom/NodeIterator.h"
+
+#include "nsIDOMNode.h"
+#include "nsError.h"
+
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsContentUtils.h"
+#include "nsCOMPtr.h"
+#include "mozilla/dom/NodeIteratorBinding.h"
+
+namespace mozilla {
+namespace 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 (!nsContentUtils::ContentIsDescendantOf(mNode, 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,
+ NodeFilterHolder aFilter) :
+ nsTraversal(aRoot, aWhatToShow, Move(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(nsIDOMNodeIterator)
+ NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNodeIterator)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator)
+
+NS_IMETHODIMP NodeIterator::GetRoot(nsIDOMNode * *aRoot)
+{
+ nsCOMPtr<nsIDOMNode> root = Root()->AsDOMNode();
+ root.forget(aRoot);
+ return NS_OK;
+}
+
+NS_IMETHODIMP NodeIterator::GetWhatToShow(uint32_t *aWhatToShow)
+{
+ *aWhatToShow = WhatToShow();
+ return NS_OK;
+}
+
+NS_IMETHODIMP NodeIterator::GetFilter(nsIDOMNodeFilter **aFilter)
+{
+ NS_ENSURE_ARG_POINTER(aFilter);
+
+ *aFilter = mFilter.ToXPCOMCallback().take();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP NodeIterator::NextNode(nsIDOMNode **_retval)
+{
+ return ImplNodeGetter(&NodeIterator::NextNode, _retval);
+}
+
+NS_IMETHODIMP NodeIterator::PreviousNode(nsIDOMNode **_retval)
+{
+ return ImplNodeGetter(&NodeIterator::PreviousNode, _retval);
+}
+
+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 = mWorkingPointer.mNode;
+ int16_t filtered = TestNode(testNode, aResult);
+ if (aResult.Failed()) {
+ return nullptr;
+ }
+
+ if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
+ mPointer = mWorkingPointer;
+ return testNode.forget();
+ }
+ }
+
+ return nullptr;
+}
+
+NS_IMETHODIMP NodeIterator::Detach(void)
+{
+ if (mRoot) {
+ mRoot->OwnerDoc()->WarnOnceAbout(nsIDocument::eNodeIteratorDetach);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP NodeIterator::GetReferenceNode(nsIDOMNode * *aRefNode)
+{
+ nsCOMPtr<nsIDOMNode> node(do_QueryInterface(GetReferenceNode()));
+ node.forget(aRefNode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP NodeIterator::GetPointerBeforeReferenceNode(bool *aBeforeNode)
+{
+ *aBeforeNode = PointerBeforeReferenceNode();
+ return NS_OK;
+}
+
+/*
+ * nsIMutationObserver interface
+ */
+
+void NodeIterator::ContentRemoved(nsIDocument *aDocument,
+ nsIContent *aContainer,
+ nsIContent *aChild,
+ int32_t aIndexInContainer,
+ nsIContent *aPreviousSibling)
+{
+ nsINode *container = NODE_FROM(aContainer, aDocument);
+
+ 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 NodeIteratorBinding::Wrap(cx, this, aGivenProto, aReflector);
+}
+
+} // namespace dom
+} // namespace mozilla