Mozilla/mozilla/content/base/src/nsGeneratedIterator.cpp
mjudge%netscape.com dcda3caccf adding new file should not be built yet. this will do no harm
git-svn-id: svn://10.0.0.236/trunk@60588 18797224-902f-48f8-a5cc-f745e15eee43
2000-02-12 06:09:19 +00:00

707 lines
17 KiB
C++

/*
* A simple iterator class for traversing the content in "close tag" order
*/
class nsGeneratedContentIterator : public nsIContentIterator,nsIGeneratedContentIterator
{
public:
NS_DECL_ISUPPORTS
nsGeneratedContentIterator();
virtual ~nsGeneratedContentIterator();
// nsIContentIterator interface methods ------------------------------
NS_IMETHOD Init(nsIContent* aRoot);
NS_IMETHOD Init(nsIDOMRange* aRange);
NS_IMETHOD Init(nsIFocusTracker *aTracker, nsIDOMRange* aRange, PRBool aSelectBefore, PRBool aSelectAfter);
NS_IMETHOD Init(nsIFocusTracker *aTracker, nsIContent* aRange, PRBool aSelectBefore, PRBool aSelectAfter);
NS_IMETHOD First();
NS_IMETHOD Last();
NS_IMETHOD Next();
NS_IMETHOD Prev();
NS_IMETHOD CurrentNode(nsIContent **aNode);
NS_IMETHOD IsDone();
NS_IMETHOD PositionAt(nsIContent* aCurNode);
NS_IMETHOD MakePre();
NS_IMETHOD MakePost();
// 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;
PRBool mPre;
private:
PRBool mSelectBefore;//set at init time
PRBool mSelectAfter;
nsIFocusTracker *mTracker;//weak reference
nsresult FillGenIter(PRInt8 aSide);
// no copy's or assigns FIX ME
nsGeneratedContentIterator(const nsGeneratedContentIterator&);
nsGeneratedContentIterator& operator=(const nsGeneratedContentIterator&);
};
/*************************
IMPLEMENTATION
**************************/
nsGeneratedContentIterator::nsGeneratedContentIterator()
:mSelectBefore(0),mSelectAfter(0), mCurSideOfContent(-1)
{
}
nsGeneratedContentIterator::~nsGeneratedContentIterator()
{
}
NS_IMPL_ADDREF(nsGeneratedContentIterator)
NS_IMPL_RELEASE(nsGeneratedContentIterator)
nsresult NS_NewGeneratedContentIterator(nsIGeneratedContentIterator** aInstancePtrResult)
{
nsGeneratedContentIterator * iter = new nsGeneratedContentIterator();
if (iter)
return iter->QueryInterface(NS_GET_IID(nsIGeneratedContentIterator), (void**) aInstancePtrResult);
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult nsGeneratedContentIterator::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(NS_GET_IID(nsIGeneratedContentIterator)))
{
*aInstancePtr = NS_STATIC_CAST(nsIGeneratedContentIterator *,this);
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(NS_GET_IID(nsIContentIterator)))
{
*aInstancePtrResult = (void*)(nsIContentIterator*)this;
NS_ADDREF_THIS();
return NS_OK;
}
return NS_NOINTERFACE;
}
/******************************************************
* Init routines
******************************************************/
nsresult nsGeneratedContentIterator::Init(nsIContent* aRoot)
{
if (!aRoot)
return NS_ERROR_NULL_POINTER;
mIsDone = PR_FALSE;
nsCOMPtr<nsIContent> root( do_QueryInterface(aRoot) );
mFirst = GetDeepFirstChild(root);
mLast = root;
mCommonParent = root;
mCurNode = mFirst;
mSelectBefore = PR_FALSE;
mSelectAfter = PR_FALSE;
mTracker = nsnull;
return NS_OK;
}
nsresult nsGeneratedContentIterator::Init(nsIDOMRange* aRange)
{
if (!aRange)
return NS_ERROR_NULL_POINTER;
mSelectBefore = PR_FALSE;
mSelectAfter = PR_FALSE;
mTracker = nsnull;
nsCOMPtr<nsIDOMNode> dN;
nsCOMPtr<nsIContent> cChild;
nsCOMPtr<nsIContent> startCon;
nsCOMPtr<nsIDOMNode> startDOM;
nsCOMPtr<nsIContent> endCon;
nsCOMPtr<nsIDOMNode> endDOM;
PRInt32 startIndx;
PRInt32 endIndx;
mIsDone = PR_FALSE;
// get common content parent
if (NS_FAILED(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(startDOM));
if (!startDOM)
return NS_ERROR_ILLEGAL_VALUE;
startCon = do_QueryInterface(startDOM);
if (!startCon)
return NS_ERROR_FAILURE;
aRange->GetStartOffset(&startIndx);
// get the end node and offset, convert to nsIContent
aRange->GetEndParent(getter_AddRefs(endDOM));
if (!endDOM)
return NS_ERROR_ILLEGAL_VALUE;
endCon = do_QueryInterface(endDOM);
if (!endCon)
return NS_ERROR_FAILURE;
aRange->GetEndOffset(&endIndx);
// short circuit when start node == end node
if (startDOM == endDOM)
{
startCon->ChildAt(0,*getter_AddRefs(cChild));
if (!cChild) // no children, must be a text node or empty container
{
mFirst = startCon;
mLast = startCon;
mCurNode = startCon;
return NS_OK;
}
else
{
if (startIndx == endIndx) // collapsed range
{
MakeEmpty();
return NS_OK;
}
}
}
// find first node in range
startCon->ChildAt(0,*getter_AddRefs(cChild));
if (!cChild) // no children, must be a text node
{
mFirst = startCon;
}
else
{
startCon->ChildAt(startIndx,*getter_AddRefs(cChild));
if (!cChild) // offset after last child, parent is first node
{
mFirst = startCon;
}
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;
}
}
// find last node in range
endCon->ChildAt(0,*getter_AddRefs(cChild));
if (!cChild) // no children, must be a text node
{
mLast = endCon;
}
else if (endIndx == 0) // before first child, parent is last node
{
mLast = endCon;
}
else
{
endCon->ChildAt(--endIndx,*getter_AddRefs(cChild));
if (!cChild) // offset after last child, last child is last node
{
endCon->ChildCount(endIndx);
endCon->ChildAt(--endIndx,*getter_AddRefs(cChild));
if (!cChild)
{
NS_NOTREACHED("nsGeneratedContentIterator::nsGeneratedContentIterator");
return NS_ERROR_FAILURE;
}
}
mLast = cChild;
}
mCurNode = mFirst;
return NS_OK;
}
NS_IMETHODIMP nsGeneratedContentIterator::Init(nsIFocusTracker *aTracker, nsIDOMRange* aRange, PRBool aSelectBefore, PRBool aSelectAfter)
{
mTracker = aTracker;
mSelectBefore = aSelectBefore;
mSelectAfter = aSelectAfter;
return Init(aRange);
}
NS_IMETHODIMP nsGeneratedContentIterator::Init(nsIFocusTracker *aTracker, nsIContent* aRange, PRBool aSelectBefore, PRBool aSelectAfter)
{
mTracker = aTracker;
mSelectBefore = aSelectBefore;
mSelectAfter = aSelectAfter;
return Init(aRange);
}
/******************************************************
* Helper routines
******************************************************/
void nsGeneratedContentIterator::MakeEmpty()
{
nsCOMPtr<nsIContent> noNode;
mCurNode = noNode;
mFirst = noNode;
mLast = noNode;
mCommonParent = noNode;
mIsDone = PR_TRUE;
}
nsCOMPtr<nsIContent> nsGeneratedContentIterator::GetDeepFirstChild(nsCOMPtr<nsIContent> aRoot)
{
nsCOMPtr<nsIContent> deepFirstChild;
nsCOMPtr<nsIContent> cN = aRoot;
nsCOMPtr<nsIContent> cChild;
if (aRoot)
{
if (mTracker)
{
nsFrameState frameState;
nsIFrame *genFrame;
result = mTracker->GetPrimaryFrameFor(aRoot, &genFrame);
if (NS_FAILED(result))
return result;
if (!genFrame)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIPresContext> context;
result = mTracker->GetPresContext(getter_AddRefs(context));
if (NS_FAILED(result) || !context)
return result?result:NS_ERROR_FAILURE;
result = genFrame->FirstChild(context,nsnull,&genFrame);
if (NS_FAILED(result) || !genFrame)
return NS_OK;//fine nothing to do here
//we SHOULD now have a generated content frame if one exists. we need to check the flag for gen content
result = genFrame->GetFrameState(&frameState);
if (NS_FAILED(result))
return result;
if (frameState & NS_FRAME_GENERATED_CONTENT)
{
result = genFrame->GetContent(getter_AddRefs(cChild));
}
}
if (!cChild)
cN->ChildAt(0,*getter_AddRefs(cChild));
while ( cChild )
{
cN = cChild;
if (mTracker)
{
nsFrameState frameState;
nsIFrame *genFrame;
result = mTracker->GetPrimaryFrameFor(cN, &genFrame);
if (NS_FAILED(result))
return result;
if (!genFrame)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIPresContext> context;
result = mTracker->GetPresContext(getter_AddRefs(context));
if (NS_FAILED(result) || !context)
return result?result:NS_ERROR_FAILURE;
result = genFrame->FirstChild(context,nsnull,&genFrame);
if (NS_FAILED(result) || !genFrame)
return NS_OK;//fine nothing to do here
//we SHOULD now have a generated content frame if one exists. we need to check the flag for gen content
result = genFrame->GetFrameState(&frameState);
if (NS_FAILED(result))
return result;
if (frameState & NS_FRAME_GENERATED_CONTENT)
{
result = genFrame->GetContent(getter_AddRefs(cChild));
}
if (!cChild)
cN->ChildAt(0,*getter_AddRefs(cChild));
}
deepFirstChild = cN;
}
return deepFirstChild;
}
nsCOMPtr<nsIContent> nsGeneratedContentIterator::GetDeepLastChild(nsCOMPtr<nsIContent> aRoot)
{
nsCOMPtr<nsIContent> deepFirstChild;
if (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;
}
}
deepFirstChild = cN;
}
return deepFirstChild;
}
// Get the next sibling, or parents next sibling, or grandpa's next sibling...
nsresult nsGeneratedContentIterator::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_FAILED(aNode->GetParent(*getter_AddRefs(parent))) || !parent)
return NS_ERROR_FAILURE;
if (NS_FAILED(parent->IndexOf(aNode, indx)))
return NS_ERROR_FAILURE;
if (NS_SUCCEEDED(parent->ChildAt(++indx, *getter_AddRefs(sib))) && sib)
{
*aSibling = sib;
}
else if (parent != mCommonParent)
{
return GetNextSibling(parent, aSibling);
}
else
{
*aSibling = nsCOMPtr<nsIContent>();
}
return NS_OK;
}
// Get the prev sibling, or parents prev sibling, or grandpa's prev sibling...
nsresult nsGeneratedContentIterator::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_FAILED(aNode->GetParent(*getter_AddRefs(parent))) || !parent)
return NS_ERROR_FAILURE;
if (NS_FAILED(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)
{
return GetPrevSibling(parent, aSibling);
}
else
{
*aSibling = nsCOMPtr<nsIContent>();
}
return NS_OK;
}
nsresult nsGeneratedContentIterator::NextNode(nsCOMPtr<nsIContent> *ioNextNode)
{
if (!ioNextNode)
return NS_ERROR_NULL_POINTER;
if (mPre) // if we are a Pre-order iterator, use pre-order
{
nsCOMPtr<nsIContent> cN = *ioNextNode;
nsCOMPtr<nsIContent> cFirstChild;
PRInt32 numChildren;
cN->ChildCount(numChildren);
// if it has children then next node is first child
if (numChildren)
{
if (NS_FAILED(cN->ChildAt(0,*getter_AddRefs(cFirstChild))))
return NS_ERROR_FAILURE;
if (!cFirstChild)
return NS_ERROR_FAILURE;
*ioNextNode = cFirstChild;
return NS_OK;
}
// else next sibling is next
return GetNextSibling(cN, ioNextNode);
}
else // post-order
{
nsCOMPtr<nsIContent> cN = *ioNextNode;
nsCOMPtr<nsIContent> cSibling;
nsCOMPtr<nsIContent> parent;
PRInt32 indx;
// get next sibling if there is one
if (NS_FAILED(cN->GetParent(*getter_AddRefs(parent))))
return NS_ERROR_FAILURE;
if (!parent || NS_FAILED(parent->IndexOf(cN, indx)))
{
// a little noise to catch some iterator usage bugs.
NS_NOTREACHED("nsGeneratedContentIterator::NextNode() : no parent found");
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 nsGeneratedContentIterator::PrevNode(nsCOMPtr<nsIContent> *ioNextNode)
{
if (!ioNextNode)
return NS_ERROR_NULL_POINTER;
if (mPre) // if we are a Pre-order iterator, use pre-order
{
nsCOMPtr<nsIContent> cN = *ioNextNode;
nsCOMPtr<nsIContent> cSibling;
nsCOMPtr<nsIContent> parent;
PRInt32 indx;
// get prev sibling if there is one
if (NS_FAILED(cN->GetParent(*getter_AddRefs(parent))))
return NS_ERROR_FAILURE;
if (!parent || NS_FAILED(parent->IndexOf(cN, indx)))
{
// a little noise to catch some iterator usage bugs.
NS_NOTREACHED("nsGeneratedContentIterator::PrevNode() : no parent found");
return NS_ERROR_FAILURE;
}
if (indx && NS_SUCCEEDED(parent->ChildAt(--indx,*getter_AddRefs(cSibling))) && cSibling)
{
// prev node is siblings "deep right" child
*ioNextNode = GetDeepLastChild(cSibling);
return NS_OK;
}
// else it's the parent
*ioNextNode = parent;
}
else // post-order
{
nsCOMPtr<nsIContent> cN = *ioNextNode;
nsCOMPtr<nsIContent> cLastChild;
PRInt32 numChildren;
cN->ChildCount(numChildren);
// if it has children then prev node is last child
if (numChildren)
{
if (NS_FAILED(cN->ChildAt(--numChildren,*getter_AddRefs(cLastChild))))
return NS_ERROR_FAILURE;
if (!cLastChild)
return NS_ERROR_FAILURE;
*ioNextNode = cLastChild;
return NS_OK;
}
// else prev sibling is previous
return GetPrevSibling(cN, ioNextNode);
}
return NS_OK;
}
/******************************************************
* ContentIterator routines
******************************************************/
nsresult nsGeneratedContentIterator::First()
{
if (!mFirst)
return NS_ERROR_FAILURE;
mIsDone = PR_FALSE;
if (mFirst == mCurNode)
return NS_OK;
mCurNode = mFirst;
return NS_OK;
}
nsresult nsGeneratedContentIterator::Last()
{
if (!mLast)
return NS_ERROR_FAILURE;
mIsDone = PR_FALSE;
if (mLast == mCurNode)
return NS_OK;
mCurNode = mLast;
return NS_OK;
}
nsresult nsGeneratedContentIterator::Next()
{
if (mIsDone)
return NS_OK;
if (!mCurNode)
return NS_OK;
if (mCurNode == mLast)
{
mIsDone = PR_TRUE;
return NS_OK;
}
return NextNode(&mCurNode);
}
nsresult nsGeneratedContentIterator::Prev()
{
if (mIsDone)
return NS_OK;
if (!mCurNode)
return NS_OK;
if (mCurNode == mFirst)
{
mIsDone = PR_TRUE;
return NS_OK;
}
return PrevNode(&mCurNode);
}
nsresult nsGeneratedContentIterator::IsDone()
{
if (mIsDone)
return NS_OK;
else
return NS_ENUMERATOR_FALSE;
}
nsresult nsGeneratedContentIterator::PositionAt(nsIContent* aCurNode)
{
// XXX need to confirm that aCurNode is within range
if (!aCurNode)
return NS_ERROR_NULL_POINTER;
mCurNode = do_QueryInterface(aCurNode);
mIsDone = PR_FALSE;
return NS_OK;
}
nsresult nsGeneratedContentIterator::MakePre()
{
// XXX need to confirm mCurNode is within range
mPre = PR_TRUE;
return NS_OK;
}
nsresult nsGeneratedContentIterator::MakePost()
{
// XXX need to confirm mCurNode is within range
mPre = PR_FALSE;
return NS_OK;
}
nsresult nsGeneratedContentIterator::CurrentNode(nsIContent **aNode)
{
if (!mCurNode)
return NS_ERROR_FAILURE;
if (mIsDone)
return NS_ERROR_FAILURE;
return mCurNode->QueryInterface(NS_GET_IID(nsIContent), (void**) aNode);
}