Mozilla/mozilla/layout/base/src/nsContentIterator.cpp
mcafee%netscape.com d98dcab829 Backing out my last change, breaks Win32 & Mac.
git-svn-id: svn://10.0.0.236/trunk@22433 18797224-902f-48f8-a5cc-f745e15eee43
1999-03-01 10:57:20 +00:00

849 lines
20 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* nsContentIterator.cpp: Implementation of the nsContentIterator object.
* This ite
*/
#include "nsISupports.h"
//#include "nsIEnumerator.h"
#include "nsIContentIterator.h"
#include "nsRange.h"
#include "nsIContent.h"
#include "nsIDOMText.h"
#include "nsISupportsArray.h"
#include "nsCOMPtr.h"
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
/*
* A simple iterator class for traversing the content in "close tag" order
*/
class nsContentIterator : public nsIContentIterator //, public nsIEnumerator
{
public:
NS_DECL_ISUPPORTS
nsContentIterator();
virtual ~nsContentIterator();
// nsIContentIterator interface methods ------------------------------
NS_IMETHOD Init(nsIContent* aRoot);
NS_IMETHOD Init(nsIDOMRange* aRange);
NS_IMETHOD First();
NS_IMETHOD Last();
NS_IMETHOD Next();
NS_IMETHOD Prev();
NS_IMETHOD CurrentNode(nsIContent **aNode);
NS_IMETHOD IsDone();
// nsIEnumertor interface methods ------------------------------
//NS_IMETHOD CurrentItem(nsISupports **aItem);
protected:
static nsCOMPtr<nsIContent> GetDeepFirstChild(nsCOMPtr<nsIContent> aRoot);
static nsCOMPtr<nsIContent> GetDeepLastChild(nsCOMPtr<nsIContent> aRoot);
nsresult GetNextSibling(nsCOMPtr<nsIContent> aNode, nsCOMPtr<nsIContent> *aSibling);
nsresult GetPrevSibling(nsCOMPtr<nsIContent> aNode, nsCOMPtr<nsIContent> *aSibling);
nsresult NextNode(nsCOMPtr<nsIContent> *ioNextNode);
nsresult PrevNode(nsCOMPtr<nsIContent> *ioPrevNode);
void MakeEmpty();
nsCOMPtr<nsIContent> mCurNode;
nsCOMPtr<nsIContent> mFirst;
nsCOMPtr<nsIContent> mLast;
nsCOMPtr<nsIContent> mCommonParent;
PRBool mIsDone;
private:
// no copy's or assigns FIX ME
nsContentIterator(const nsContentIterator&);
nsContentIterator& operator=(const nsContentIterator&);
};
nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
/******************************************************
* repository cruft
******************************************************/
nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult)
{
nsContentIterator * iter = new nsContentIterator();
return iter->QueryInterface(nsIContentIterator::IID(), (void**) aInstancePtrResult);
}
/******************************************************
* XPCOM cruft
******************************************************/
NS_IMPL_ADDREF(nsContentIterator)
NS_IMPL_RELEASE(nsContentIterator)
nsresult nsContentIterator::QueryInterface(const nsIID& aIID,
void** aInstancePtrResult)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer");
if (nsnull == aInstancePtrResult)
{
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(kISupportsIID))
{
*aInstancePtrResult = (void*)(nsISupports*)(nsIContentIterator*)this;
NS_ADDREF_THIS();
return NS_OK;
}
/* if (aIID.Equals(nsIEnumerator::IID()))
{
*aInstancePtrResult = (void*)(nsIEnumerator*)this;
NS_ADDREF_THIS();
return NS_OK;
} */
if (aIID.Equals(nsIContentIterator::IID()))
{
*aInstancePtrResult = (void*)(nsIContentIterator*)this;
NS_ADDREF_THIS();
return NS_OK;
}
return !NS_OK;
}
/******************************************************
* constructor/destructor
******************************************************/
nsContentIterator::nsContentIterator() :
// don't need to explicitly initialize |nsCOMPtr|s, they will automatically be NULL
mIsDone(PR_FALSE)
{
NS_INIT_REFCNT();
}
nsContentIterator::~nsContentIterator()
{
}
/******************************************************
* Init routines
******************************************************/
nsresult nsContentIterator::Init(nsIContent* aRoot)
{
if (!aRoot)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> root( dont_QueryInterface(aRoot) );
mFirst = GetDeepFirstChild(root);
mLast = root;
mCommonParent = root;
mCurNode = mFirst;
return NS_OK;
}
nsresult nsContentIterator::Init(nsIDOMRange* aRange)
{
if (!aRange)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> cN;
nsCOMPtr<nsIDOMNode> dN;
// get common content parent
if (!NS_SUCCEEDED(aRange->GetCommonParent(getter_AddRefs(dN))) || !dN)
return NS_ERROR_FAILURE;
mCommonParent = do_QueryInterface(dN);
// get the start node and offset, convert to nsIContent
aRange->GetStartParent(getter_AddRefs(dN));
if (!dN)
return NS_ERROR_ILLEGAL_VALUE;
cN = do_QueryInterface(dN);
if (!cN)
return NS_ERROR_FAILURE;
PRInt32 indx;
aRange->GetStartOffset(&indx);
// find first node in range
nsCOMPtr<nsIContent> cChild;
cN->ChildAt(0,*getter_AddRefs(cChild));
if (!cChild) // no children, must be a text node
{
mFirst = cN;
}
else
{
cN->ChildAt(indx,*getter_AddRefs(cChild));
if (!cChild) // offset after last child, parent is first node
{
mFirst = cN;
}
else
{
mFirst = GetDeepFirstChild(cChild);
}
// Does that first node really intersect the range?
// the range could be collapsed, or the range could be
// 'degenerate', ie not collapsed but still containing
// no content. In this case, we want the iterator to
// be empty
if (!IsNodeIntersectsRange(mFirst, aRange))
{
MakeEmpty();
return NS_OK;
}
}
aRange->GetEndParent(getter_AddRefs(dN));
if (!dN)
return NS_ERROR_ILLEGAL_VALUE;
cN = do_QueryInterface(dN);
if (!cN)
return NS_ERROR_FAILURE;
aRange->GetEndOffset(&indx);
// find last node in range
cN->ChildAt(0,*getter_AddRefs(cChild));
if (!cChild) // no children, must be a text node
{
mLast = cN;
}
else if (indx == 0) // before first child, parent is last node
{
mLast = cN;
}
else
{
cN->ChildAt(--indx,*getter_AddRefs(cChild));
if (!cChild) // offset after last child, last child is last node
{
cN->ChildCount(indx);
cN->ChildAt(--indx,*getter_AddRefs(cChild));
if (!cChild)
{
NS_NOTREACHED("nsContentIterator::nsContentIterator");
return NS_ERROR_FAILURE;
}
}
mLast = cChild;
}
mCurNode = mFirst;
return NS_OK;
}
/******************************************************
* Helper routines
******************************************************/
void nsContentIterator::MakeEmpty()
{
nsCOMPtr<nsIContent> noNode;
mCurNode = noNode;
mFirst = noNode;
mLast = noNode;
mCommonParent = noNode;
mIsDone = PR_TRUE;
}
nsCOMPtr<nsIContent> nsContentIterator::GetDeepFirstChild(nsCOMPtr<nsIContent> aRoot)
{
if (!aRoot)
return aRoot;
nsCOMPtr<nsIContent> cN = aRoot;
nsCOMPtr<nsIContent> cChild;
cN->ChildAt(0,*getter_AddRefs(cChild));
while ( cChild )
{
cN = cChild;
cN->ChildAt(0,*getter_AddRefs(cChild));
}
return cN;
}
nsCOMPtr<nsIContent> nsContentIterator::GetDeepLastChild(nsCOMPtr<nsIContent> aRoot)
{
if (!aRoot)
return aRoot;
nsCOMPtr<nsIContent> cN = aRoot;
nsCOMPtr<nsIContent> cChild;
PRInt32 numChildren;
cN->ChildCount(numChildren);
while ( numChildren )
{
cN->ChildAt(--numChildren,*getter_AddRefs(cChild));
if (cChild)
{
cChild->ChildCount(numChildren);
cN = cChild;
}
else
{
break;
}
}
return cN;
}
// Get the next sibling, or parents next sibling, or grandpa's next sibling...
nsresult nsContentIterator::GetNextSibling(nsCOMPtr<nsIContent> aNode, nsCOMPtr<nsIContent> *aSibling)
{
if (!aNode)
return NS_ERROR_NULL_POINTER;
if (!aSibling)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> sib;
nsCOMPtr<nsIContent> parent;
PRInt32 indx;
if (!NS_SUCCEEDED(aNode->GetParent(*getter_AddRefs(parent))))
return NS_ERROR_FAILURE;
if (!NS_SUCCEEDED(parent->IndexOf(aNode, indx)))
return NS_ERROR_FAILURE;
if (NS_SUCCEEDED(parent->ChildAt(++indx, *getter_AddRefs(sib))) && sib)
{
*aSibling = sib;
}
else if (parent != mCommonParent)
{
GetNextSibling(parent, aSibling);
}
else
{
*aSibling = nsCOMPtr<nsIContent>();
}
return NS_OK;
}
// Get the prev sibling, or parents prev sibling, or grandpa's prev sibling...
nsresult nsContentIterator::GetPrevSibling(nsCOMPtr<nsIContent> aNode, nsCOMPtr<nsIContent> *aSibling)
{
if (!aNode)
return NS_ERROR_NULL_POINTER;
if (!aSibling)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> sib;
nsCOMPtr<nsIContent> parent;
PRInt32 indx;
if (!NS_SUCCEEDED(aNode->GetParent(*getter_AddRefs(parent))))
return NS_ERROR_FAILURE;
if (!NS_SUCCEEDED(parent->IndexOf(aNode, indx)))
return NS_ERROR_FAILURE;
if (indx && NS_SUCCEEDED(parent->ChildAt(--indx, *getter_AddRefs(sib))) && sib)
{
*aSibling = sib;
}
else if (parent != mCommonParent)
{
GetPrevSibling(parent, aSibling);
}
else
{
*aSibling = nsCOMPtr<nsIContent>();
}
return NS_OK;
}
nsresult nsContentIterator::NextNode(nsCOMPtr<nsIContent> *ioNextNode)
{
if (!ioNextNode)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> cN = *ioNextNode;
nsCOMPtr<nsIContent> cSibling;
nsCOMPtr<nsIContent> parent;
PRInt32 indx;
// get next sibling if there is one
if (!NS_SUCCEEDED(cN->GetParent(*getter_AddRefs(parent))))
return NS_ERROR_FAILURE;
if (!NS_SUCCEEDED(parent->IndexOf(cN, indx)))
return NS_ERROR_FAILURE;
if (NS_SUCCEEDED(parent->ChildAt(++indx,*getter_AddRefs(cSibling))) && cSibling)
{
// next node is siblings "deep left" child
*ioNextNode = GetDeepFirstChild(cSibling);
return NS_OK;
}
// else it's the parent
*ioNextNode = parent;
return NS_OK;
}
nsresult nsContentIterator::PrevNode(nsCOMPtr<nsIContent> *ioNextNode)
{
if (!ioNextNode)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> cN = *ioNextNode;
nsCOMPtr<nsIContent> cLastChild;
PRInt32 numChildren;
cN->ChildCount(numChildren);
// if it has children then prev node is last child
if (numChildren)
{
cN->ChildAt(--numChildren,*getter_AddRefs(cLastChild));
if (!cLastChild)
return NS_ERROR_FAILURE;
*ioNextNode = cLastChild;
return NS_OK;
}
// else prev sibling is previous
return GetPrevSibling(cN, ioNextNode);
}
/******************************************************
* ContentIterator routines
******************************************************/
nsresult nsContentIterator::First()
{
if (!mFirst)
return NS_ERROR_FAILURE;
mIsDone = PR_FALSE;
if (mFirst == mCurNode)
return NS_OK;
mCurNode = mFirst;
return NS_OK;
}
nsresult nsContentIterator::Last()
{
if (!mLast)
return NS_ERROR_FAILURE;
mIsDone = PR_FALSE;
if (mLast == mCurNode)
return NS_OK;
mCurNode = mLast;
return NS_OK;
}
nsresult nsContentIterator::Next()
{
if (mIsDone)
return NS_ERROR_FAILURE;
if (!mCurNode)
return NS_OK;
if (mCurNode == mLast)
{
mIsDone = PR_TRUE;
return NS_ERROR_FAILURE;
}
return NextNode(&mCurNode);
}
nsresult nsContentIterator::Prev()
{
if (mIsDone)
return NS_ERROR_FAILURE;
if (!mCurNode)
return NS_OK;
if (mCurNode == mFirst)
{
mIsDone = PR_TRUE;
return NS_ERROR_FAILURE;
}
return PrevNode(&mCurNode);
}
nsresult nsContentIterator::IsDone()
{
if (mIsDone)
return NS_OK;
else
return NS_COMFALSE;
}
nsresult nsContentIterator::CurrentNode(nsIContent **aNode)
{
if (!mCurNode)
return NS_ERROR_FAILURE;
if (mIsDone)
return NS_ERROR_FAILURE;
return mCurNode->QueryInterface(nsIContent::IID(), (void**) aNode);
}
/*====================================================================================*/
/*====================================================================================*/
/******************************************************
* nsContentSubtreeIterator
******************************************************/
/*
* A simple iterator class for traversing the content in "top subtree" order
*/
class nsContentSubtreeIterator : public nsContentIterator
{
public:
nsContentSubtreeIterator() {};
virtual ~nsContentSubtreeIterator() {};
// nsContentIterator overrides ------------------------------
NS_IMETHOD Init(nsIContent* aRoot);
NS_IMETHOD Init(nsIDOMRange* aRange);
NS_IMETHOD Next();
NS_IMETHOD Prev();
protected:
nsresult GetTopAncestorInRange( nsCOMPtr<nsIContent> aNode,
nsCOMPtr<nsIContent> *outAnestor);
// no copy's or assigns FIX ME
nsContentSubtreeIterator(const nsContentSubtreeIterator&);
nsContentSubtreeIterator& operator=(const nsContentSubtreeIterator&);
nsCOMPtr<nsIDOMRange> mRange;
};
nsresult NS_NewContentSubtreeIterator(nsIContentIterator** aInstancePtrResult);
/******************************************************
* repository cruft
******************************************************/
nsresult NS_NewContentSubtreeIterator(nsIContentIterator** aInstancePtrResult)
{
nsContentIterator * iter = new nsContentSubtreeIterator();
return iter->QueryInterface(nsIContentIterator::IID(), (void**) aInstancePtrResult);
}
/******************************************************
* Init routines
******************************************************/
nsresult nsContentSubtreeIterator::Init(nsIContent* aRoot)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult nsContentSubtreeIterator::Init(nsIDOMRange* aRange)
{
if (!aRange)
return NS_ERROR_NULL_POINTER;
mRange = aRange;
// get the start node and offset, convert to nsIContent
nsCOMPtr<nsIDOMNode> commonParent;
nsCOMPtr<nsIDOMNode> startParent;
nsCOMPtr<nsIDOMNode> endParent;
nsCOMPtr<nsIContent> cStartP;
nsCOMPtr<nsIContent> cEndP;
nsCOMPtr<nsIContent> cN;
nsCOMPtr<nsIContent> candidate;
// get common content parent
if (!NS_SUCCEEDED(aRange->GetCommonParent(getter_AddRefs(commonParent))) || !commonParent)
return NS_ERROR_FAILURE;
mCommonParent = do_QueryInterface(commonParent);
// get start content parent
if (!NS_SUCCEEDED(aRange->GetStartParent(getter_AddRefs(startParent))) || !startParent)
return NS_ERROR_FAILURE;
cStartP = do_QueryInterface(startParent);
// get end content parent
if (!NS_SUCCEEDED(aRange->GetEndParent(getter_AddRefs(endParent))) || !endParent)
return NS_ERROR_FAILURE;
cEndP = do_QueryInterface(endParent);
// find first node in range
PRInt32 indx;
aRange->GetStartOffset(&indx);
nsCOMPtr<nsIContent> cChild;
PRInt32 numChildren;
cStartP->ChildCount(numChildren);
if (numChildren) // no children, must be a text node
{
cN = cStartP;
}
else
{
cStartP->ChildAt(indx,*getter_AddRefs(cChild));
if (!cChild) // offset after last child
{
cN = cStartP;
}
else
{
candidate = cChild;
}
}
if (!candidate)
{
// then candidate is next node after cN
if (!NS_SUCCEEDED(GetNextSibling(cN, &candidate)))
{
MakeEmpty();
return NS_OK;
}
}
candidate = GetDeepFirstChild(candidate);
// confirm that this first possible contained node
// is indeed contained. Else we have a range that
// does not fully contain any node.
PRBool nodeBefore, nodeAfter;
if (!NS_SUCCEEDED(CompareNodeToRange(candidate, aRange, &nodeBefore, &nodeAfter)))
return NS_ERROR_FAILURE;
if (nodeBefore || nodeAfter)
{
MakeEmpty();
return NS_OK;
}
// cool, we have the first node in the range. Now we walk
// up it's ancestors to find the most senior that is still
// in the range. That's the real first node.
if (!NS_SUCCEEDED(GetTopAncestorInRange(candidate, &mFirst)))
return NS_ERROR_FAILURE;
// now to find the last node
aRange->GetEndOffset(&indx);
if (!indx)
{
cN = cEndP;
}
else
{
cEndP->ChildCount(numChildren);
if (numChildren) // no children, must be a text node
{
cN = cEndP;
}
else
{
cEndP->ChildAt(--indx,*getter_AddRefs(cChild));
if (!cChild) // shouldn't happen
{
NS_ASSERTION(0,"tree traversal trouble in nsContentSubtreeIterator::Init");
return NS_ERROR_FAILURE;
}
else
{
candidate = cChild;
}
}
}
if (!candidate)
{
// then candidate is prev node before cN
if (!NS_SUCCEEDED(GetPrevSibling(cN, &candidate)))
{
MakeEmpty();
return NS_OK;
}
}
candidate = GetDeepLastChild(candidate);
// confirm that this first possible contained node
// is indeed contained. Else we have a range that
// does not fully contain any node.
if (!NS_SUCCEEDED(CompareNodeToRange(candidate, aRange, &nodeBefore, &nodeAfter)))
return NS_ERROR_FAILURE;
if (nodeBefore || nodeAfter)
{
MakeEmpty();
return NS_OK;
}
// cool, we have the last node in the range. Now we walk
// up it's ancestors to find the most senior that is still
// in the range. That's the real first node.
if (!NS_SUCCEEDED(GetTopAncestorInRange(candidate, &mLast)))
return NS_ERROR_FAILURE;
mCurNode = mFirst;
return NS_OK;
}
/****************************************************************
* nsContentSubtreeIterator overrides of ContentIterator routines
****************************************************************/
nsresult nsContentSubtreeIterator::Next()
{
if (mIsDone)
return NS_ERROR_FAILURE;
if (!mCurNode)
return NS_OK;
if (mCurNode == mLast)
{
mIsDone = PR_TRUE;
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIContent> nextNode;
if (!NS_SUCCEEDED(GetNextSibling(mCurNode, &nextNode)))
return NS_ERROR_FAILURE;
nextNode = GetDeepFirstChild(nextNode);
return GetTopAncestorInRange(nextNode, &mCurNode);
}
nsresult nsContentSubtreeIterator::Prev()
{
if (mIsDone)
return NS_ERROR_FAILURE;
if (!mCurNode)
return NS_OK;
if (mCurNode == mFirst)
{
mIsDone = PR_TRUE;
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIContent> prevNode;
prevNode = GetDeepFirstChild(mCurNode);
if (!NS_SUCCEEDED(PrevNode(&prevNode)))
return NS_ERROR_FAILURE;
prevNode = GetDeepLastChild(prevNode);
return GetTopAncestorInRange(prevNode, &mCurNode);
}
/****************************************************************
* nsContentSubtreeIterator helper routines
****************************************************************/
nsresult nsContentSubtreeIterator::GetTopAncestorInRange(
nsCOMPtr<nsIContent> aNode,
nsCOMPtr<nsIContent> *outAnestor)
{
if (!aNode)
return NS_ERROR_NULL_POINTER;
if (!outAnestor)
return NS_ERROR_NULL_POINTER;
// sanity check: aNode is itself in the range
PRBool nodeBefore, nodeAfter;
if (!NS_SUCCEEDED(CompareNodeToRange(aNode, mRange, &nodeBefore, &nodeAfter)))
return NS_ERROR_FAILURE;
if (nodeBefore || nodeAfter)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIContent> parent;
while (aNode)
{
if (!NS_SUCCEEDED(aNode->GetParent(*getter_AddRefs(parent))))
return NS_ERROR_FAILURE;
if (!NS_SUCCEEDED(CompareNodeToRange(parent, mRange, &nodeBefore, &nodeAfter)))
return NS_ERROR_FAILURE;
if (nodeBefore || nodeAfter)
{
*outAnestor = aNode;
return NS_OK;
}
aNode = parent;
}
return NS_ERROR_FAILURE;
}