michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Implementation of DOM Traversal's nsIDOMNodeIterator michael@0: */ michael@0: michael@0: #include "mozilla/dom/NodeIterator.h" michael@0: michael@0: #include "nsIDOMNode.h" michael@0: #include "nsError.h" michael@0: michael@0: #include "nsIContent.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "mozilla/dom/NodeIteratorBinding.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: /* michael@0: * NodePointer implementation michael@0: */ michael@0: NodeIterator::NodePointer::NodePointer(nsINode *aNode, bool aBeforeNode) : michael@0: mNode(aNode), michael@0: mBeforeNode(aBeforeNode) michael@0: { michael@0: } michael@0: michael@0: bool NodeIterator::NodePointer::MoveToNext(nsINode *aRoot) michael@0: { michael@0: if (!mNode) michael@0: return false; michael@0: michael@0: if (mBeforeNode) { michael@0: mBeforeNode = false; michael@0: return true; michael@0: } michael@0: michael@0: nsINode* child = mNode->GetFirstChild(); michael@0: if (child) { michael@0: mNode = child; michael@0: return true; michael@0: } michael@0: michael@0: return MoveForward(aRoot, mNode); michael@0: } michael@0: michael@0: bool NodeIterator::NodePointer::MoveToPrevious(nsINode *aRoot) michael@0: { michael@0: if (!mNode) michael@0: return false; michael@0: michael@0: if (!mBeforeNode) { michael@0: mBeforeNode = true; michael@0: return true; michael@0: } michael@0: michael@0: if (mNode == aRoot) michael@0: return false; michael@0: michael@0: MoveBackward(mNode->GetParentNode(), mNode->GetPreviousSibling()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void NodeIterator::NodePointer::AdjustAfterRemoval(nsINode *aRoot, michael@0: nsINode *aContainer, michael@0: nsIContent *aChild, michael@0: nsIContent *aPreviousSibling) michael@0: { michael@0: // If mNode is null or the root there is nothing to do. michael@0: if (!mNode || mNode == aRoot) michael@0: return; michael@0: michael@0: // check if ancestor was removed michael@0: if (!nsContentUtils::ContentIsDescendantOf(mNode, aChild)) michael@0: return; michael@0: michael@0: if (mBeforeNode) { michael@0: michael@0: // Try the next sibling michael@0: nsINode *nextSibling = aPreviousSibling ? aPreviousSibling->GetNextSibling() michael@0: : aContainer->GetFirstChild(); michael@0: michael@0: if (nextSibling) { michael@0: mNode = nextSibling; michael@0: return; michael@0: } michael@0: michael@0: // Next try siblings of ancestors michael@0: if (MoveForward(aRoot, aContainer)) michael@0: return; michael@0: michael@0: // No suitable node was found so try going backwards michael@0: mBeforeNode = false; michael@0: } michael@0: michael@0: MoveBackward(aContainer, aPreviousSibling); michael@0: } michael@0: michael@0: bool NodeIterator::NodePointer::MoveForward(nsINode *aRoot, nsINode *aNode) michael@0: { michael@0: while (1) { michael@0: if (aNode == aRoot) michael@0: break; michael@0: michael@0: nsINode *sibling = aNode->GetNextSibling(); michael@0: if (sibling) { michael@0: mNode = sibling; michael@0: return true; michael@0: } michael@0: aNode = aNode->GetParentNode(); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void NodeIterator::NodePointer::MoveBackward(nsINode *aParent, nsINode *aNode) michael@0: { michael@0: if (aNode) { michael@0: do { michael@0: mNode = aNode; michael@0: aNode = aNode->GetLastChild(); michael@0: } while (aNode); michael@0: } else { michael@0: mNode = aParent; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Factories, constructors and destructors michael@0: */ michael@0: michael@0: NodeIterator::NodeIterator(nsINode *aRoot, michael@0: uint32_t aWhatToShow, michael@0: const NodeFilterHolder &aFilter) : michael@0: nsTraversal(aRoot, aWhatToShow, aFilter), michael@0: mPointer(mRoot, true) michael@0: { michael@0: aRoot->AddMutationObserver(this); michael@0: } michael@0: michael@0: NodeIterator::~NodeIterator() michael@0: { michael@0: /* destructor code */ michael@0: if (mRoot) michael@0: mRoot->RemoveMutationObserver(this); michael@0: } michael@0: michael@0: /* michael@0: * nsISupports and cycle collection stuff michael@0: */ michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(NodeIterator) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NodeIterator) michael@0: if (tmp->mRoot) michael@0: tmp->mRoot->RemoveMutationObserver(tmp); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilter) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NodeIterator) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilter) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: // QueryInterface implementation for NodeIterator michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NodeIterator) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMNodeIterator) michael@0: NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNodeIterator) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator) michael@0: michael@0: /* readonly attribute nsIDOMNode root; */ michael@0: NS_IMETHODIMP NodeIterator::GetRoot(nsIDOMNode * *aRoot) michael@0: { michael@0: NS_ADDREF(*aRoot = Root()->AsDOMNode()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute unsigned long whatToShow; */ michael@0: NS_IMETHODIMP NodeIterator::GetWhatToShow(uint32_t *aWhatToShow) michael@0: { michael@0: *aWhatToShow = WhatToShow(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute nsIDOMNodeFilter filter; */ michael@0: NS_IMETHODIMP NodeIterator::GetFilter(nsIDOMNodeFilter **aFilter) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aFilter); michael@0: michael@0: *aFilter = mFilter.ToXPCOMCallback().take(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsIDOMNode nextNode () raises (DOMException); */ michael@0: NS_IMETHODIMP NodeIterator::NextNode(nsIDOMNode **_retval) michael@0: { michael@0: return ImplNodeGetter(&NodeIterator::NextNode, _retval); michael@0: } michael@0: michael@0: /* nsIDOMNode previousNode () raises (DOMException); */ michael@0: NS_IMETHODIMP NodeIterator::PreviousNode(nsIDOMNode **_retval) michael@0: { michael@0: return ImplNodeGetter(&NodeIterator::PreviousNode, _retval); michael@0: } michael@0: michael@0: already_AddRefed michael@0: NodeIterator::NextOrPrevNode(NodePointer::MoveToMethodType aMove, michael@0: ErrorResult& aResult) michael@0: { michael@0: if (mInAcceptNode) { michael@0: aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: mWorkingPointer = mPointer; michael@0: michael@0: struct AutoClear { michael@0: NodePointer* mPtr; michael@0: AutoClear(NodePointer* ptr) : mPtr(ptr) {} michael@0: ~AutoClear() { mPtr->Clear(); } michael@0: } ac(&mWorkingPointer); michael@0: michael@0: while ((mWorkingPointer.*aMove)(mRoot)) { michael@0: nsCOMPtr testNode = mWorkingPointer.mNode; michael@0: int16_t filtered = TestNode(testNode, aResult); michael@0: if (aResult.Failed()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) { michael@0: mPointer = mWorkingPointer; michael@0: return testNode.forget(); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: /* void detach (); */ michael@0: NS_IMETHODIMP NodeIterator::Detach(void) michael@0: { michael@0: if (mRoot) { michael@0: mRoot->OwnerDoc()->WarnOnceAbout(nsIDocument::eNodeIteratorDetach); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute nsIDOMNode referenceNode; */ michael@0: NS_IMETHODIMP NodeIterator::GetReferenceNode(nsIDOMNode * *aRefNode) michael@0: { michael@0: nsCOMPtr node(do_QueryInterface(GetReferenceNode())); michael@0: node.forget(aRefNode); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute boolean pointerBeforeReferenceNode; */ michael@0: NS_IMETHODIMP NodeIterator::GetPointerBeforeReferenceNode(bool *aBeforeNode) michael@0: { michael@0: *aBeforeNode = PointerBeforeReferenceNode(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * nsIMutationObserver interface michael@0: */ michael@0: michael@0: void NodeIterator::ContentRemoved(nsIDocument *aDocument, michael@0: nsIContent *aContainer, michael@0: nsIContent *aChild, michael@0: int32_t aIndexInContainer, michael@0: nsIContent *aPreviousSibling) michael@0: { michael@0: nsINode *container = NODE_FROM(aContainer, aDocument); michael@0: michael@0: mPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling); michael@0: mWorkingPointer.AdjustAfterRemoval(mRoot, container, aChild, aPreviousSibling); michael@0: } michael@0: michael@0: JSObject* michael@0: NodeIterator::WrapObject(JSContext *cx) michael@0: { michael@0: return NodeIteratorBinding::Wrap(cx, this); michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla