/* * 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 GetDeepFirstChild(nsCOMPtr aRoot); static nsCOMPtr GetDeepLastChild(nsCOMPtr aRoot); nsresult GetNextSibling(nsCOMPtr aNode, nsCOMPtr *aSibling); nsresult GetPrevSibling(nsCOMPtr aNode, nsCOMPtr *aSibling); nsresult NextNode(nsCOMPtr *ioNextNode); nsresult PrevNode(nsCOMPtr *ioPrevNode); void MakeEmpty(); nsCOMPtr mCurNode; nsCOMPtr mFirst; nsCOMPtr mLast; nsCOMPtr 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 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 dN; nsCOMPtr cChild; nsCOMPtr startCon; nsCOMPtr startDOM; nsCOMPtr endCon; nsCOMPtr 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 noNode; mCurNode = noNode; mFirst = noNode; mLast = noNode; mCommonParent = noNode; mIsDone = PR_TRUE; } nsCOMPtr nsGeneratedContentIterator::GetDeepFirstChild(nsCOMPtr aRoot) { nsCOMPtr deepFirstChild; nsCOMPtr cN = aRoot; nsCOMPtr 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 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 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 nsGeneratedContentIterator::GetDeepLastChild(nsCOMPtr aRoot) { nsCOMPtr deepFirstChild; if (aRoot) { nsCOMPtr cN = aRoot; nsCOMPtr 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 aNode, nsCOMPtr *aSibling) { if (!aNode) return NS_ERROR_NULL_POINTER; if (!aSibling) return NS_ERROR_NULL_POINTER; nsCOMPtr sib; nsCOMPtr 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(); } return NS_OK; } // Get the prev sibling, or parents prev sibling, or grandpa's prev sibling... nsresult nsGeneratedContentIterator::GetPrevSibling(nsCOMPtr aNode, nsCOMPtr *aSibling) { if (!aNode) return NS_ERROR_NULL_POINTER; if (!aSibling) return NS_ERROR_NULL_POINTER; nsCOMPtr sib; nsCOMPtr 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(); } return NS_OK; } nsresult nsGeneratedContentIterator::NextNode(nsCOMPtr *ioNextNode) { if (!ioNextNode) return NS_ERROR_NULL_POINTER; if (mPre) // if we are a Pre-order iterator, use pre-order { nsCOMPtr cN = *ioNextNode; nsCOMPtr 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 cN = *ioNextNode; nsCOMPtr cSibling; nsCOMPtr 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 *ioNextNode) { if (!ioNextNode) return NS_ERROR_NULL_POINTER; if (mPre) // if we are a Pre-order iterator, use pre-order { nsCOMPtr cN = *ioNextNode; nsCOMPtr cSibling; nsCOMPtr 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 cN = *ioNextNode; nsCOMPtr 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); }