/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "MPL"); you may not use this file except in * compliance with the MPL. You may obtain a copy of the MPL at * http://www.mozilla.org/MPL/ * * Software distributed under the MPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the MPL * for the specific language governing rights and limitations under the * MPL. * * The Initial Developer of this code under the MPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 2000 Netscape Communications Corporation. All Rights * Reserved. */ #include "nsCOMPtr.h" #include "nsIContentIterator.h" #include "nsIPresContext.h" #include "nsIFrame.h" #include "nsIContent.h" #include "nsIEnumerator.h" #include "nsFrameList.h" class nsFrameContentIterator : public nsIContentIterator { public: nsFrameContentIterator(nsIPresContext* aPresContext, nsIFrame* aFrame); virtual ~nsFrameContentIterator(); // nsISupports NS_DECL_ISUPPORTS // nsIContentIterator virtual nsresult Init(nsIContent* aRoot); virtual nsresult Init(nsIDOMRange* aRange); virtual void First(); virtual void Last(); virtual void Next(); virtual void Prev(); virtual nsIContent *GetCurrentNode(); virtual PRBool IsDone(); virtual nsresult PositionAt(nsIContent* aCurNode); private: nsCOMPtr mPresContext; nsIFrame* mParentFrame; nsIFrame* mCurrentChild; PRBool mIsDone; }; nsFrameContentIterator::nsFrameContentIterator(nsIPresContext* aPresContext, nsIFrame* aFrame) : mPresContext(aPresContext), mParentFrame(aFrame), mIsDone(PR_FALSE) { First(); } NS_IMPL_ISUPPORTS1(nsFrameContentIterator, nsIContentIterator) nsFrameContentIterator::~nsFrameContentIterator() { } nsresult nsFrameContentIterator::Init(nsIContent* aRoot) { return NS_ERROR_ALREADY_INITIALIZED; } nsresult nsFrameContentIterator::Init(nsIDOMRange* aRange) { return NS_ERROR_ALREADY_INITIALIZED; } void nsFrameContentIterator::First() { // Get the first child frame and make it the current node mCurrentChild = mParentFrame->GetFirstChild(nsnull); mIsDone = !mCurrentChild; } static nsIFrame* GetNextChildFrame(nsIPresContext* aPresContext, nsIFrame* aFrame) { NS_PRECONDITION(aFrame, "null pointer"); // Get the last-in-flow aFrame = aFrame->GetLastInFlow(); // Get its next sibling nsIFrame* nextSibling = aFrame->GetNextSibling(); // If there's no next sibling, then check if the parent frame // has a next-in-flow and look there if (!nextSibling) { nsIFrame* parent; aFrame->GetParent()->GetNextInFlow(&parent); if (parent) { nextSibling = parent->GetFirstChild(nsnull); } } return nextSibling; } void nsFrameContentIterator::Last() { // Starting with the first child walk and find the last child mCurrentChild = nsnull; nsIFrame* nextChild = mParentFrame->GetFirstChild(nsnull); while (nextChild) { mCurrentChild = nextChild; nextChild = ::GetNextChildFrame(mPresContext, nextChild); } mIsDone = !mCurrentChild; } void nsFrameContentIterator::Next() { nsIFrame* nextChild = ::GetNextChildFrame(mPresContext, mCurrentChild); if (nextChild) { // Advance to the next child mCurrentChild = nextChild; // If we're at the end then the collection is at the end mIsDone = (nsnull == ::GetNextChildFrame(mPresContext, mCurrentChild)); return; } // No next frame, we're done. mIsDone = PR_TRUE; } static nsIFrame* GetPrevChildFrame(nsIPresContext* aPresContext, nsIFrame* aFrame) { NS_PRECONDITION(aFrame, "null pointer"); // Get its previous sibling. Because we have a singly linked list we // need to search from the first child nsIFrame* parent = aFrame->GetParent(); nsIFrame* prevSibling; nsIFrame* firstChild = parent->GetFirstChild(nsnull); NS_ASSERTION(firstChild, "parent has no first child"); nsFrameList frameList(firstChild); prevSibling = frameList.GetPrevSiblingFor(aFrame); // If there's no previous sibling, then check if the parent frame // has a prev-in-flow and look there if (!prevSibling) { parent->GetPrevInFlow(&parent); if (parent) { firstChild = parent->GetFirstChild(nsnull); frameList.SetFrames(firstChild); prevSibling = frameList.LastChild(); } } // Get the first-in-flow while (PR_TRUE) { nsIFrame* prevInFlow; prevSibling->GetPrevInFlow(&prevInFlow); if (prevInFlow) { prevSibling = prevInFlow; } else { break; } } return prevSibling; } void nsFrameContentIterator::Prev() { nsIFrame* prevChild = ::GetPrevChildFrame(mPresContext, mCurrentChild); if (prevChild) { // Make the previous child the current child mCurrentChild = prevChild; // If we're at the beginning then the collection is at the end mIsDone = (nsnull == ::GetPrevChildFrame(mPresContext, mCurrentChild)); return; } // No previous frame, we're done. mIsDone = PR_TRUE; } nsIContent * nsFrameContentIterator::GetCurrentNode() { if (mCurrentChild && !mIsDone) { return mCurrentChild->GetContent(); } return nsnull; } PRBool nsFrameContentIterator::IsDone() { return mIsDone; } nsresult nsFrameContentIterator::PositionAt(nsIContent* aCurNode) { // Starting with the first child frame search for the child frame // with the matching content object nsIFrame* child = mParentFrame->GetFirstChild(nsnull); while (child) { if (child->GetContent() == aCurNode) { break; } child = ::GetNextChildFrame(mPresContext, child); } if (child) { // Make it the current child mCurrentChild = child; mIsDone = PR_FALSE; } return NS_OK; } nsresult NS_NewFrameContentIterator(nsIPresContext* aPresContext, nsIFrame* aFrame, nsIContentIterator** aIterator) { NS_ENSURE_ARG_POINTER(aIterator); if (!aIterator) { return NS_ERROR_NULL_POINTER; } NS_ENSURE_ARG_POINTER(aFrame); if (!aFrame) { return NS_ERROR_NULL_POINTER; } // Make sure the frame corresponds to generated content NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT, "unexpected frame"); nsFrameContentIterator* it = new nsFrameContentIterator(aPresContext, aFrame); if (!it) { return NS_ERROR_OUT_OF_MEMORY; } return it->QueryInterface(NS_GET_IID(nsIContentIterator), (void **)aIterator); }