forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			4838 lines
		
	
	
	
		
			149 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			4838 lines
		
	
	
	
		
			149 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 "License"); you may not use this file except in
 | |
|  * compliance with the License.  You may obtain a copy of the License at
 | |
|  * http://www.mozilla.org/NPL/
 | |
|  *
 | |
|  * Software distributed under the License is distributed on an "AS IS"
 | |
|  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
 | |
|  * the License for the specific language governing rights and limitations
 | |
|  * under the License.
 | |
|  *
 | |
|  * The Original Code is Mozilla Communicator client code.
 | |
|  *
 | |
|  * The Initial Developer of the Original Code is Netscape Communications
 | |
|  * Corporation.  Portions created by Netscape are Copyright (C) 1998
 | |
|  * Netscape Communications Corporation.  All Rights Reserved.
 | |
|  */
 | |
| #include "nsBlockFrame.h"
 | |
| #include "nsBlockReflowContext.h"
 | |
| #include "nsBlockBandData.h"
 | |
| #include "nsBulletFrame.h"
 | |
| #include "nsLineBox.h"
 | |
| 
 | |
| #include "nsFrameReflowState.h"
 | |
| #include "nsLineLayout.h"
 | |
| #include "nsInlineReflow.h"
 | |
| #include "nsPlaceholderFrame.h"
 | |
| #include "nsStyleConsts.h"
 | |
| #include "nsHTMLIIDs.h"
 | |
| #include "nsCSSRendering.h"
 | |
| #include "nsIPresContext.h"
 | |
| #include "nsIPresShell.h"
 | |
| #include "nsIReflowCommand.h"
 | |
| #include "nsISpaceManager.h"
 | |
| #include "nsIStyleContext.h"
 | |
| #include "nsIView.h"
 | |
| #include "nsIFontMetrics.h"
 | |
| #include "nsHTMLParts.h"
 | |
| #include "nsHTMLAtoms.h"
 | |
| #include "nsHTMLValue.h"
 | |
| #include "nsDOMEvent.h"
 | |
| #include "nsIHTMLContent.h"
 | |
| #include "prprf.h"
 | |
| #include "nsLayoutAtoms.h"
 | |
| 
 | |
| // XXX temporary for :first-letter support
 | |
| #include "nsITextContent.h"
 | |
| static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);/* XXX */
 | |
| 
 | |
| // XXX for IsEmptyLine
 | |
| #include "nsTextFragment.h"
 | |
| 
 | |
| // XXX TODO:
 | |
| 
 | |
| // If I can add in IsAPlaceHolder then we can remove the mFloaters
 | |
| // void array from the nsLineBox
 | |
| 
 | |
| #ifdef NS_DEBUG
 | |
| #undef NOISY_FIRST_LINE
 | |
| #undef REALLY_NOISY_FIRST_LINE
 | |
| #undef NOISY_FIRST_LETTER
 | |
| #undef NOISY_MAX_ELEMENT_SIZE
 | |
| #undef NOISY_RUNIN
 | |
| #undef NOISY_FLOATER_CLEARING
 | |
| #undef NOISY_INCREMENTAL_REFLOW
 | |
| #undef REFLOW_STATUS_COVERAGE
 | |
| #else
 | |
| #undef NOISY_FIRST_LINE
 | |
| #undef REALLY_NOISY_FIRST_LINE
 | |
| #undef NOISY_FIRST_LETTER
 | |
| #undef NOISY_MAX_ELEMENT_SIZE
 | |
| #undef NOISY_RUNIN
 | |
| #undef NOISY_FLOATER_CLEARING
 | |
| #undef NOISY_INCREMENTAL_REFLOW
 | |
| #undef REFLOW_STATUS_COVERAGE
 | |
| #endif
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| 
 | |
| // Debugging support code
 | |
| 
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
| static PRInt32 gNoiseIndent;
 | |
| static const char* kReflowCommandType[] = {
 | |
|   "FrameAppended",
 | |
|   "FrameInserted",
 | |
|   "FrameRemoved",
 | |
|   "ContentChanged",
 | |
|   "StyleChanged",
 | |
|   "PullupReflow",
 | |
|   "PushReflow",
 | |
|   "CheckPullupReflow",
 | |
|   "UserDefined",
 | |
| };
 | |
| #endif
 | |
| 
 | |
| #ifdef REALLY_NOISY_FIRST_LINE
 | |
| static void
 | |
| DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
 | |
| {
 | |
|   fputs(gap, stdout);
 | |
|   aFrame->ListTag(stdout);
 | |
|   fputs(name, out);
 | |
|   printf(": ");
 | |
|   nsIStyleContext* sc;
 | |
|   aFrame->GetStyleContext(sc);
 | |
|   while (nsnull != sc) {
 | |
|     nsIStyleContext* psc;
 | |
|     printf("%p ", sc);
 | |
|     psc = sc->GetParent();
 | |
|     NS_RELEASE(sc);
 | |
|     sc = psc;
 | |
|   }
 | |
|   printf("\n");
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef NS_DEBUG
 | |
| static void
 | |
| VerifyLineLength(nsLineBox* aLine)
 | |
| {
 | |
|   nsIFrame* frame = aLine->mFirstChild;
 | |
|   PRInt32 n = aLine->mChildCount;
 | |
|   while (--n >= 0) {
 | |
|     frame->GetNextSibling(&frame);
 | |
|   }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef REFLOW_STATUS_COVERAGE
 | |
| static void
 | |
| RecordReflowStatus(PRBool aChildIsBlock, nsReflowStatus aFrameReflowStatus)
 | |
| {
 | |
|   static PRUint32 record[2];
 | |
| 
 | |
|   // 0: child-is-block
 | |
|   // 1: child-is-inline
 | |
|   PRIntn index = 0;
 | |
|   if (!aChildIsBlock) index |= 1;
 | |
| 
 | |
|   // Compute new status
 | |
|   PRUint32 newS = record[index];
 | |
|   if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) {
 | |
|     if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
 | |
|       newS |= 1;
 | |
|     }
 | |
|     else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
 | |
|       newS |= 2;
 | |
|     }
 | |
|     else {
 | |
|       newS |= 4;
 | |
|     }
 | |
|   }
 | |
|   else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
 | |
|     newS |= 8;
 | |
|   }
 | |
|   else {
 | |
|     newS |= 16;
 | |
|   }
 | |
| 
 | |
|   // Log updates to the status that yield different values
 | |
|   if (record[index] != newS) {
 | |
|     record[index] = newS;
 | |
|     printf("record(%d): %02x %02x\n", index, record[0], record[1]);
 | |
|   }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| 
 | |
| class nsBlockReflowState : public nsFrameReflowState {
 | |
| public:
 | |
|   nsBlockReflowState(nsIPresContext& aPresContext,
 | |
|                      const nsHTMLReflowState& aReflowState,
 | |
|                      const nsHTMLReflowMetrics& aMetrics,
 | |
|                      nsLineLayout* aLineLayout);
 | |
| 
 | |
|   ~nsBlockReflowState();
 | |
| 
 | |
|   /**
 | |
|    * Update the mCurrentBand data based on the current mY position.
 | |
|    */
 | |
|   void GetAvailableSpace();
 | |
| 
 | |
|   void InitFloater(nsPlaceholderFrame* aPlaceholderFrame);
 | |
| 
 | |
|   void AddFloater(nsPlaceholderFrame* aPlaceholderFrame,
 | |
|                   PRBool aInitialReflow);
 | |
| 
 | |
|   void PlaceFloater(nsPlaceholderFrame* aFloater, PRBool& aIsLeftFloater);
 | |
| 
 | |
|   void PlaceBelowCurrentLineFloaters(nsVoidArray* aFloaters);
 | |
| 
 | |
|   void PlaceCurrentLineFloaters(nsVoidArray* aFloaters);
 | |
| 
 | |
|   void ClearFloaters(nscoord aY, PRUint8 aBreakType);
 | |
| 
 | |
|   PRBool IsLeftMostChild(nsIFrame* aFrame);
 | |
| 
 | |
|   PRBool IsAdjacentWithTop() const {
 | |
|     return mY == mBorderPadding.top;
 | |
|   }
 | |
| 
 | |
|   PRBool ShouldApplyTopMargin() const {
 | |
|     return mIsMarginRoot || !IsAdjacentWithTop();
 | |
|   }
 | |
| 
 | |
|   nsLineLayout* mLineLayout;
 | |
|   nsInlineReflow* mInlineReflow;
 | |
| 
 | |
|   nsISpaceManager* mSpaceManager;
 | |
|   nscoord mSpaceManagerX, mSpaceManagerY;
 | |
|   nsBlockFrame* mBlock;
 | |
|   nsBlockFrame* mNextInFlow;
 | |
| 
 | |
|   nsReflowStatus mReflowStatus;
 | |
| 
 | |
|   nsBlockFrame* mRunInFromFrame;
 | |
|   nsBlockFrame* mRunInToFrame;
 | |
| 
 | |
|   PRUint8 mTextAlign;
 | |
| 
 | |
|   PRUintn mPrevMarginFlags;
 | |
| 
 | |
|   nscoord mBottomEdge;          // maximum Y
 | |
| 
 | |
|   PRBool mUnconstrainedWidth;
 | |
|   PRBool mUnconstrainedHeight;
 | |
|   nscoord mY;
 | |
|   nscoord mKidXMost;
 | |
|   nscoord mAscent, mDescent;
 | |
| 
 | |
|   // Previous child. This is used when pulling up a frame to update
 | |
|   // the sibling list.
 | |
|   nsIFrame* mPrevChild;
 | |
| 
 | |
|   nsVoidArray mPendingFloaters;
 | |
| 
 | |
|   nsLineBox* mCurrentLine;
 | |
|   nsLineBox* mPrevLine;
 | |
| 
 | |
|   // The next list ordinal for counting list bullets
 | |
|   PRInt32 mNextListOrdinal;
 | |
| 
 | |
|   nsBlockBandData mCurrentBand;
 | |
|   nsRect mAvailSpaceRect;
 | |
| };
 | |
| 
 | |
| // XXX This is vile. Make it go away
 | |
| void
 | |
| nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame)
 | |
| {
 | |
|   mBlockReflowState->InitFloater(aFrame);
 | |
| }
 | |
| void
 | |
| nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
 | |
| {
 | |
|   mBlockReflowState->AddFloater(aFrame, PR_FALSE);
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| 
 | |
| nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
 | |
|                                        const nsHTMLReflowState& aReflowState,
 | |
|                                        const nsHTMLReflowMetrics& aMetrics,
 | |
|                                        nsLineLayout* aLineLayout)
 | |
|   : nsFrameReflowState(aPresContext, aReflowState, aMetrics)
 | |
| {
 | |
|   mInlineReflow = nsnull;
 | |
| 
 | |
|   mLineLayout = aLineLayout;
 | |
| 
 | |
|   mSpaceManager = aReflowState.spaceManager;
 | |
| 
 | |
|   // Translate into our content area and then save the 
 | |
|   // coordinate system origin for later.
 | |
|   mSpaceManager->Translate(mBorderPadding.left, mBorderPadding.top);
 | |
|   mSpaceManager->GetTranslation(mSpaceManagerX, mSpaceManagerY);
 | |
| 
 | |
|   mReflowStatus = NS_FRAME_COMPLETE;
 | |
| 
 | |
|   mPresContext = aPresContext;
 | |
|   mBlock = (nsBlockFrame*) frame;
 | |
|   mBlock->GetNextInFlow((nsIFrame*&)mNextInFlow);
 | |
|   mKidXMost = 0;
 | |
| 
 | |
|   mRunInFromFrame = nsnull;
 | |
|   mRunInToFrame = nsnull;
 | |
| 
 | |
|   mY = mAscent = mDescent = 0;
 | |
|   mUnconstrainedWidth = availableWidth == NS_UNCONSTRAINEDSIZE;
 | |
|   mUnconstrainedHeight = availableHeight == NS_UNCONSTRAINEDSIZE;
 | |
| #ifdef NS_DEBUG
 | |
|   if (!mUnconstrainedWidth && (availableWidth > 100000)) {
 | |
|     mBlock->ListTag(stdout);
 | |
|     printf(": bad parent: maxSize WAS %d,%d\n", availableWidth, availableHeight);
 | |
|     if (availableWidth > 100000) {
 | |
|       availableWidth = NS_UNCONSTRAINEDSIZE;
 | |
|       mUnconstrainedWidth = PR_TRUE;
 | |
|     }
 | |
|   }
 | |
|   if (!mUnconstrainedHeight && (availableHeight > 100000)) {
 | |
|     mBlock->ListTag(stdout);
 | |
|     printf(": bad parent: maxSize WAS %d,%d\n", availableWidth, availableHeight);
 | |
|     if (availableHeight > 100000) {
 | |
|       availableHeight = NS_UNCONSTRAINEDSIZE;
 | |
|       mUnconstrainedHeight = PR_TRUE;
 | |
|     }
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   mTextAlign = mStyleText->mTextAlign;
 | |
| 
 | |
|   nscoord lr = mBorderPadding.left + mBorderPadding.right;
 | |
|   mY = mBorderPadding.top;
 | |
| 
 | |
|   if (HaveFixedContentWidth()) {
 | |
|     // The CSS2 spec says that the width attribute defines the width
 | |
|     // of the "content area" which does not include the border
 | |
|     // padding. So we add those back in.
 | |
|     mBorderArea.width = computedWidth + lr;
 | |
|     mContentArea.width = computedWidth;
 | |
|   }
 | |
|   else {
 | |
|     if (mUnconstrainedWidth) {
 | |
|       mBorderArea.width = NS_UNCONSTRAINEDSIZE;
 | |
|       mContentArea.width = NS_UNCONSTRAINEDSIZE;
 | |
|     }
 | |
|     else {
 | |
|       mBorderArea.width = availableWidth;
 | |
|       mContentArea.width = availableWidth - lr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   mBorderArea.height = availableHeight;
 | |
|   mContentArea.height = availableHeight;
 | |
|   mBottomEdge = availableHeight;
 | |
|   if (!mUnconstrainedHeight) {
 | |
|     mBottomEdge -= mBorderPadding.bottom;
 | |
|   }
 | |
|   mCurrentBand.Init(mSpaceManager, mContentArea);
 | |
| 
 | |
|   mPrevChild = nsnull;
 | |
|   mCurrentLine = nsnull;
 | |
|   mPrevLine = nsnull;
 | |
| }
 | |
| 
 | |
| nsBlockReflowState::~nsBlockReflowState()
 | |
| {
 | |
|   // Restore the coordinate system
 | |
|   mSpaceManager->Translate(-mBorderPadding.left, -mBorderPadding.top);
 | |
| }
 | |
| 
 | |
| // Get the available reflow space for the current y coordinate. The
 | |
| // available space is relative to our coordinate system (0,0) is our
 | |
| // upper left corner.
 | |
| void
 | |
| nsBlockReflowState::GetAvailableSpace()
 | |
| {
 | |
|   nsISpaceManager* sm = mSpaceManager;
 | |
| 
 | |
| #ifdef NS_DEBUG
 | |
|   // Verify that the caller setup the coordinate system properly
 | |
|   nscoord wx, wy;
 | |
|   sm->GetTranslation(wx, wy);
 | |
|   NS_ASSERTION((wx == mSpaceManagerX) && (wy == mSpaceManagerY),
 | |
|                "bad coord system");
 | |
| #endif
 | |
| 
 | |
|   mCurrentBand.GetAvailableSpace(mY - mBorderPadding.top, mAvailSpaceRect);
 | |
| 
 | |
|   NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
 | |
|      ("nsBlockReflowState::GetAvailableSpace: band={%d,%d,%d,%d} count=%d",
 | |
|       mAvailSpaceRect.x, mAvailSpaceRect.y,
 | |
|       mAvailSpaceRect.width, mAvailSpaceRect.height,
 | |
|       mCurrentBand.GetTrapezoidCount()));
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
|   if (reason == eReflowReason_Incremental) {
 | |
|     nsFrame::IndentBy(stdout, gNoiseIndent);
 | |
|     printf("GetAvailableSpace: band=%d,%d,%d,%d count=%d\n",
 | |
|            mAvailSpaceRect.x, mAvailSpaceRect.y,
 | |
|            mAvailSpaceRect.width, mAvailSpaceRect.height,
 | |
|            mCurrentBand.GetTrapezoidCount());
 | |
|   }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| 
 | |
| #define NS_BLOCK_FRAME_CID \
 | |
|  { 0xa6cf90df, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}}
 | |
| 
 | |
| static const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID;
 | |
| 
 | |
| nsresult
 | |
| NS_NewBlockFrame(nsIFrame*& aNewFrame, PRUint32 aFlags)
 | |
| {
 | |
|   nsBlockFrame* it = new nsBlockFrame;
 | |
|   if (nsnull == it) {
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
|   it->SetFlags(aFlags);
 | |
|   aNewFrame = it;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsBlockFrame::nsBlockFrame()
 | |
| {
 | |
| }
 | |
| 
 | |
| nsBlockFrame::~nsBlockFrame()
 | |
| {
 | |
|   NS_IF_RELEASE(mFirstLineStyle);
 | |
|   NS_IF_RELEASE(mFirstLetterStyle);
 | |
|   nsTextRun::DeleteTextRuns(mTextRuns);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::DeleteFrame(nsIPresContext& aPresContext)
 | |
| {
 | |
|   // When we have a bullet frame and it's not in our child list then
 | |
|   // we need to delete it ourselves (this is the common case for
 | |
|   // list-item's that have outside bullets).
 | |
|   if ((nsnull != mBullet) &&
 | |
|       ((nsnull == mLines) || (mBullet != mLines->mFirstChild))) {
 | |
|     mBullet->DeleteFrame(aPresContext);
 | |
|     mBullet = nsnull;
 | |
|   }
 | |
| 
 | |
|   nsLineBox::DeleteLineList(aPresContext, mLines);
 | |
|   nsLineBox::DeleteLineList(aPresContext, mOverflowLines);
 | |
| 
 | |
|   mFloaters.DeleteFrames(aPresContext);
 | |
| 
 | |
|   return nsBlockFrameSuper::DeleteFrame(aPresContext);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
 | |
| {
 | |
|   if (NULL == aInstancePtr) {
 | |
|     return NS_ERROR_NULL_POINTER;
 | |
|   }
 | |
|   if (aIID.Equals(kBlockFrameCID)) {
 | |
|     nsBlockFrame* tmp = this;
 | |
|     *aInstancePtr = (void*) tmp;
 | |
|     return NS_OK;
 | |
|   }
 | |
|   return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr);
 | |
| }
 | |
| 
 | |
| static nsresult
 | |
| ReResolveLineList(nsIPresContext* aPresContext,
 | |
|                   nsLineBox* aLine,
 | |
|                   nsIStyleContext* aStyleContext)
 | |
| {
 | |
|   nsresult rv = NS_OK;
 | |
|   while (nsnull != aLine) {
 | |
|     nsIFrame* child = aLine->mFirstChild;
 | |
|     PRInt32 n = aLine->mChildCount;
 | |
|     while ((--n >= 0) && NS_SUCCEEDED(rv)) {
 | |
|       rv = child->ReResolveStyleContext(aPresContext, aStyleContext);
 | |
|       child->GetNextSibling(&child);
 | |
|     }
 | |
|     aLine = aLine->mNext;
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::ReResolveStyleContext(nsIPresContext* aPresContext,
 | |
|                                     nsIStyleContext* aParentContext)
 | |
| {
 | |
|   nsIStyleContext* oldContext = mStyleContext;
 | |
| 
 | |
|   // NOTE: using nsFrame's ReResolveStyleContext method to avoid
 | |
|   // useless version in base classes.
 | |
|   nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   if (oldContext != mStyleContext) {
 | |
|     // Re-resolve the :first-line pseudo style context
 | |
|     if (nsnull == mPrevInFlow) {
 | |
|       nsIStyleContext* newFirstLineStyle =
 | |
|         aPresContext->ProbePseudoStyleContextFor(mContent,
 | |
|                                                  nsHTMLAtoms::firstLinePseudo,
 | |
|                                                  mStyleContext);
 | |
|       if (newFirstLineStyle != mFirstLineStyle) {
 | |
|         NS_IF_RELEASE(mFirstLineStyle);
 | |
|         mFirstLineStyle = newFirstLineStyle;
 | |
|       }
 | |
|       else {
 | |
|         NS_IF_RELEASE(newFirstLineStyle);
 | |
|       }
 | |
| 
 | |
|       // Re-resolve the :first-letter pseudo style context
 | |
|       nsIStyleContext* newFirstLetterStyle =
 | |
|         aPresContext->ProbePseudoStyleContextFor(mContent,
 | |
|                                                  nsHTMLAtoms::firstLetterPseudo,
 | |
|                                                  (nsnull != mFirstLineStyle
 | |
|                                                   ? mFirstLineStyle
 | |
|                                                   : mStyleContext));
 | |
|       if (newFirstLetterStyle != mFirstLetterStyle) {
 | |
|         NS_IF_RELEASE(mFirstLetterStyle);
 | |
|         mFirstLetterStyle = newFirstLetterStyle;
 | |
|       }
 | |
|       else {
 | |
|         NS_IF_RELEASE(newFirstLetterStyle);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Update the child frames on each line
 | |
|     nsLineBox* line = mLines;
 | |
|     while (nsnull != line) {
 | |
|       nsIFrame* child = line->mFirstChild;
 | |
|       PRInt32 n = line->mChildCount;
 | |
|       while ((--n >= 0) && NS_SUCCEEDED(rv)) {
 | |
|         if (line == mLines) {
 | |
|           rv = child->ReResolveStyleContext(aPresContext,
 | |
|                                             (nsnull != mFirstLineStyle
 | |
|                                              ? mFirstLineStyle
 | |
|                                              : mStyleContext));
 | |
|         }
 | |
|         else {
 | |
|           rv = child->ReResolveStyleContext(aPresContext, mStyleContext);
 | |
|         }
 | |
|         child->GetNextSibling(&child);
 | |
|       }
 | |
|       line = line->mNext;
 | |
|     }
 | |
| 
 | |
|     if (NS_SUCCEEDED(rv) && (nsnull != mOverflowLines)) {
 | |
|       rv = ReResolveLineList(aPresContext, mOverflowLines, mStyleContext);
 | |
|     }
 | |
|     if (NS_SUCCEEDED(rv) && (nsnull != mPrevInFlow)) {
 | |
|       nsLineBox* lines = ((nsBlockFrame*)mPrevInFlow)->mOverflowLines;
 | |
|       if (nsnull != lines) {
 | |
|         rv = ReResolveLineList(aPresContext, lines, mStyleContext);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::IsSplittable(nsSplittableType& aIsSplittable) const
 | |
| {
 | |
|   aIsSplittable = NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| static void
 | |
| ListTextRuns(FILE* out, PRInt32 aIndent, nsTextRun* aRuns)
 | |
| {
 | |
|   while (nsnull != aRuns) {
 | |
|     aRuns->List(out, aIndent);
 | |
|     aRuns = aRuns->GetNext();
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_METHOD
 | |
| nsBlockFrame::List(FILE* out, PRInt32 aIndent) const
 | |
| {
 | |
|   PRInt32 i;
 | |
| 
 | |
|   nsAutoString tagString;
 | |
|   if (nsnull != mContent) {
 | |
|     nsIAtom* tag;
 | |
|     mContent->GetTag(tag);
 | |
|     if (tag != nsnull)  {
 | |
|       tag->ToString(tagString);
 | |
|       NS_RELEASE(tag);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Indent
 | |
|   for (i = aIndent; --i >= 0; ) fputs("  ", out);
 | |
| 
 | |
|   // Output the tag
 | |
|   ListTag(out);
 | |
|   nsIView* view;
 | |
|   GetView(&view);
 | |
|   if (nsnull != view) {
 | |
|     fprintf(out, " [view=%p]", view);
 | |
|   }
 | |
| 
 | |
|   // Output the flow linkage
 | |
|   if (nsnull != mPrevInFlow) {
 | |
|     fprintf(out, " prev-in-flow=%p", mPrevInFlow);
 | |
|   }
 | |
|   if (nsnull != mNextInFlow) {
 | |
|     fprintf(out, " next-in-flow=%p", mNextInFlow);
 | |
|   }
 | |
| 
 | |
|   // Output the rect and state
 | |
|   out << mRect;
 | |
|   if (0 != mState) {
 | |
|     fprintf(out, " [state=%08x]", mState);
 | |
|   }
 | |
|   fputs("<\n", out);
 | |
|   aIndent++;
 | |
| 
 | |
|   // Output the lines
 | |
|   if (nsnull != mLines) {
 | |
|     nsLineBox* line = mLines;
 | |
|     while (nsnull != line) {
 | |
|       line->List(out, aIndent);
 | |
|       line = line->mNext;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsIAtom* listName = nsnull;
 | |
|   PRInt32 listIndex = 0;
 | |
|   for (;;) {
 | |
|     nsIFrame* kid;
 | |
|     GetAdditionalChildListName(listIndex++, &listName);
 | |
|     if (nsnull == listName) {
 | |
|       break;
 | |
|     }
 | |
|     FirstChild(listName, &kid);
 | |
|     if (nsnull != kid) {
 | |
|       IndentBy(out, aIndent);
 | |
|       nsAutoString tmp;
 | |
|       if (nsnull != listName) {
 | |
|         listName->ToString(tmp);
 | |
|         fputs(tmp, out);
 | |
|       }
 | |
|       fputs("<\n", out);
 | |
|       while (nsnull != kid) {
 | |
|         kid->List(out, aIndent + 1);
 | |
|         kid->GetNextSibling(&kid);
 | |
|       }
 | |
|       IndentBy(out, aIndent);
 | |
|       fputs(">\n", out);
 | |
|     }
 | |
|     NS_IF_RELEASE(listName);
 | |
|   }
 | |
| 
 | |
|   // Output the text-runs
 | |
|   if (nsnull != mTextRuns) {
 | |
|     for (i = aIndent; --i >= 0; ) fputs("  ", out);
 | |
|     fputs("text-runs <\n", out);
 | |
| 
 | |
|     ListTextRuns(out, aIndent + 1, mTextRuns);
 | |
| 
 | |
|     for (i = aIndent; --i >= 0; ) fputs("  ", out);
 | |
|     fputs(">\n", out);
 | |
|   }
 | |
| 
 | |
|   aIndent--;
 | |
|   for (i = aIndent; --i >= 0; ) fputs("  ", out);
 | |
|   fputs(">\n", out);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::GetFrameName(nsString& aResult) const
 | |
| {
 | |
|   return MakeFrameName("Block", aResult);
 | |
| }
 | |
| 
 | |
| /////////////////////////////////////////////////////////////////////////////
 | |
| // Child frame enumeration
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::FirstChild(nsIAtom* aListName, nsIFrame** aFirstChild) const
 | |
| {
 | |
|   NS_PRECONDITION(nsnull != aFirstChild, "null OUT parameter pointer");
 | |
|   if (nsnull == aListName) {
 | |
|     *aFirstChild = (nsnull != mLines) ? mLines->mFirstChild : nsnull;
 | |
|     return NS_OK;
 | |
|   }
 | |
|   else if (aListName == nsLayoutAtoms::floaterList) {
 | |
|     *aFirstChild = mFloaters.FirstChild();
 | |
|     return NS_OK;
 | |
|   }
 | |
|   else if (aListName == nsLayoutAtoms::bulletList) {
 | |
|     *aFirstChild = mBullet;
 | |
|     return NS_OK;
 | |
|   }
 | |
|   *aFirstChild = nsnull;
 | |
|   return NS_ERROR_INVALID_ARG;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::GetAdditionalChildListName(PRInt32   aIndex,
 | |
|                                          nsIAtom** aListName) const
 | |
| {
 | |
|   NS_PRECONDITION(nsnull != aListName, "null OUT parameter pointer");
 | |
|   if (aIndex < 0) {
 | |
|     return NS_ERROR_INVALID_ARG;
 | |
|   }
 | |
|   *aListName = nsnull;
 | |
|   switch (aIndex) {
 | |
|   case NS_BLOCK_FRAME_FLOATER_LIST_INDEX:
 | |
|     *aListName = nsLayoutAtoms::floaterList;
 | |
|     NS_ADDREF(*aListName);
 | |
|     break;
 | |
|   case NS_BLOCK_FRAME_BULLET_LIST_INDEX:
 | |
|     *aListName = nsLayoutAtoms::bulletList;
 | |
|     NS_ADDREF(*aListName);
 | |
|     break;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::IsPercentageBase(PRBool& aBase) const
 | |
| {
 | |
|   aBase = PR_TRUE;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////////
 | |
| // Frame structure methods
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////////
 | |
| // Reflow methods
 | |
| 
 | |
| #if 0
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::ComputeCollapsedMargins(nsIPresContext& aPresContext,
 | |
|                                        const nsReflowState* aParentReflowState,
 | |
|                                        nscoord* aTopMarginResult,
 | |
|                                        nscoord* aBottomMarginResult)
 | |
| {
 | |
|   NS_PRECONDITION((nsnull != aTopMarginResult) &&
 | |
|                   (nsnull != aBottomMarginResult), "null ptr");
 | |
|   if ((nsnull != aTopMarginResult) || (nsnull != aBottomMarginResult)) {
 | |
|     nsMargin myMargin(0, 0, 0, 0);
 | |
|     if (0 == (BLOCK_IS_INLINE & mFlags)) {
 | |
|       nsHTMLReflowState::ComputeMarginFor(this, aParentReflowState, myMargin);
 | |
|     }
 | |
| 
 | |
|     // Find the first appropriate line (skipping over an empty line if
 | |
|     // present) that contains a block that can be used for a
 | |
|     // parent-child margin collapse.
 | |
|     nsLineBox* firstLine = nsnull;
 | |
|     nscoord firstLineTopMargin = 0;
 | |
|     if (nsnull != aTopMarginResult) {
 | |
|       nsLineBox* line = mLines;
 | |
|       if (nsnull != line) {
 | |
|         if (line->IsEmptyLine()) {
 | |
|           line = line->mNext;
 | |
|         }
 | |
|         if ((nsnull != line) && line->IsBlock()) {
 | |
|           firstLine = line;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Find the last appropriate line (skipping over an empty line)...
 | |
|     nsLineBox* lastLine = nsnull;
 | |
|     nscoord lastLineBottomMargin = 0;
 | |
|     if (nsnull != aBottomMarginResult) {
 | |
|       // Find the last line (line) and the previous to the last line
 | |
|       // (prevLine)
 | |
|       nsLineBox* line = mLines;
 | |
|       nsLineBox* prevLine = nsnull;
 | |
|       while (nsnull != line) {
 | |
|         nsLineBox* next = line->mNext;
 | |
|         if (nsnull == next) {
 | |
|           break;
 | |
|         }
 | |
|         prevLine = line;
 | |
|         line = next;
 | |
|       }
 | |
|       if (nsnull != line) {
 | |
|         if (line->IsEmptyLine()) {
 | |
|           line = prevLine;
 | |
|         }
 | |
|         if ((nsnull != line) && line->IsBlock()) {
 | |
|           lastLine = line;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (nsnull != firstLine) {
 | |
|       if (lastLine == firstLine) {
 | |
|         ComputeCollapsedMargins(aPresContext, nsnull,
 | |
|                                 &firstLineTopMargin,
 | |
|                                 &lastLineBottomMargin);
 | |
|       }
 | |
|       else {
 | |
|         ComputeCollapsedMargins(aPresContext, nsnull,
 | |
|                                 &firstLineTopMargin,
 | |
|                                 nsnull);
 | |
|       }
 | |
|       firstLineTopMargin
 | |
|     }
 | |
|     else if (nsnull != lastLine) {
 | |
|       ComputeCollapsedMargins(aPresContext, nsnull,
 | |
|                               nsnull,
 | |
|                               &lastLineBottomMargin);
 | |
|     }
 | |
| 
 | |
|     if ((nsnull != aTopMarginResult) && (nsnull != aBottomMarginResult)) {
 | |
|       nscoord topMargin =
 | |
|         nsBlockReflowContext::MaxMargin(myMargin.top, firstLineTopMargin);
 | |
|       *aTopMarginResult = topMargin;
 | |
|       nscoord bottomMargin =
 | |
|         nsBlockReflowContext::MaxMargin(myMargin.bottom, lastLineBottomMargin);
 | |
|       *aBottomMarginResult = bottomMargin;
 | |
|     }
 | |
|     else if (nsnull != aTopMarginResult) {
 | |
|       nscoord topMargin =
 | |
|         nsBlockReflowContext::MaxMargin(myMargin.top, firstLineTopMargin);
 | |
|       *aTopMarginResult = topMargin;
 | |
|     }
 | |
|     else {
 | |
|       nscoord bottomMargin =
 | |
|         nsBlockReflowContext::MaxMargin(myMargin.bottom, lastLineBottomMargin);
 | |
|       *aBottomMarginResult = bottomMargin;
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::Reflow(nsIPresContext&          aPresContext,
 | |
|                       nsHTMLReflowMetrics&     aMetrics,
 | |
|                       const nsHTMLReflowState& aReflowState,
 | |
|                       nsReflowStatus&          aStatus)
 | |
| {
 | |
|   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
 | |
|                  ("enter nsBlockFrame::Reflow: maxSize=%d,%d reason=%d",
 | |
|                   aReflowState.availableWidth,
 | |
|                   aReflowState.availableHeight,
 | |
|                   aReflowState.reason));
 | |
| 
 | |
|   // Replace parent provided reflow state with our own significantly
 | |
|   // more extensive version.
 | |
|   nsLineLayout ll(aPresContext, aReflowState.spaceManager);
 | |
|   nsLineLayout* lineLayout = ≪
 | |
|   nsBlockReflowState state(aPresContext, aReflowState, aMetrics, lineLayout);
 | |
|   if (NS_BLOCK_MARGIN_ROOT & mFlags) {
 | |
|     state.mIsMarginRoot = PR_TRUE;
 | |
|   }
 | |
|   lineLayout->Init(&state);
 | |
| 
 | |
|   // Prepare inline-reflow engine
 | |
|   nsInlineReflow inlineReflow(*lineLayout, state, this, PR_TRUE);
 | |
|   state.mInlineReflow = &inlineReflow;
 | |
|   lineLayout->PushInline(&inlineReflow);
 | |
| 
 | |
|   nsresult rv = NS_OK;
 | |
|   nsIFrame* target;
 | |
|   switch (state.reason) {
 | |
|   case eReflowReason_Initial:
 | |
|     rv = PrepareInitialReflow(state);
 | |
|     mState &= ~NS_FRAME_FIRST_REFLOW;
 | |
|     break;
 | |
| 
 | |
|   case eReflowReason_Incremental:
 | |
|     state.reflowCommand->GetTarget(target);
 | |
|     if (this == target) {
 | |
|       nsIReflowCommand::ReflowType type;
 | |
|       state.reflowCommand->GetType(type);
 | |
|       switch (type) {
 | |
|       case nsIReflowCommand::FrameAppended:
 | |
|         rv = PrepareFrameAppendedReflow(state);
 | |
|         break;
 | |
|       case nsIReflowCommand::FrameInserted:
 | |
|         rv = PrepareFrameInsertedReflow(state);
 | |
|         break;
 | |
|       case nsIReflowCommand::FrameRemoved:
 | |
|         rv = PrepareFrameRemovedReflow(state);
 | |
|         break;
 | |
|       case nsIReflowCommand::StyleChanged:
 | |
|         rv = PrepareStyleChangedReflow(state);
 | |
|         break;
 | |
|       case nsIReflowCommand::ReflowDirty:
 | |
|         break;
 | |
|       default:
 | |
|         // Map any other incremental operations into full reflows
 | |
|         rv = PrepareResizeReflow(state);
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       // Get next frame in reflow command chain
 | |
|       state.reflowCommand->GetNext(state.mNextRCFrame);
 | |
| 
 | |
|       // Now do the reflow
 | |
|       rv = PrepareChildIncrementalReflow(state);
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|   case eReflowReason_Resize:
 | |
|   default:
 | |
|     DrainOverflowLines();
 | |
|     rv = PrepareResizeReflow(state);
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   // Now reflow...
 | |
|   rv = ReflowDirtyLines(state);
 | |
|   aStatus = state.mReflowStatus;
 | |
| 
 | |
|   // XXX get rid of this!
 | |
|   BuildFloaterList();
 | |
| 
 | |
|   // Compute our final size
 | |
|   ComputeFinalSize(state, aMetrics);
 | |
|   lineLayout->PopInline();
 | |
| 
 | |
|   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
 | |
|                ("exit nsBlockFrame::Reflow: size=%d,%d reflowStatus=%d rv=%x",
 | |
|                 aMetrics.width, aMetrics.height, aStatus, rv));
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| // XXX make this virtual
 | |
| // XXX factor into its component pieces
 | |
| void
 | |
| nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
 | |
|                                 nsHTMLReflowMetrics& aMetrics)
 | |
| {
 | |
|   // XXX handle floater problems this way...
 | |
|   PRBool isFixedWidth = aState.HaveFixedContentWidth();
 | |
|   PRBool isFixedHeight = aState.HaveFixedContentHeight();
 | |
| 
 | |
| #if 0
 | |
|   if (NS_BODY_SHRINK_WRAP & mFlags) {
 | |
|     isFixedWidth = PR_FALSE;
 | |
|     isFixedHeight = PR_FALSE;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // Compute final width
 | |
|   if (isFixedWidth) {
 | |
|     // Use style defined width
 | |
|     aMetrics.width = aState.mBorderPadding.left + aState.computedWidth +
 | |
|       aState.mBorderPadding.right;
 | |
|   }
 | |
|   else {
 | |
|     nscoord computedWidth = aState.mKidXMost + aState.mBorderPadding.right;
 | |
|     PRBool compact = PR_FALSE;
 | |
|     if (NS_STYLE_DISPLAY_COMPACT == aState.mStyleDisplay->mDisplay) {
 | |
|       // If we are display: compact AND we have no lines or we have
 | |
|       // exactly one line and that line is not a block line AND that
 | |
|       // line doesn't end in a BR of any sort THEN we remain a compact
 | |
|       // frame.
 | |
|       if ((nsnull == mLines) ||
 | |
|           ((nsnull == mLines->mNext) && !mLines->IsBlock() &&
 | |
|            (NS_STYLE_CLEAR_NONE == mLines->mBreakType) &&
 | |
|            (computedWidth <= aState.mCompactMarginWidth))) {
 | |
|         compact = PR_TRUE;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // There are two options here. We either shrink wrap around our
 | |
|     // contents or we fluff out to the maximum available width. Note:
 | |
|     // We always shrink wrap when given an unconstrained width.
 | |
|     if ((0 == (NS_BLOCK_SHRINK_WRAP & mFlags)) &&
 | |
|         !aState.mUnconstrainedWidth &&
 | |
|         !compact) {
 | |
|       // Fluff out to the max width if we aren't already that wide
 | |
|       if (computedWidth < aState.availableWidth) {
 | |
|         computedWidth = aState.availableWidth;
 | |
|       }
 | |
|     }
 | |
|     aMetrics.width = computedWidth;
 | |
|   }
 | |
| 
 | |
|   // Compute final height
 | |
|   if (isFixedHeight) {
 | |
|     // Use style defined height
 | |
|     aMetrics.height = aState.mBorderPadding.top + aState.computedHeight +
 | |
|       aState.mBorderPadding.bottom;
 | |
|   }
 | |
|   else {
 | |
|     // Shrink wrap our height around our contents.
 | |
|     if (aState.mIsMarginRoot) {
 | |
|       // When we are a margin root make sure that our last childs
 | |
|       // bottom margin is fully applied.
 | |
|       // XXX check for a fit
 | |
|       aState.mY += aState.mPrevBottomMargin;
 | |
|     }
 | |
|     aState.mY += aState.mBorderPadding.bottom;
 | |
|     aMetrics.height = aState.mY;
 | |
|   }
 | |
| 
 | |
|   // Return top and bottom margin information
 | |
|   if (aState.mIsMarginRoot) {
 | |
|     aMetrics.mCarriedOutTopMargin = 0;
 | |
|     aMetrics.mCarriedOutBottomMargin = 0;
 | |
|   }
 | |
|   else {
 | |
|     aMetrics.mCarriedOutTopMargin = aState.mCarriedOutTopMargin;
 | |
|     aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
 | |
|   }
 | |
| 
 | |
|   // Special check for zero sized content: If our content is zero
 | |
|   // sized then we collapse into nothingness.
 | |
|   PRBool emptyFrame = PR_FALSE;
 | |
|   // We need to check the specified width and see if it's 'auto'
 | |
|   const nsStylePosition* position;
 | |
|   aState.frame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&) position);
 | |
|   PRIntn specifiedWidthUnit = position->mWidth.GetUnit();
 | |
|   if ((eStyleUnit_Auto == specifiedWidthUnit) &&
 | |
|       (NS_AUTOHEIGHT == aState.computedHeight) &&
 | |
|       ((0 == aState.mKidXMost - aState.mBorderPadding.left) &&
 | |
|        (0 == aState.mY - aState.mBorderPadding.top))) {
 | |
|     aMetrics.width = 0;
 | |
|     aMetrics.height = 0;
 | |
|     aState.mAscent = 0;
 | |
|     aState.mDescent = 0;
 | |
|     emptyFrame = PR_TRUE;
 | |
|   }
 | |
| 
 | |
|   aMetrics.ascent = aMetrics.height;
 | |
|   aMetrics.descent = 0;
 | |
| 
 | |
|   if (aState.mComputeMaxElementSize) {
 | |
|     nscoord maxWidth, maxHeight;
 | |
|     if (emptyFrame) {
 | |
|       // When a frame is empty it must not provide any
 | |
|       // max-element-size information.
 | |
|       maxWidth = maxHeight = 0;
 | |
|     }
 | |
|     else {
 | |
|       if (aState.mNoWrap) {
 | |
|         // When no-wrap is true the max-element-size.width is the
 | |
|         // width of the widest line plus the right border. Note that
 | |
|         // aState.mKidXMost already has the left border factored into
 | |
|         // it
 | |
|         maxWidth = aState.mKidXMost + aState.mBorderPadding.right;
 | |
|       }
 | |
|       else {
 | |
|         // Add in border and padding dimensions to already computed
 | |
|         // max-element-size values.
 | |
|         maxWidth = aState.mMaxElementSize.width +
 | |
|           aState.mBorderPadding.left + aState.mBorderPadding.right;
 | |
|       }
 | |
|       maxHeight = aState.mMaxElementSize.height +
 | |
|         aState.mBorderPadding.top + aState.mBorderPadding.bottom;
 | |
|     }
 | |
| 
 | |
|     // Store away the final value
 | |
|     aMetrics.maxElementSize->width = maxWidth;
 | |
|     aMetrics.maxElementSize->height = maxHeight;
 | |
| #ifdef NOISY_MAX_ELEMENT_SIZE
 | |
|     ListTag(stdout);
 | |
|     printf(": max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n",
 | |
|            maxWidth, maxHeight, aMetrics.width, aMetrics.height,
 | |
|            aState.availableWidth, aState.availableHeight);
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   // Compute the combined area of our children
 | |
|   // XXX take into account the overflow->clip property!
 | |
|   nscoord x0 = 0, y0 = 0, x1 = aMetrics.width, y1 = aMetrics.height;
 | |
|   nsLineBox* line = mLines;
 | |
|   while (nsnull != line) {
 | |
|     // Compute min and max x/y values for the reflowed frame's
 | |
|     // combined areas
 | |
|     nscoord x = line->mCombinedArea.x;
 | |
|     nscoord y = line->mCombinedArea.y;
 | |
|     nscoord xmost = x + line->mCombinedArea.width;
 | |
|     nscoord ymost = y + line->mCombinedArea.height;
 | |
|     if (x < x0) x0 = x;
 | |
|     if (xmost > x1) x1 = xmost;
 | |
|     if (y < y0) y0 = y;
 | |
|     if (ymost > y1) y1 = ymost;
 | |
| 
 | |
|     // If the line has floaters, factor those in as well
 | |
|     nsVoidArray* floaters = line->mFloaters;
 | |
|     if (nsnull != floaters) {
 | |
|       PRInt32 i, n = floaters->Count();
 | |
|       for (i = 0; i < n; i++) {
 | |
|         nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
 | |
|         nsIFrame* frame = ph->GetAnchoredItem();
 | |
|         // XXX This is wrong! The floater may have a combined area
 | |
|         // that exceeds its bounding box!
 | |
|         nsRect r;
 | |
|         frame->GetRect(r);
 | |
|         if (r.x < x0) x0 = r.x;
 | |
|         if (r.XMost() > x1) x1 = r.XMost();
 | |
|         if (r.y < y0) y0 = r.y;
 | |
|         if (r.YMost() > y1) y1 = r.YMost();
 | |
|       }
 | |
|     }
 | |
|     line = line->mNext;
 | |
|   }
 | |
|   aMetrics.mCombinedArea.x = x0;
 | |
|   aMetrics.mCombinedArea.y = y0;
 | |
|   aMetrics.mCombinedArea.width = x1 - x0;
 | |
|   aMetrics.mCombinedArea.height = y1 - y0;
 | |
| 
 | |
|   if (nsnull != mBullet) {
 | |
|     nsRect r;
 | |
|     mBullet->GetRect(r);
 | |
|     nscoord x0 = aMetrics.mCombinedArea.x;
 | |
|     nscoord y0 = aMetrics.mCombinedArea.y;
 | |
|     nscoord x1 = x0 + aMetrics.mCombinedArea.width;
 | |
|     nscoord y1 = y0 + aMetrics.mCombinedArea.height;
 | |
|     if (r.x < x0) x0 = r.x;
 | |
|     if (r.XMost() > x1) x1 = r.XMost();
 | |
|     if (r.y < y0) y0 = r.y;
 | |
|     if (r.YMost() > y1) y1 = r.YMost();
 | |
|     aMetrics.mCombinedArea.x = x0;
 | |
|     aMetrics.mCombinedArea.y = y0;
 | |
|     aMetrics.mCombinedArea.width = x1 - x0;
 | |
|     aMetrics.mCombinedArea.height = y1 - y0;
 | |
| 
 | |
|     // If the combined area of our children exceeds our bounding box
 | |
|     // then set the NS_FRAME_OUTSIDE_CHILDREN flag, otherwise clear it.
 | |
|     if ((aMetrics.mCombinedArea.x < 0) ||
 | |
|         (aMetrics.mCombinedArea.y < 0) ||
 | |
|         (aMetrics.mCombinedArea.XMost() > aMetrics.width) ||
 | |
|         (aMetrics.mCombinedArea.YMost() > aMetrics.height)) {
 | |
|       mState |= NS_FRAME_OUTSIDE_CHILDREN;
 | |
|     }
 | |
|     else {
 | |
|       mState &= ~NS_FRAME_OUTSIDE_CHILDREN;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If the combined area of our children exceeds our bounding box
 | |
|   // then set the NS_FRAME_OUTSIDE_CHILDREN flag, otherwise clear it.
 | |
|   if ((aMetrics.mCombinedArea.x < 0) ||
 | |
|       (aMetrics.mCombinedArea.y < 0) ||
 | |
|       (aMetrics.mCombinedArea.XMost() > aMetrics.width) ||
 | |
|       (aMetrics.mCombinedArea.YMost() > aMetrics.height)) {
 | |
|     mState |= NS_FRAME_OUTSIDE_CHILDREN;
 | |
|   }
 | |
|   else {
 | |
|     mState &= ~NS_FRAME_OUTSIDE_CHILDREN;
 | |
|   }
 | |
| #if XXX
 | |
| ListTag(stdout);
 | |
| printf(": => carried=%d,%d\n", aMetrics.mCarriedOutTopMargin, aMetrics.mCarriedOutBottomMargin);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsBlockFrame::PrepareInitialReflow(nsBlockReflowState& aState)
 | |
| {
 | |
|   if ((nsnull == mPrevInFlow) && (nsnull != aState.mRunInFrame)) {
 | |
| #ifdef NOISY_RUNIN
 | |
|     ListTag(stdout);
 | |
|     printf(": run-in from: ");
 | |
|     aReflowState.mRunInFrame->ListTag(stdout);
 | |
|     printf("\n");
 | |
| #endif
 | |
|     // Take frames away from the run-in frame
 | |
|     TakeRunInFrames(aState.mRunInFrame);
 | |
|   }
 | |
| 
 | |
|   DrainOverflowLines();
 | |
|   PrepareResizeReflow(aState);
 | |
|   ComputeTextRuns(aState);
 | |
|   RenumberLists(aState);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //XXX gotta go
 | |
| nsresult
 | |
| nsBlockFrame::PrepareFrameAppendedReflow(nsBlockReflowState& aState)
 | |
| {
 | |
|   RenumberLists(aState);
 | |
|   nsresult rv = ComputeTextRuns(aState);
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| //XXX gotta go
 | |
| nsresult
 | |
| nsBlockFrame::PrepareFrameInsertedReflow(nsBlockReflowState& aState)
 | |
| {
 | |
|   nsresult rv = PrepareResizeReflow(aState);
 | |
|   RenumberLists(aState);
 | |
|   rv = ComputeTextRuns(aState);
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| //XXX gotta go
 | |
| nsresult
 | |
| nsBlockFrame::PrepareFrameRemovedReflow(nsBlockReflowState& aState)
 | |
| {
 | |
|   nsresult rv = PrepareResizeReflow(aState);
 | |
|   RenumberLists(aState);
 | |
|   rv = ComputeTextRuns(aState);
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState)
 | |
| {
 | |
|   // If by chance we are inside a table, then give up and reflow
 | |
|   // everything because we don't cache max-element-size information in
 | |
|   // the lines.
 | |
|   if (aState.mComputeMaxElementSize) {
 | |
|     return PrepareResizeReflow(aState);
 | |
|   }
 | |
| 
 | |
|   // Determine the line being impacted
 | |
|   PRBool isFloater;
 | |
|   nsLineBox* line = FindLineFor(aState.mNextRCFrame, isFloater);
 | |
|   if (nsnull == line) {
 | |
|     // This can't happen, but just in case it does...
 | |
|     return PrepareResizeReflow(aState);
 | |
|   }
 | |
| 
 | |
|   // XXX: temporary: If the child frame is a floater then punt
 | |
|   if (isFloater) {
 | |
|     return PrepareResizeReflow(aState);
 | |
|   }
 | |
| 
 | |
|   // XXX need code for run-in/compact
 | |
| 
 | |
|   // Mark (at least) the affected line dirty.
 | |
|   line->MarkDirty();
 | |
|   if (aState.mNoWrap || line->IsBlock()) {
 | |
|     // If we aren't wrapping then we know for certain that any changes
 | |
|     // to a childs reflow can't affect the line that follows. This is
 | |
|     // also true if the line is a block line.
 | |
|   }
 | |
|   else {
 | |
|     // XXX: temporary: For now we are conservative and mark this line
 | |
|     // and any inline lines that follow it dirty.
 | |
|     line = line->mNext;
 | |
|     while (nsnull != line) {
 | |
|       if (line->IsBlock()) {
 | |
|         break;
 | |
|       }
 | |
|       line->MarkDirty();
 | |
|       line = line->mNext;
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsBlockFrame::PrepareStyleChangedReflow(nsBlockReflowState& aState)
 | |
| {
 | |
|   // XXX temporary
 | |
|   return PrepareResizeReflow(aState);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
 | |
| {
 | |
|   // Mark everything dirty
 | |
|   nsLineBox* line = mLines;
 | |
|   while (nsnull != line) {
 | |
|     line->MarkDirty();
 | |
|     line = line->mNext;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //----------------------------------------
 | |
| 
 | |
| nsLineBox*
 | |
| nsBlockFrame::FindLineFor(nsIFrame* aFrame, PRBool& aIsFloaterResult)
 | |
| {
 | |
|   aIsFloaterResult = PR_FALSE;
 | |
|   nsLineBox* line = mLines;
 | |
|   while (nsnull != line) {
 | |
|     if (line->Contains(aFrame)) {
 | |
|       return line;
 | |
|     }
 | |
|     if (nsnull != line->mFloaters) {
 | |
|       nsVoidArray& a = *line->mFloaters;
 | |
|       PRInt32 i, n = a.Count();
 | |
|       for (i = 0; i < n; i++) {
 | |
|         nsPlaceholderFrame* ph = (nsPlaceholderFrame*) a[i];
 | |
|         if (aFrame == ph->GetAnchoredItem()) {
 | |
|           aIsFloaterResult = PR_TRUE;
 | |
|           return line;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     line = line->mNext;
 | |
|   }
 | |
|   return line;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsBlockFrame::RecoverStateFrom(nsBlockReflowState& aState,
 | |
|                                 nsLineBox* aLine,
 | |
|                                 nscoord aDeltaY)
 | |
| {
 | |
|   aState.mCurrentLine = aLine;
 | |
| 
 | |
|   // Recover xmost
 | |
|   nscoord xmost = aLine->mBounds.XMost();
 | |
|   if (xmost > aState.mKidXMost) {
 | |
|     aState.mKidXMost = xmost;
 | |
|   }
 | |
| 
 | |
|   // Recover the natural (un-collapsed margins) for the child
 | |
|   nsMargin childMargins(0, 0, 0, 0);
 | |
|   if (aLine->IsBlock()) {
 | |
|     nsIFrame* frame = aLine->mFirstChild;
 | |
|     const nsStyleSpacing* spacing;
 | |
|     frame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct*&)spacing);
 | |
|     nsBlockReflowContext::ComputeMarginsFor(aState.mPresContext, frame,
 | |
|                                             spacing, aState, childMargins);
 | |
|   }
 | |
| 
 | |
|   // Recompute running margin value (aState.mPrevBottomMargin). Also
 | |
|   // recover the aState.mCarriedOutTopMargin, when appropriate.
 | |
|   nscoord topMargin, bottomMargin;
 | |
|   nsBlockReflowContext::CollapseMargins(childMargins,
 | |
|                                         aLine->GetCarriedOutTopMargin(),
 | |
|                                         aLine->GetCarriedOutBottomMargin(),
 | |
|                                         aLine->GetHeight(),
 | |
|                                         aState.mPrevBottomMargin,
 | |
|                                         topMargin, bottomMargin);
 | |
|   aState.mPrevBottomMargin = bottomMargin;
 | |
|   if (!aState.ShouldApplyTopMargin()) {
 | |
|     aState.mCarriedOutTopMargin = topMargin;
 | |
|   }
 | |
| 
 | |
|   if (0 != aDeltaY) {
 | |
|     // Move this lines frames by the current delta value
 | |
|     SlideFrames(aState.mPresContext, aState.mSpaceManager, aLine, aDeltaY);
 | |
|     SlideFloaters(aState.mPresContext, aState.mSpaceManager, aLine, aDeltaY,
 | |
|                   PR_FALSE);
 | |
|   }
 | |
|   if (nsnull != aLine->mFloaters) {
 | |
|     aState.mY = aLine->mBounds.y;
 | |
|     aState.PlaceCurrentLineFloaters(aLine->mFloaters);
 | |
|     aState.mY = aLine->mBounds.YMost();
 | |
|     aState.PlaceBelowCurrentLineFloaters(aLine->mFloaters);
 | |
|   }
 | |
| 
 | |
|   // Advance Y to be below the line.
 | |
|   aState.mY = aLine->mBounds.YMost();
 | |
| 
 | |
|   // XXX_fix_me: if the line has clear-before semantics then figure out
 | |
|   // if we need to do anything here or not!
 | |
| 
 | |
|   // Apply any clear before/after semantics the line might have
 | |
|   if (!aLine->IsBlock() && (NS_STYLE_CLEAR_NONE != aLine->mBreakType)) {
 | |
|     // Apply clear
 | |
|     switch (aLine->mBreakType) {
 | |
|     case NS_STYLE_CLEAR_LEFT:
 | |
|     case NS_STYLE_CLEAR_RIGHT:
 | |
|     case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
 | |
|       // XXX_fix_me is this the right y value to use? or should we use
 | |
|       // the previous aState.mY?
 | |
|       aState.ClearFloaters(aState.mY, aLine->mBreakType);
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Propogate reflow "damage" from the just reflowed line (aLine) to
 | |
|  * any subsequent lines that were affected. The only thing that causes
 | |
|  * damage is a change to the impact that floaters make.
 | |
|  */
 | |
| void
 | |
| nsBlockFrame::PropogateReflowDamage(nsBlockReflowState& aState,
 | |
|                                     nsLineBox* aLine,
 | |
|                                     nscoord aDeltaY)
 | |
| {
 | |
|   if (aLine->mCombinedArea.YMost() > aLine->mBounds.YMost()) {
 | |
|     // The line has an object that extends outside of its bounding box.
 | |
|     nscoord impactY0 = aLine->mCombinedArea.y;
 | |
|     nscoord impactY1 = aLine->mCombinedArea.YMost();
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
|     if (aState.reason == eReflowReason_Incremental) {
 | |
|       IndentBy(stdout, gNoiseIndent);
 | |
|       printf("impactY0=%d impactY1=%d deltaY=%d\n",
 | |
|              impactY0, impactY1, aDeltaY);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     // XXX Because we don't know what it is (it might be a floater; it
 | |
|     // might be something that is just relatively positioned) we
 | |
|     // *assume* that it's a floater and that lines that follow will
 | |
|     // need reflowing.
 | |
| 
 | |
|     // Note: we cannot stop after the first non-intersecting line
 | |
|     // because lines might be overlapping because of negative margins.
 | |
|     nsLineBox* next = aLine->mNext;
 | |
|     while (nsnull != next) {
 | |
|       nscoord lineY0 = next->mBounds.y + aDeltaY;
 | |
|       nscoord lineY1 = lineY0 + next->mBounds.height;
 | |
|       if ((lineY0 < impactY1) && (impactY0 < lineY1)) {
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
|         if (aState.reason == eReflowReason_Incremental) {
 | |
|           IndentBy(stdout, gNoiseIndent);
 | |
|           printf("line=%p setting dirty\n", next);
 | |
|         }
 | |
| #endif
 | |
|         next->MarkDirty();
 | |
|       }
 | |
|       next = next->mNext;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Reflow the dirty lines
 | |
|  */
 | |
| nsresult
 | |
| nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
 | |
| {
 | |
|   nsresult rv = NS_OK;
 | |
|   PRBool keepGoing = PR_TRUE;
 | |
| 
 | |
|   // Inform line layout of where the text runs are
 | |
|   aState.mLineLayout->SetReflowTextRuns(mTextRuns);
 | |
| 
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
|   if (aState.reason == eReflowReason_Incremental) {
 | |
|     nsIReflowCommand::ReflowType type;
 | |
|     aState.reflowCommand->GetType(type);
 | |
|     IndentBy(stdout, gNoiseIndent);
 | |
|     ListTag(stdout);
 | |
|     printf(": incrementally reflowing dirty lines: type=%s(%d)\n",
 | |
|            kReflowCommandType[type], type);
 | |
|     gNoiseIndent++;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // Reflow the lines that are already ours
 | |
|   aState.mPrevLine = nsnull;
 | |
|   nsLineBox* line = mLines;
 | |
|   nscoord deltaY = 0;
 | |
|   while (nsnull != line) {
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
|     if (aState.reason == eReflowReason_Incremental) {
 | |
|       IndentBy(stdout, gNoiseIndent);
 | |
|       printf("line=%p mY=%d dirty=%s oldBounds=%d,%d,%d,%d deltaY=%d\n",
 | |
|              line, aState.mY, line->IsDirty() ? "yes" : "no",
 | |
|              line->mBounds, deltaY);
 | |
|       gNoiseIndent++;
 | |
|     }
 | |
| #endif
 | |
|     if (line->IsDirty()) {
 | |
|       // Compute the dirty lines "before" YMost, after factoring in
 | |
|       // the running deltaY value - the running value is implicit in
 | |
|       // aState.mY.
 | |
|       nscoord oldHeight = line->mBounds.height;
 | |
| 
 | |
|       // Reflow the dirty line
 | |
|       rv = ReflowLine(aState, line, keepGoing);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         return rv;
 | |
|       }
 | |
|       DidReflowLine(aState, line, keepGoing);
 | |
|       if (!keepGoing) {
 | |
|         if (0 == line->ChildCount()) {
 | |
|           DeleteLine(aState, line);
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|       nscoord newHeight = line->mBounds.height;
 | |
|       deltaY += newHeight - oldHeight;
 | |
| 
 | |
|       // If the next line is clean then check and see if reflowing the
 | |
|       // current line "damaged" the next line. Damage occurs when the
 | |
|       // current line contains floaters that intrude upon the
 | |
|       // subsequent lines.
 | |
|       nsLineBox* next = line->mNext;
 | |
|       if ((nsnull != next) && !next->IsDirty()) {
 | |
|         PropogateReflowDamage(aState, line, deltaY);
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       // XXX what if the slid line doesn't fit because we are in a
 | |
|       // vertically constrained situation?
 | |
|       // Recover state as if we reflowed this line
 | |
|       RecoverStateFrom(aState, line, deltaY);
 | |
|     }
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
|     if (aState.reason == eReflowReason_Incremental) {
 | |
|       gNoiseIndent--;
 | |
|       IndentBy(stdout, gNoiseIndent);
 | |
|       printf("line=%p mY=%d newBounds=%d,%d,%d,%d deltaY=%d\n",
 | |
|              line, aState.mY, line->mBounds, deltaY);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     // If this is an inline frame then its time to stop
 | |
|     aState.mPrevLine = line;
 | |
|     line = line->mNext;
 | |
|     aState.mLineLayout->NextLine();
 | |
|   }
 | |
| 
 | |
|   // Pull data from a next-in-flow if we can
 | |
|   while (keepGoing && (nsnull != aState.mNextInFlow)) {
 | |
|     // Grab first line from our next-in-flow
 | |
|     line = aState.mNextInFlow->mLines;
 | |
|     if (nsnull == line) {
 | |
|       aState.mNextInFlow = (nsBlockFrame*) aState.mNextInFlow->mNextInFlow;
 | |
|       continue;
 | |
|     }
 | |
|     // XXX See if the line is not dirty; if it's not maybe we can
 | |
|     // avoid the pullup if it can't fit?
 | |
|     aState.mNextInFlow->mLines = line->mNext;
 | |
|     line->mNext = nsnull;
 | |
|     if (0 == line->ChildCount()) {
 | |
|       // The line is empty. Try the next one.
 | |
|       NS_ASSERTION(nsnull == line->mFirstChild, "bad empty line");
 | |
|       delete line;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     // XXX move to a subroutine: run-in, overflow, pullframe and this do this
 | |
|     // Make the children in the line ours.
 | |
|     nsIFrame* frame = line->mFirstChild;
 | |
|     nsIFrame* lastFrame = nsnull;
 | |
|     PRInt32 n = line->ChildCount();
 | |
|     while (--n >= 0) {
 | |
|       frame->SetParent(this);
 | |
|       lastFrame = frame;
 | |
|       frame->GetNextSibling(&frame);
 | |
|     }
 | |
|     lastFrame->SetNextSibling(nsnull);
 | |
| 
 | |
|     // Add line to our line list
 | |
|     if (nsnull == aState.mPrevLine) {
 | |
|       NS_ASSERTION(nsnull == mLines, "bad aState.mPrevLine");
 | |
|       mLines = line;
 | |
|     }
 | |
|     else {
 | |
|       NS_ASSERTION(nsnull == aState.mPrevLine->mNext, "bad aState.mPrevLine");
 | |
|       aState.mPrevLine->mNext = line;
 | |
|       aState.mPrevChild->SetNextSibling(line->mFirstChild);
 | |
|     }
 | |
| 
 | |
|     // Now reflow it and any lines that it makes during it's reflow
 | |
|     // (we have to loop here because reflowing the line may case a new
 | |
|     // line to be created; see SplitLine's callers for examples of
 | |
|     // when this happens).
 | |
|     while (nsnull != line) {
 | |
|       rv = ReflowLine(aState, line, keepGoing);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         return rv;
 | |
|       }
 | |
|       DidReflowLine(aState, line, keepGoing);
 | |
|       if (!keepGoing) {
 | |
|         if (0 == line->ChildCount()) {
 | |
|           DeleteLine(aState, line);
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       // If this is an inline frame then its time to stop
 | |
|       aState.mPrevLine = line;
 | |
|       line = line->mNext;
 | |
|       aState.mLineLayout->NextLine();
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
|   if (aState.reason == eReflowReason_Incremental) {
 | |
|     gNoiseIndent--;
 | |
|     IndentBy(stdout, gNoiseIndent);
 | |
|     ListTag(stdout);
 | |
|     printf(": done reflowing dirty lines (status=%x, mLimitToOneLine=%d)\n",
 | |
|            aState.mReflowStatus, aState.mLimitToOneLine);
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::DeleteLine(nsBlockReflowState& aState,
 | |
|                           nsLineBox* aLine)
 | |
| {
 | |
|   NS_PRECONDITION(0 == aLine->ChildCount(), "can't delete !empty line");
 | |
|   if (0 == aLine->ChildCount()) {
 | |
|     if (nsnull == aState.mPrevLine) {
 | |
|       NS_ASSERTION(aLine == mLines, "huh");
 | |
|       mLines = nsnull;
 | |
|     }
 | |
|     else {
 | |
|       NS_ASSERTION(aState.mPrevLine->mNext == aLine, "bad prev-line");
 | |
|       aState.mPrevLine->mNext = aLine->mNext;
 | |
|     }
 | |
|     delete aLine;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::WillReflowLine(nsBlockReflowState& aState,
 | |
|                              nsLineBox* aLine)
 | |
| {
 | |
|   // Setup the first-letter-style-ok flag
 | |
|   nsLineLayout& lineLayout = *aState.mLineLayout;
 | |
|   if (mFirstLetterStyle && (0 == lineLayout.GetLineNumber())) {
 | |
|     lineLayout.SetFirstLetterStyleOK(PR_TRUE);
 | |
|   }
 | |
|   else {
 | |
|     lineLayout.SetFirstLetterStyleOK(PR_FALSE);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Reflow a line. The line will either contain a single block frame
 | |
|  * or contain 1 or more inline frames. aLineReflowStatus indicates
 | |
|  * whether or not the caller should continue to reflow more lines.
 | |
|  */
 | |
| nsresult
 | |
| nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
 | |
|                           nsLineBox* aLine,
 | |
|                           PRBool& aKeepGoing)
 | |
| {
 | |
|   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
 | |
|                  ("nsBlockFrame::ReflowLine: line=%p", aLine));
 | |
| 
 | |
|   nsresult rv = NS_OK;
 | |
| 
 | |
|   // If the line already has floaters on it from last time, remove
 | |
|   // them from the spacemanager now.
 | |
|   if (nsnull != aLine->mFloaters) {
 | |
|     aLine->mFloaters->Clear();
 | |
|   }
 | |
| 
 | |
|   // If the line is empty then first pull a frame into it so that we
 | |
|   // know what kind of line it is (block or inline).
 | |
|   if (0 == aLine->ChildCount()) {
 | |
|     nsIFrame* frame;
 | |
|     rv = PullFrame(aState, aLine, frame);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return rv;
 | |
|     }
 | |
|     if (nsnull == frame) {
 | |
|       aKeepGoing = PR_FALSE;
 | |
|       return rv;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Setup the line-layout for the new line
 | |
|   aState.mLineLayout->Reset();
 | |
|   aState.mCurrentLine = aLine;
 | |
|   aLine->ClearDirty();
 | |
|   aLine->SetNeedDidReflow();
 | |
| 
 | |
|   // Now that we know what kind of line we have, reflow it
 | |
|   if (aLine->IsBlock()) {
 | |
|     // When reflowing a block frame we always get the available space
 | |
|     aState.GetAvailableSpace();
 | |
| 
 | |
|     if ((nsnull != aState.lineLayout) &&
 | |
|         (0 != aState.lineLayout->GetPlacedFrames())) {
 | |
|       // Blocks are not allowed on the same line as anything else
 | |
|       aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
 | |
|       aKeepGoing = PR_FALSE;
 | |
|     }
 | |
|     else {
 | |
|       // Notify observers that we are about to reflow the line
 | |
|       WillReflowLine(aState, aLine);
 | |
| 
 | |
|       rv = ReflowBlockFrame(aState, aLine, aKeepGoing);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         return rv;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     // When this class is an inline frame and we are reflowing inline
 | |
|     // frames then there is no point in getting available space.
 | |
|     nscoord x, availWidth, availHeight;
 | |
|     aState.GetAvailableSpace();
 | |
| 
 | |
|     // Setup initial coordinate system for reflowing the inline frames
 | |
|     // into.
 | |
|     x = aState.mAvailSpaceRect.x + aState.mBorderPadding.left;
 | |
|     availWidth = aState.mAvailSpaceRect.width;
 | |
| 
 | |
|     if (aState.mUnconstrainedHeight) {
 | |
|       availHeight = NS_UNCONSTRAINEDSIZE;
 | |
|     }
 | |
|     else {
 | |
|       /* XXX get the height right! */
 | |
|       availHeight = aState.mAvailSpaceRect.height;
 | |
|     }
 | |
|     aState.mInlineReflow->Init(x, aState.mY, availWidth, availHeight);
 | |
| 
 | |
|     // Notify observers that we are about to reflow the line
 | |
|     WillReflowLine(aState, aLine);
 | |
| 
 | |
|     // Reflow the frames that are already on the line first
 | |
|     PRBool keepLineGoing = PR_TRUE;
 | |
|     PRInt32 i;
 | |
|     nsIFrame* frame = aLine->mFirstChild;
 | |
|     for (i = 0; i < aLine->ChildCount(); i++) {
 | |
|       rv = ReflowInlineFrame(aState, aLine, frame,
 | |
|                              keepLineGoing, aKeepGoing);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         return rv;
 | |
|       }
 | |
|       if (!keepLineGoing) {
 | |
|         // It is possible that one or more of next lines are empty
 | |
|         // (because of DeleteNextInFlowsFor). If so, delete them now
 | |
|         // in case we are finished.
 | |
|         nsLineBox* nextLine = aLine->mNext;
 | |
|         while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
 | |
|           // Discard empty lines immediately. Empty lines can happen
 | |
|           // here because of DeleteNextInFlowsFor not being able to
 | |
|           // delete lines.
 | |
|           aLine->mNext = nextLine->mNext;
 | |
|           NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
 | |
|           delete nextLine;
 | |
|           nextLine = aLine->mNext;
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|       frame->GetNextSibling(&frame);
 | |
|     }
 | |
| 
 | |
|     // Pull frames and reflow them until we can't
 | |
|     while (keepLineGoing) {
 | |
|       nsIFrame* frame;
 | |
|       rv = PullFrame(aState, aLine, frame);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         return rv;
 | |
|       }
 | |
|       if (nsnull == frame) {
 | |
|         break;
 | |
|       }
 | |
|       rv = ReflowInlineFrame(aState, aLine, frame, keepLineGoing,
 | |
|                              aKeepGoing);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         return rv;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // If we are propogating out a break-before status then there is
 | |
|     // no point in placing the line.
 | |
|     if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
 | |
|       rv = PlaceLine(aState, aLine, aKeepGoing);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Pull frame from the next available location (one of our lines or
 | |
|  * one of our next-in-flows lines).
 | |
|  */
 | |
| nsresult
 | |
| nsBlockFrame::PullFrame(nsBlockReflowState& aState,
 | |
|                          nsLineBox* aLine,
 | |
|                          nsIFrame*& aFrameResult)
 | |
| {
 | |
|   nsresult rv = NS_OK;
 | |
|   PRBool stopPulling;
 | |
|   aFrameResult = nsnull;
 | |
| 
 | |
|   // First check our remaining lines
 | |
|   while (nsnull != aLine->mNext) {
 | |
|     rv = PullFrame(aState, aLine, &aLine->mNext, PR_FALSE,
 | |
|                    aFrameResult, stopPulling);
 | |
|     if (NS_FAILED(rv) || stopPulling) {
 | |
|       return rv;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Pull frames from the next-in-flow(s) until we can't
 | |
|   nsBlockFrame* nextInFlow = aState.mNextInFlow;
 | |
|   while (nsnull != nextInFlow) {
 | |
|     nsLineBox* line = nextInFlow->mLines;
 | |
|     if (nsnull == line) {
 | |
|       nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
 | |
|       aState.mNextInFlow = nextInFlow;
 | |
|       continue;
 | |
|     }
 | |
|     rv = PullFrame(aState, aLine, &nextInFlow->mLines, PR_TRUE,
 | |
|                    aFrameResult, stopPulling);
 | |
|     if (NS_FAILED(rv) || stopPulling) {
 | |
|       return rv;
 | |
|     }
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Try to pull a frame out a line pointed at by aFromList. If a frame
 | |
|  * is pulled then aPulled will be set to PR_TRUE. In addition, if
 | |
|  * aUpdateGeometricParent is set then the pulled frames geometric
 | |
|  * parent will be updated (e.g. when pulling from a next-in-flows line
 | |
|  * list).
 | |
|  *
 | |
|  * Note: pulling a frame from a line that is a place-holder frame
 | |
|  * doesn't automatically remove the corresponding floater from the
 | |
|  * line's floater array. This happens indirectly: either the line gets
 | |
|  * emptied (and destroyed) or the line gets reflowed (because we mark
 | |
|  * it dirty) and the code at the top of ReflowLine empties the
 | |
|  * array. So eventually, it will be removed, just not right away.
 | |
|  */
 | |
| nsresult
 | |
| nsBlockFrame::PullFrame(nsBlockReflowState& aState,
 | |
|                          nsLineBox* aLine,
 | |
|                          nsLineBox** aFromList,
 | |
|                          PRBool aUpdateGeometricParent,
 | |
|                          nsIFrame*& aFrameResult,
 | |
|                          PRBool& aStopPulling)
 | |
| {
 | |
|   nsLineBox* fromLine = *aFromList;
 | |
|   NS_ASSERTION(nsnull != fromLine, "bad line to pull from");
 | |
|   if (0 == fromLine->ChildCount()) {
 | |
|     // Discard empty lines immediately. Empty lines can happen here
 | |
|     // because of DeleteChildsNextInFlow not being able to delete
 | |
|     // lines. Don't stop pulling - there may be more frames around.
 | |
|     *aFromList = fromLine->mNext;
 | |
|     NS_ASSERTION(nsnull == fromLine->mFirstChild, "bad empty line");
 | |
|     delete fromLine;
 | |
|     aStopPulling = PR_FALSE;
 | |
|     aFrameResult = nsnull;
 | |
|   }
 | |
|   else if ((0 != aLine->ChildCount()) && fromLine->IsBlock()) {
 | |
|     // If our line is not empty and the child in aFromLine is a block
 | |
|     // then we cannot pull up the frame into this line. In this case
 | |
|     // we stop pulling.
 | |
|     aStopPulling = PR_TRUE;
 | |
|     aFrameResult = nsnull;
 | |
|   }
 | |
|   else {
 | |
|     // Take frame from fromLine
 | |
|     nsIFrame* frame = fromLine->mFirstChild;
 | |
|     if (0 == aLine->mChildCount++) {
 | |
|       aLine->mFirstChild = frame;
 | |
|       aLine->SetIsBlock(fromLine->IsBlock());
 | |
|       NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock");
 | |
|     }
 | |
|     if (0 != --fromLine->mChildCount) {
 | |
|       // Mark line dirty now that we pulled a child
 | |
|       fromLine->MarkDirty();
 | |
|       frame->GetNextSibling(&fromLine->mFirstChild);
 | |
|     }
 | |
|     else {
 | |
|       // Free up the fromLine now that it's empty
 | |
|       *aFromList = fromLine->mNext;
 | |
|       delete fromLine;
 | |
|     }
 | |
| 
 | |
|     // Change geometric parents
 | |
|     if (aUpdateGeometricParent) {
 | |
|       frame->SetParent(this);
 | |
| 
 | |
|       // The frame is being pulled from a next-in-flow; therefore we
 | |
|       // need to add it to our sibling list.
 | |
|       if (nsnull != aState.mPrevChild) {
 | |
|         aState.mPrevChild->SetNextSibling(frame);
 | |
|       }
 | |
|       frame->SetNextSibling(nsnull);
 | |
| #ifdef NS_DEBUG
 | |
|       VerifyLineLength(aLine);
 | |
| #endif
 | |
|     }
 | |
| 
 | |
|     // Stop pulling because we found a frame to pull
 | |
|     aStopPulling = PR_TRUE;
 | |
|     aFrameResult = frame;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::DidReflowLine(nsBlockReflowState& aState,
 | |
|                              nsLineBox* aLine,
 | |
|                              PRBool aLineReflowStatus)
 | |
| {
 | |
|   // If the line no longer needs a floater array, get rid of it and
 | |
|   // save some memory
 | |
|   nsVoidArray* array = aLine->mFloaters;
 | |
|   if (nsnull != array) {
 | |
|     if (0 == array->Count()) {
 | |
|       delete array;
 | |
|       aLine->mFloaters = nsnull;
 | |
|     }
 | |
|     else {
 | |
|       array->Compact();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::SlideFrames(nsIPresContext& aPresContext,
 | |
|                            nsISpaceManager* aSpaceManager,
 | |
|                            nsLineBox* aLine, nscoord aDY)
 | |
| {
 | |
| #if 0
 | |
| ListTag(stdout); printf(": SlideFrames: line=%p dy=%d\n", aDY);
 | |
| #endif
 | |
|   // Adjust the Y coordinate of the frames in the line
 | |
|   nsRect r;
 | |
|   nsIFrame* kid = aLine->mFirstChild;
 | |
|   PRInt32 n = aLine->ChildCount();
 | |
|   while (--n >= 0) {
 | |
|     kid->GetRect(r);
 | |
|     r.y += aDY;
 | |
|     kid->SetRect(r);
 | |
| 
 | |
|     // If the child has any floaters that impact the space manager,
 | |
|     // slide them now.
 | |
|     nsIHTMLReflow* ihr;
 | |
|     if (NS_OK == kid->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) {
 | |
|       ihr->MoveInSpaceManager(aPresContext, aSpaceManager, 0, aDY);
 | |
|     }
 | |
| 
 | |
|     kid->GetNextSibling(&kid);
 | |
|   }
 | |
| 
 | |
|   // Adjust line state
 | |
|   aLine->mBounds.y += aDY;
 | |
|   aLine->mCombinedArea.y += aDY;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::SlideFloaters(nsIPresContext& aPresContext,
 | |
|                              nsISpaceManager* aSpaceManager,
 | |
|                              nsLineBox* aLine, nscoord aDY,
 | |
|                              PRBool aUpdateSpaceManager)
 | |
| {
 | |
|   nsVoidArray* floaters = aLine->mFloaters;
 | |
|   if (nsnull != floaters) {
 | |
|     nsRect r;
 | |
|     PRInt32 i, n = floaters->Count();
 | |
|     for (i = 0; i < n; i++) {
 | |
|       nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
 | |
|       nsIFrame* floater = ph->GetAnchoredItem();
 | |
|       floater->GetRect(r);
 | |
|       r.y += aDY;
 | |
|       floater->SetRect(r);
 | |
| 
 | |
|       if (aUpdateSpaceManager) {
 | |
|         // Adjust placement in space manager by the same amount
 | |
|         aSpaceManager->OffsetRegion(floater, 0, aDY);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::MoveInSpaceManager(nsIPresContext& aPresContext,
 | |
|                                  nsISpaceManager* aSpaceManager,
 | |
|                                  nscoord aDeltaX, nscoord aDeltaY)
 | |
| {
 | |
| #if 0
 | |
| ListTag(stdout); printf(": MoveInSpaceManager: d=%d,%d\n", aDeltaX, aDeltaY);
 | |
| #endif
 | |
|   nsLineBox* line = mLines;
 | |
|   while (nsnull != line) {
 | |
|     // Move the floaters in the spacemanager
 | |
|     nsVoidArray* floaters = line->mFloaters;
 | |
|     if (nsnull != floaters) {
 | |
|       PRInt32 i, n = floaters->Count();
 | |
|       for (i = 0; i < n; i++) {
 | |
|         nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
 | |
|         nsIFrame* floater = ph->GetAnchoredItem();
 | |
|         aSpaceManager->OffsetRegion(floater, aDeltaX, aDeltaY);
 | |
| #if 0
 | |
| ((nsFrame*)kid)->ListTag(stdout); printf(": offset=%d,%d\n", aDeltaX, aDeltaY);
 | |
| #endif
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Tell kids about the move too
 | |
|     PRInt32 n = line->ChildCount();
 | |
|     nsIFrame* kid = line->mFirstChild;
 | |
|     while (--n >= 0) {
 | |
|       nsIHTMLReflow* ihr;
 | |
|       if (NS_OK == kid->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) {
 | |
|         ihr->MoveInSpaceManager(aPresContext, aSpaceManager, aDeltaX, aDeltaY);
 | |
|       }
 | |
|       kid->GetNextSibling(&kid);
 | |
|     }
 | |
|     
 | |
|     line = line->mNext;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsBlockFrame*
 | |
| nsBlockFrame::FindFollowingBlockFrame(nsIFrame* aFrame)
 | |
| {
 | |
|   nsBlockFrame* followingBlockFrame = nsnull;
 | |
|   nsIFrame* frame = aFrame;
 | |
|   for (;;) {
 | |
|     nsIFrame* nextFrame;
 | |
|     frame->GetNextSibling(&nextFrame);
 | |
|     if (nsnull != nextFrame) {
 | |
|       const nsStyleDisplay* display;
 | |
|       nextFrame->GetStyleData(eStyleStruct_Display,
 | |
|                               (const nsStyleStruct*&) display);
 | |
|       if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
 | |
| #ifdef NOISY_RUNIN
 | |
|         ListTag(stdout);
 | |
|         printf(": frame: ");
 | |
|         aFrame->ListTag(stdout);
 | |
|         printf(" followed by: ");
 | |
|         nextFrame->ListTag(stdout);
 | |
|         printf("\n");
 | |
| #endif
 | |
|         followingBlockFrame = (nsBlockFrame*) nextFrame;
 | |
|         break;
 | |
|       }
 | |
|       else if (NS_STYLE_DISPLAY_INLINE == display->mDisplay) {
 | |
|         // If it's a text-frame and it's just whitespace and we are
 | |
|         // in a normal whitespace situation THEN skip it and keep
 | |
|         // going...
 | |
|         // XXX WRITE ME!
 | |
|       }
 | |
|       frame = nextFrame;
 | |
|     }
 | |
|     else
 | |
|       break;
 | |
|   }
 | |
|   return followingBlockFrame;
 | |
| }
 | |
| 
 | |
| #if XXX
 | |
| void
 | |
| nsBlockFrame::WillReflowFrame(nsBlockReflowState& aState,
 | |
|                                nsLineBox* aLine,
 | |
|                                nsIFrame* aFrame)
 | |
| {
 | |
|   nsIStyleContext* kidSC;
 | |
|   aFrame->GetStyleContext(kidSC);
 | |
|   if (nsnull != kidSC) {
 | |
|     nsIStyleContext* kidParentSC;
 | |
|     kidParentSC = kidSC->GetParent();
 | |
|     if (nsnull != kidParentSC) {
 | |
|       if (kidParentSC != mStyleContext) {
 | |
|         // The frame has changed situations so re-resolve its style
 | |
|         // context in the new situation.
 | |
|         aFrame->ReResolveStyleContext(&aState.mPresContext, mStyleContext);
 | |
|       }
 | |
|       NS_RELEASE(kidParentSC);
 | |
|     }
 | |
|     NS_RELEASE(kidSC);
 | |
|   }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| void
 | |
| nsBlockFrame::WillReflowFrame(nsBlockReflowState& aState,
 | |
|                               nsLineBox* aLine,
 | |
|                               nsIFrame* aFrame)
 | |
| {
 | |
|   PRBool repairStyleContext = PR_TRUE;
 | |
| 
 | |
|   // When reflowing a frame that is on the first-line, check and see
 | |
|   // if a special style context should be placed in the context chain.
 | |
|   if ((nsnull == mPrevInFlow) &&
 | |
|       (0 == aState.mLineLayout->GetLineNumber())) {
 | |
|     if (nsnull != mFirstLineStyle) {
 | |
|       // Update the child frames style to inherit from the first-line
 | |
|       // style.
 | |
| 
 | |
|       // XXX add code to check first and only do it if it needs doing!
 | |
| #ifdef REALLY_NOISY_FIRST_LINE
 | |
|       DumpStyleGeneaology(aFrame, "");
 | |
| #endif
 | |
| #ifdef NOISY_FIRST_LINE
 | |
|       ListTag(stdout);
 | |
|       printf(": ");
 | |
|       ((nsFrame*)aFrame)->ListTag(stdout);
 | |
|       printf(" adding in first-line style\n");
 | |
| #endif
 | |
|       aFrame->ReResolveStyleContext(&aState.mPresContext, mFirstLineStyle);
 | |
|       repairStyleContext = PR_FALSE;
 | |
| #ifdef REALLY_NOISY_FIRST_LINE
 | |
|       DumpStyleGeneaology(aFrame, "  ");
 | |
| #endif
 | |
|     }
 | |
|     if ((nsnull != mFirstLetterStyle) &&
 | |
|         aState.mLineLayout->GetFirstLetterStyleOK()) {
 | |
|       aFrame->ReResolveStyleContext(&aState.mPresContext, mFirstLetterStyle);
 | |
|       repairStyleContext = PR_FALSE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (repairStyleContext) {
 | |
|     // Update style context when appropriate
 | |
|     nsIStyleContext* kidSC;
 | |
|     aFrame->GetStyleContext(&kidSC);
 | |
|     if (nsnull != kidSC) {
 | |
|       nsIStyleContext* kidParentSC;
 | |
|       kidParentSC = kidSC->GetParent();
 | |
|       if (nsnull != kidParentSC) {
 | |
|         if (kidParentSC != mStyleContext) {
 | |
|           aFrame->ReResolveStyleContext(&aState.mPresContext, mStyleContext);
 | |
|         }
 | |
|         NS_RELEASE(kidParentSC);
 | |
|       }
 | |
|       NS_RELEASE(kidSC);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::DidReflowFrame(nsBlockReflowState& aState,
 | |
|                               nsLineBox* aLine,
 | |
|                               nsIFrame* aFrame,
 | |
|                               nsReflowStatus aStatus)
 | |
| {
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
 | |
|                                 nsLineBox* aLine,
 | |
|                                 PRBool& aKeepReflowGoing)
 | |
| {
 | |
|   NS_PRECONDITION(aKeepReflowGoing, "bad caller");
 | |
|   NS_PRECONDITION(0 == aState.mLineLayout->GetPlacedFrames(),
 | |
|                   "non-empty line with a block");
 | |
| 
 | |
|   nsresult rv = NS_OK;
 | |
| 
 | |
|   nsIFrame* frame = aLine->mFirstChild;
 | |
| 
 | |
|   // Prepare the inline reflow engine
 | |
|   nsBlockFrame* runInToFrame;
 | |
|   nsBlockFrame* compactWithFrame;
 | |
|   nscoord compactMarginWidth = 0;
 | |
|   PRBool isCompactFrame = PR_FALSE;
 | |
|   const nsStyleDisplay* display;
 | |
|   frame->GetStyleData(eStyleStruct_Display,
 | |
|                       (const nsStyleStruct*&) display);
 | |
|   switch (display->mDisplay) {
 | |
|   case NS_STYLE_DISPLAY_RUN_IN:
 | |
| #ifdef NOISY_RUNIN
 | |
|     ListTag(stdout);
 | |
|     printf(": trying to see if ");
 | |
|     aFrame->ListTag(stdout);
 | |
|     printf(" is a run-in candidate\n");
 | |
| #endif
 | |
|     runInToFrame = FindFollowingBlockFrame(frame);
 | |
|     if (nsnull != runInToFrame) {
 | |
| // XXX run-in frame should be pushed to the next-in-flow too if the
 | |
| // run-in-to frame is pushed.
 | |
|       nsRect r(0, aState.mY, 0, 0);
 | |
|       aLine->mBounds = r;
 | |
|       aLine->mCombinedArea = r;
 | |
|       aLine->mCarriedOutTopMargin = 0;
 | |
|       aLine->mCarriedOutBottomMargin = 0;
 | |
|       aLine->SetMarginFlags(0);
 | |
| #if XXX_need_line_outside_children
 | |
|       aLine->ClearOutsideChildren();
 | |
| #endif
 | |
|       aLine->mBreakType = NS_STYLE_CLEAR_NONE;
 | |
| //XXX        aFrame->WillReflow(aState.mPresContext);
 | |
|       frame->SetRect(r);
 | |
|       aState.mPrevChild = frame;
 | |
|       aState.mRunInToFrame = runInToFrame;
 | |
|       aState.mRunInFrame = (nsBlockFrame*) frame;
 | |
|       return rv;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|   case NS_STYLE_DISPLAY_COMPACT:
 | |
|     compactWithFrame = FindFollowingBlockFrame(frame);
 | |
|     if (nsnull != compactWithFrame) {
 | |
|       const nsStyleSpacing* spacing;
 | |
|       nsMargin margin;
 | |
|       nsresult rv;
 | |
|       rv = compactWithFrame->GetStyleData(eStyleStruct_Spacing,
 | |
|                                           (const nsStyleStruct*&) spacing);
 | |
|       if (NS_SUCCEEDED(rv) && (nsnull != spacing)) {
 | |
|         nsHTMLReflowState::ComputeMarginFor(compactWithFrame, &aState,
 | |
|                                             margin);
 | |
|         compactMarginWidth = margin.left;
 | |
|       }
 | |
|       isCompactFrame = PR_TRUE;
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   nsBlockReflowContext brc(aState.mPresContext, *aState.mLineLayout, aState);
 | |
|   brc.SetCompactMarginWidth(compactMarginWidth);
 | |
| 
 | |
|   // Clear floaters before the block if the clear style is not none
 | |
|   aLine->mBreakType = display->mBreakType;
 | |
|   if (NS_STYLE_CLEAR_NONE != display->mBreakType) {
 | |
|     switch (display->mBreakType) {
 | |
|     case NS_STYLE_CLEAR_LEFT:
 | |
|     case NS_STYLE_CLEAR_RIGHT:
 | |
|     case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
 | |
|       aState.ClearFloaters(aState.mY, display->mBreakType);
 | |
|       // XXX: ?If we just advanced Y then we need to factor that amount
 | |
|       // into the next margin calculation and reduce the amount of Y
 | |
|       // margin applied by the amount just moved.
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Set run-in frame if this is the run-in-to frame. That way the
 | |
|   // target block frame knows to pick up the children from the run-in
 | |
|   // frame.
 | |
|   if (frame == aState.mRunInToFrame) {
 | |
|     brc.SetRunInFrame(aState.mRunInFrame);
 | |
|   }
 | |
| 
 | |
|   // Compute the available space for the block
 | |
|   nscoord availHeight = aState.mUnconstrainedHeight
 | |
|     ? NS_UNCONSTRAINEDSIZE
 | |
|     : aState.mBottomEdge - aState.mY;
 | |
| 
 | |
|   // Now setup the availSpace rect. If the block frame we are
 | |
|   // reflowing is one of "ours" (block, run-in, compact, list-item)
 | |
|   // then we get it an available space that is *NOT* affected by
 | |
|   // floaters.  Otherwise we position the block outside of the
 | |
|   // floaters.
 | |
|   nscoord availX, availWidth;
 | |
|   nsSplittableType splitType;
 | |
|   switch (display->mDisplay) {
 | |
|   case NS_STYLE_DISPLAY_BLOCK:
 | |
|   case NS_STYLE_DISPLAY_RUN_IN:
 | |
|   case NS_STYLE_DISPLAY_COMPACT:
 | |
|   case NS_STYLE_DISPLAY_LIST_ITEM:
 | |
|     if (NS_SUCCEEDED(frame->IsSplittable(splitType)) &&
 | |
|         (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == splitType)) {
 | |
|       availX = aState.mBorderPadding.left;
 | |
|       availWidth = aState.mUnconstrainedWidth
 | |
|         ? NS_UNCONSTRAINEDSIZE
 | |
|         : aState.mContentArea.width;
 | |
|       break;
 | |
|     }
 | |
|     // Assume the frame is clueless about the space manager
 | |
|     // FALLTHROUGH
 | |
| 
 | |
|   default:
 | |
|     availX = aState.mAvailSpaceRect.x + aState.mBorderPadding.left;
 | |
|     availWidth = aState.mAvailSpaceRect.width;
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   // Reflow the block into the available space
 | |
|   nsRect availSpace(availX, aState.mY, availWidth, availHeight);
 | |
|   WillReflowFrame(aState, aLine, frame);
 | |
|   nsReflowStatus frameReflowStatus;
 | |
|   nsMargin computedOffsets;
 | |
|   rv = brc.ReflowBlock(frame, availSpace, aState.IsAdjacentWithTop(),
 | |
|                        computedOffsets, frameReflowStatus);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv;
 | |
|   }
 | |
|   DidReflowFrame(aState, aLine, frame, frameReflowStatus);
 | |
|   aState.mPrevChild = frame;
 | |
| 
 | |
| #if defined(REFLOW_STATUS_COVERAGE)
 | |
|   RecordReflowStatus(PR_TRUE, frameReflowStatus);
 | |
| #endif
 | |
| 
 | |
|   if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
 | |
|     // None of the child block fits.
 | |
|     PushLines(aState);
 | |
|     aKeepReflowGoing = PR_FALSE;
 | |
|     aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
 | |
|   }
 | |
|   else {
 | |
|     // Note: line-break-after a block is a nop
 | |
| 
 | |
|     // Try to place the child block
 | |
|     PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
 | |
|     PRBool applyTopMargin = aState.ShouldApplyTopMargin();
 | |
|     aKeepReflowGoing = brc.PlaceBlock(isAdjacentWithTop, applyTopMargin,
 | |
|                                       aState.mPrevBottomMargin, computedOffsets,
 | |
|                                       aLine->mBounds, aLine->mCombinedArea);
 | |
|     if (aKeepReflowGoing) {
 | |
|       // Some of the child block fit
 | |
| 
 | |
|       // Set carry out top margin value when margin is not being applied
 | |
|       if (!applyTopMargin) {
 | |
|         aState.mCarriedOutTopMargin = brc.GetCollapsedTopMargin();
 | |
|       }
 | |
| 
 | |
|       // Advance to new Y position
 | |
|       nscoord newY = aLine->mBounds.YMost();
 | |
|       if (isCompactFrame) {
 | |
|         // For compact frames, we don't adjust the Y coordinate at all IF
 | |
|         // the compact frame ended up fitting in the margin space
 | |
|         // allocated for it.
 | |
|         nsRect r;
 | |
|         frame->GetRect(r);
 | |
|         if (r.width <= compactMarginWidth) {
 | |
|           // XXX margins will be wrong
 | |
|           // XXX ltr/rtl for horizontal placement within the margin area
 | |
|           // XXX vertical alignment with the compactWith frame's *first line*
 | |
|           newY = aState.mY;
 | |
|         }
 | |
|       }
 | |
|       aState.mY = newY;
 | |
|       aLine->mCarriedOutTopMargin = brc.GetCarriedOutTopMargin();
 | |
|       aLine->mCarriedOutBottomMargin = brc.GetCarriedOutBottomMargin();
 | |
| 
 | |
|       // Continue the block frame now if it didn't completely fit in
 | |
|       // the available space.
 | |
|       if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
 | |
|         PRBool madeContinuation;
 | |
|         rv = CreateContinuationFor(aState, aLine, frame, madeContinuation);
 | |
|         if (NS_FAILED(rv)) {
 | |
|           return rv;
 | |
|         }
 | |
| 
 | |
|         // Push continuation to a new line, but only if we actually
 | |
|         // made one.
 | |
|         if (madeContinuation) {
 | |
|           frame->GetNextSibling(&frame);
 | |
|           nsLineBox* line = new nsLineBox(frame, 1, LINE_IS_BLOCK);
 | |
|           if (nsnull == line) {
 | |
|             return NS_ERROR_OUT_OF_MEMORY;
 | |
|           }
 | |
|           line->mNext = aLine->mNext;
 | |
|           aLine->mNext = line;
 | |
| 
 | |
|           // Do not count the continuation child on the line it used
 | |
|           // to be on
 | |
|           aLine->mChildCount--;
 | |
|         }
 | |
| 
 | |
|         // Advance to next line since some of the block fit. That way
 | |
|         // only the following lines will be pushed.
 | |
|         aState.mPrevLine = aLine;
 | |
|         PushLines(aState);
 | |
|         aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
 | |
|         aKeepReflowGoing = PR_FALSE;
 | |
| 
 | |
|         // The bottom margin for a block is only applied on the last
 | |
|         // flow block. Since we just continued the child block frame,
 | |
|         // we know that line->mFirstChild is not the last flow block
 | |
|         // therefore zero out the running margin value.
 | |
|         aState.mPrevBottomMargin = 0;
 | |
|       }
 | |
|       else {
 | |
|         aState.mPrevBottomMargin = brc.GetCollapsedBottomMargin();
 | |
|       }
 | |
| 
 | |
|       // Post-process the "line"
 | |
|       PostPlaceLine(aState, aLine, brc.GetMaxElementSize());
 | |
| 
 | |
|       // Notify anyone who cares that the line has been placed
 | |
|       DidPlaceLine(aState, aLine,
 | |
|                    applyTopMargin ? brc.GetCollapsedTopMargin() : 0,
 | |
|                    brc.GetCollapsedBottomMargin(), aKeepReflowGoing);
 | |
|     }
 | |
|     else {
 | |
|       // None of the block fits. Determine the correct reflow status.
 | |
|       if (aLine == mLines) {
 | |
|         // If it's our very first line then we need to be pushed to
 | |
|         // our parents next-in-flow. Therefore, return break-before
 | |
|         // status for our reflow status.
 | |
|         aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
 | |
|       }
 | |
|       else {
 | |
|         // Push the line that didn't fit and any lines that follow it
 | |
|         // to our next-in-flow.
 | |
|         PushLines(aState);
 | |
|         aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Reflow an inline frame. The reflow status is mapped from the frames
 | |
|  * reflow status to the lines reflow status (not to our reflow status).
 | |
|  * The line reflow status is simple: PR_TRUE means keep placing frames
 | |
|  * on the line; PR_FALSE means don't (the line is done). If the line
 | |
|  * has some sort of breaking affect then aLine->mBreakType will be set
 | |
|  * to something other than NS_STYLE_CLEAR_NONE.
 | |
|  */
 | |
| nsresult
 | |
| nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
 | |
|                                  nsLineBox* aLine,
 | |
|                                  nsIFrame* aFrame,
 | |
|                                  PRBool& aKeepLineGoing,
 | |
|                                  PRBool& aKeepReflowGoing)
 | |
| {
 | |
|   NS_PRECONDITION(aKeepLineGoing && aKeepReflowGoing, "bad caller");
 | |
| 
 | |
|   // Send pre-reflow notification
 | |
|   WillReflowFrame(aState, aLine, aFrame);
 | |
| 
 | |
|   // If it's currently ok to be reflowing in first-letter style then
 | |
|   // we must be about to reflow a frame that has first-letter style.
 | |
|   PRBool reflowingFirstLetter = aState.mLineLayout->GetFirstLetterStyleOK();
 | |
| 
 | |
|   // Reflow the inline frame
 | |
|   nsReflowStatus frameReflowStatus;
 | |
|   nsresult rv = aState.mInlineReflow->ReflowFrame(aFrame, aState.IsAdjacentWithTop(),
 | |
|                                                   frameReflowStatus);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
| #if defined(REFLOW_STATUS_COVERAGE)
 | |
|   RecordReflowStatus(PR_FALSE, frameReflowStatus);
 | |
| #endif
 | |
| 
 | |
|   // Send post-reflow notification
 | |
|   DidReflowFrame(aState, aLine, aFrame, frameReflowStatus);
 | |
|   aState.mPrevChild = aFrame;
 | |
| 
 | |
|   // Process the child frames reflow status. There are 5 cases:
 | |
|   // complete, not-complete, break-before, break-after-complete,
 | |
|   // break-after-not-complete. There are two situations: we are a
 | |
|   // block or we are an inline. This makes a total of 10 cases
 | |
|   // (fortunately, there is some overlap).
 | |
|   aLine->mBreakType = NS_STYLE_CLEAR_NONE;
 | |
|   if (NS_INLINE_IS_BREAK(frameReflowStatus)) {
 | |
|     // Always abort the line reflow (because a line break is the
 | |
|     // minimal amount of break we do).
 | |
|     aKeepLineGoing = PR_FALSE;
 | |
| 
 | |
|     // XXX what should aLine->mBreakType be set to in all these cases?
 | |
|     PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
 | |
|     NS_ASSERTION(breakType != NS_STYLE_CLEAR_NONE, "bad break type");
 | |
|     NS_ASSERTION(NS_STYLE_CLEAR_PAGE != breakType, "no page breaks yet");
 | |
| 
 | |
|     if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
 | |
|       // Break-before cases.
 | |
|       if (aFrame == aLine->mFirstChild) {
 | |
|         // All break-before's that occur at the first child on a
 | |
|         // line stop the overall reflow.
 | |
|         aKeepReflowGoing = PR_FALSE;
 | |
|         if (mLines == aLine) {
 | |
|           // If it's our first child on our first line then propogate
 | |
|           // outward the break-before reflow status unmodified.
 | |
|           aState.mReflowStatus = frameReflowStatus;
 | |
|         }
 | |
|         else {
 | |
|           // Its not our first line; push the remaining lines to a
 | |
|           // next-in-flow
 | |
|           PushLines(aState);
 | |
| 
 | |
|           // Adjust the reflow status to indicate a
 | |
|           // break-after-not-complete; because we need to be continued
 | |
|           // and we need to force a line break (or something stronger)
 | |
|           // upstream.
 | |
|           aState.mReflowStatus = NS_FRAME_NOT_COMPLETE | NS_INLINE_BREAK |
 | |
|             NS_INLINE_BREAK_AFTER | NS_INLINE_MAKE_BREAK_TYPE(breakType);
 | |
|         }
 | |
|       }
 | |
|       else {
 | |
|         // It's not the first child on this line so go ahead and split
 | |
|         // the line. We will see the frame again on the next-line.
 | |
|         rv = SplitLine(aState, aLine, aFrame);
 | |
|         if (NS_FAILED(rv)) {
 | |
|           return rv;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       // Break-after cases
 | |
|       aLine->mBreakType = breakType;
 | |
|       if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
 | |
|         // Create a continuation for the incomplete frame. Note that the
 | |
|         // frame may already have a continuation.
 | |
|         PRBool madeContinuation;
 | |
|         rv = CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
 | |
|         if (NS_FAILED(rv)) {
 | |
|           return rv;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Split line, but after the frame just reflowed
 | |
|       aFrame->GetNextSibling(&aFrame);
 | |
|       rv = SplitLine(aState, aLine, aFrame);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         return rv;
 | |
|       }
 | |
| 
 | |
|       // Mark next line dirty in case SplitLine didn't end up
 | |
|       // pushing any frames.
 | |
|       nsLineBox* next = aLine->mNext;
 | |
|       if ((nsnull != next) && !next->IsBlock()) {
 | |
|         next->MarkDirty();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   else if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
 | |
|     // Frame is not-complete, no special breaking status
 | |
| 
 | |
|     // Create a continuation for the incomplete frame. Note that the
 | |
|     // frame may already have a continuation.
 | |
|     PRBool madeContinuation;
 | |
|     rv = CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return rv;
 | |
|     }
 | |
| 
 | |
|     PRBool needSplit = PR_FALSE;
 | |
|     if (!reflowingFirstLetter) {
 | |
|       needSplit = PR_TRUE;
 | |
|     }
 | |
| 
 | |
|     if (needSplit) {
 | |
|       // Split line after the current frame
 | |
|       aKeepLineGoing = PR_FALSE;
 | |
|       aFrame->GetNextSibling(&aFrame);
 | |
|       rv = SplitLine(aState, aLine, aFrame);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         return rv;
 | |
|       }
 | |
|       // Mark next line dirty in case SplitLine didn't end up
 | |
|       // pushing any frames.
 | |
|       nsLineBox* next = aLine->mNext;
 | |
|       if ((nsnull != next) && !next->IsBlock()) {
 | |
|         next->MarkDirty();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Create a continuation, if necessary, for aFrame. Place it on the
 | |
|  * same line that aFrame is on. Set aMadeNewFrame to PR_TRUE if a
 | |
|  * new frame is created.
 | |
|  */
 | |
| nsresult
 | |
| nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
 | |
|                                      nsLineBox* aLine,
 | |
|                                      nsIFrame* aFrame,
 | |
|                                      PRBool& aMadeNewFrame)
 | |
| {
 | |
|   aMadeNewFrame = PR_FALSE;
 | |
|   nsresult rv;
 | |
|   nsIFrame* nextInFlow;
 | |
|   rv = CreateNextInFlow(aState.mPresContext, this, aFrame, nextInFlow);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv;
 | |
|   }
 | |
|   if (nsnull != nextInFlow) {
 | |
|     aMadeNewFrame = PR_TRUE;
 | |
|     aLine->mChildCount++;
 | |
| #ifdef NS_DEBUG
 | |
|     VerifyLineLength(aLine);
 | |
| #endif
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsBlockFrame::SplitLine(nsBlockReflowState& aState,
 | |
|                          nsLineBox* aLine,
 | |
|                          nsIFrame* aFrame)
 | |
| {
 | |
|   PRInt32 pushCount = aLine->ChildCount() -
 | |
|     aState.mInlineReflow->GetCurrentFrameNum();
 | |
|   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
 | |
|                  ("nsBlockFrame::SplitLine: pushing %d frames",
 | |
|                   pushCount));
 | |
|   if (0 != pushCount) {
 | |
|     NS_ASSERTION(aLine->ChildCount() > pushCount, "bad push");
 | |
|     NS_ASSERTION(nsnull != aFrame, "whoops");
 | |
|     nsLineBox* to = aLine->mNext;
 | |
|     if (nsnull != to) {
 | |
|       // Only push into the next line if it's empty; otherwise we can
 | |
|       // end up pushing a frame which is continued into the same frame
 | |
|       // as it's continuation. This causes all sorts of bad side
 | |
|       // effects so we don't allow it.
 | |
|       if (0 != to->ChildCount()) {
 | |
|         nsLineBox* insertedLine = new nsLineBox(aFrame, pushCount, 0);
 | |
|         if (nsnull == insertedLine) {
 | |
|           return NS_ERROR_OUT_OF_MEMORY;
 | |
|         }
 | |
|         aLine->mNext = insertedLine;
 | |
|         insertedLine->mNext = to;
 | |
|         to = insertedLine;
 | |
|       } else {
 | |
|         to->mFirstChild = aFrame;
 | |
|         to->mChildCount += pushCount;
 | |
|         to->MarkDirty();
 | |
|       }
 | |
|     } else {
 | |
|       to = new nsLineBox(aFrame, pushCount, 0);
 | |
|       if (nsnull == to) {
 | |
|         return NS_ERROR_OUT_OF_MEMORY;
 | |
|       }
 | |
|       aLine->mNext = to;
 | |
|     }
 | |
|     to->SetIsBlock(aLine->IsBlock());
 | |
|     aLine->mChildCount -= pushCount;
 | |
| #ifdef NS_DEBUG
 | |
|     VerifyLineLength(aLine);
 | |
| #endif
 | |
| 
 | |
|     // Let inline reflow know that some frames are no longer part of
 | |
|     // its state.
 | |
|     if (!aLine->IsBlock()) {
 | |
|       aState.mInlineReflow->ChangeFrameCount(aLine->ChildCount());
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| PRBool
 | |
| nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine)
 | |
| {
 | |
|   nsLineBox* next = aLine->mNext;
 | |
|   while (nsnull != next) {
 | |
|     // There is another line
 | |
|     if (0 != next->ChildCount()) {
 | |
|       // If the next line is a block line then we must not justify
 | |
|       // this line because it means that this line is the last in a
 | |
|       // group of inline lines.
 | |
|       return !next->IsBlock();
 | |
|     }
 | |
| 
 | |
|     // The next line is empty, try the next one
 | |
|     next = next->mNext;
 | |
|   }
 | |
| 
 | |
|   // XXX Not sure about this part
 | |
|   // Try our next-in-flows lines to answer the question
 | |
|   nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
 | |
|   while (nsnull != nextInFlow) {
 | |
|     nsLineBox* line = nextInFlow->mLines;
 | |
|     while (nsnull != line) {
 | |
|       if (0 != line->ChildCount()) {
 | |
|         return !line->IsBlock();
 | |
|       }
 | |
|       line = line->mNext;
 | |
|     }
 | |
|     nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
 | |
|   }
 | |
| 
 | |
|   // This is the last line - so don't allow justification
 | |
|   return PR_FALSE;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
 | |
|                          nsLineBox* aLine,
 | |
|                          PRBool& aKeepReflowGoing)
 | |
| {
 | |
|   nsresult rv = NS_OK;
 | |
| 
 | |
|   // Align the children. This also determines the actual height and
 | |
|   // width of the line.
 | |
|   nsInlineReflow& ir = *aState.mInlineReflow;
 | |
|   ir.VerticalAlignFrames(aLine->mBounds, aState.mAscent, aState.mDescent);
 | |
| 
 | |
|   // Only block frames horizontally align their children because
 | |
|   // inline frames "shrink-wrap" around their children (therefore
 | |
|   // there is no extra horizontal space).
 | |
|   PRBool allowJustify = PR_TRUE;
 | |
|   if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) {
 | |
|     allowJustify = ShouldJustifyLine(aState, aLine);
 | |
|   }
 | |
|   ir.TrimTrailingWhiteSpace(aLine->mBounds);
 | |
|   ir.HorizontalAlignFrames(aLine->mBounds, allowJustify);
 | |
|   ir.RelativePositionFrames(aLine->mCombinedArea);
 | |
| 
 | |
|   // Calculate the bottom margin for the line.
 | |
|   nscoord lineBottomMargin = 0;
 | |
|   if (0 == aLine->mBounds.height) {
 | |
|     nsIFrame* brFrame = aState.mLineLayout->GetBRFrame();
 | |
|     if (nsnull != brFrame) {
 | |
|       // If a line ends in a BR, and the line is empty of height, then
 | |
|       // we make sure that the line ends up with some height
 | |
|       // anyway. Note that the height looks like vertical margin so
 | |
|       // that it can compress with other block margins.
 | |
|       nsIStyleContext* brSC;
 | |
|       nsIPresContext& px = aState.mPresContext;
 | |
|       nsresult rv = brFrame->GetStyleContext(&brSC);
 | |
|       if ((NS_OK == rv) && (nsnull != brSC)) {
 | |
|         const nsStyleFont* font = (const nsStyleFont*)
 | |
|           brSC->GetStyleData(eStyleStruct_Font);
 | |
|         nsIFontMetrics* fm = px.GetMetricsFor(font->mFont);
 | |
|         if (nsnull != fm) {
 | |
|           fm->GetHeight(lineBottomMargin);
 | |
|           NS_RELEASE(fm);
 | |
|         }
 | |
|         NS_RELEASE(brSC);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     aState.mRunInFromFrame = nsnull;
 | |
|     aState.mRunInToFrame = nsnull;
 | |
|   }
 | |
| 
 | |
|   // Calculate the lines top and bottom margin values. The margin will
 | |
|   // come from an embedded block frame, not from inline
 | |
|   // frames. Because this is an "inline" line, the child margins are
 | |
|   // all effectively zero so we pass in nsMargin(0, 0, 0, 0).
 | |
|   nscoord topMargin, bottomMargin;
 | |
|   nsBlockReflowContext::CollapseMargins(nsMargin(0, 0, 0, 0),
 | |
|                                         ir.GetCarriedOutTopMargin(),
 | |
|                                         ir.GetCarriedOutBottomMargin(),
 | |
|                                         aLine->mBounds.height,
 | |
|                                         aState.mPrevBottomMargin,
 | |
|                                         topMargin, bottomMargin);
 | |
| 
 | |
| #if XXX
 | |
| ListTag(stdout);
 | |
| printf(": ");
 | |
| ((nsFrame*)(aLine->mFirstChild))->ListTag(stdout);
 | |
| printf(" mY=%d carried=%d,%d top=%d bottom=%d prev=%d shouldApply=%s\n",
 | |
|        aState.mY, ir.GetCarriedOutTopMargin(), ir.GetCarriedOutBottomMargin(),
 | |
|        topMargin, bottomMargin, aState.mPrevBottomMargin,
 | |
|        aState.ShouldApplyTopMargin() ? "yes" : "no");
 | |
| #endif
 | |
|   if (!aState.ShouldApplyTopMargin()) {
 | |
|     aState.mCarriedOutTopMargin = topMargin;
 | |
|     topMargin = 0;
 | |
|   }
 | |
| 
 | |
|   // See if the line fit. If it doesn't we need to push it. Our first
 | |
|   // line will always fit.
 | |
|   nscoord newY = aLine->mBounds.YMost() + topMargin + lineBottomMargin;
 | |
|   NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
 | |
|      ("nsBlockFrame::PlaceLine: newY=%d limit=%d lineHeight=%d",
 | |
|       newY, aState.mBottomEdge, aLine->mBounds.height));
 | |
|   if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
 | |
|     // Push this line and all of it's children and anything else that
 | |
|     // follows to our next-in-flow
 | |
|     PushLines(aState);
 | |
| 
 | |
|     // Stop reflow and whack the reflow status if reflow hasn't
 | |
|     // already been stopped.
 | |
|     if (aKeepReflowGoing) {
 | |
|       NS_ASSERTION(NS_FRAME_COMPLETE == aState.mReflowStatus,
 | |
|                    "lost reflow status");
 | |
|       aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
 | |
|       aKeepReflowGoing = PR_FALSE;
 | |
|     }
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
 | |
|   aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
 | |
|   aState.mPrevBottomMargin = bottomMargin;
 | |
|   if (0 != topMargin) {
 | |
|     // Apply collapsed top-margin value
 | |
|     SlideFrames(aState.mPresContext, aState.mSpaceManager, aLine, topMargin);
 | |
|     SlideFloaters(aState.mPresContext, aState.mSpaceManager, aLine, topMargin,
 | |
|                   PR_TRUE);
 | |
|   }
 | |
|   aState.mY = newY;
 | |
| 
 | |
|   PostPlaceLine(aState, aLine, ir.GetMaxElementSize());
 | |
| 
 | |
|   // Any below current line floaters to place?
 | |
|   if (0 != aState.mPendingFloaters.Count()) {
 | |
|     aState.PlaceBelowCurrentLineFloaters(&aState.mPendingFloaters);
 | |
|     aState.mPendingFloaters.Clear();
 | |
|   }
 | |
| 
 | |
|   // Apply break-after clearing if necessary
 | |
|   PRUint8 breakType = aLine->mBreakType;
 | |
|   switch (breakType) {
 | |
|   case NS_STYLE_CLEAR_LEFT:
 | |
|   case NS_STYLE_CLEAR_RIGHT:
 | |
|   case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
 | |
|     aState.ClearFloaters(aState.mY, breakType);
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   // Notify anyone who cares that the line has been placed
 | |
|   DidPlaceLine(aState, aLine, topMargin, bottomMargin, aKeepReflowGoing);
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| // Compute the line's max-element-size by adding into the raw value
 | |
| // computed by reflowing the contents of the line (aMaxElementSize)
 | |
| // the impact of floaters on this line or the preceeding lines.
 | |
| void
 | |
| nsBlockFrame::ComputeLineMaxElementSize(nsBlockReflowState& aState,
 | |
|                                          nsLineBox* aLine,
 | |
|                                          nsSize* aMaxElementSize)
 | |
| {
 | |
|   nscoord maxWidth, maxHeight;
 | |
|   aState.mCurrentBand.GetMaxElementSize(&maxWidth, &maxHeight);
 | |
| 
 | |
|   // Add in the maximum width of any floaters in the band because we
 | |
|   // always place some non-floating content with a floater.
 | |
|   aMaxElementSize->width += maxWidth;
 | |
| 
 | |
|   // If the maximum-height of the tallest floater is larger than the
 | |
|   // maximum-height of the content then update the max-element-size
 | |
|   // height
 | |
|   if (maxHeight > aMaxElementSize->height) {
 | |
|     aMaxElementSize->height = maxHeight;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState,
 | |
|                              nsLineBox* aLine,
 | |
|                              const nsSize& aMaxElementSize)
 | |
| {
 | |
|   // Update max-element-size
 | |
|   if (aState.mComputeMaxElementSize) {
 | |
|     nsSize lineMaxElementSize(aMaxElementSize);
 | |
|     if (0 != aState.mCurrentBand.GetFloaterCount()) {
 | |
|       // Add in floater impacts to the lines max-element-size
 | |
|       ComputeLineMaxElementSize(aState, aLine, &lineMaxElementSize);
 | |
|     }
 | |
|     if (lineMaxElementSize.width > aState.mMaxElementSize.width) {
 | |
|       aState.mMaxElementSize.width = lineMaxElementSize.width;
 | |
|     }
 | |
|     if (lineMaxElementSize.height > aState.mMaxElementSize.height) {
 | |
|       aState.mMaxElementSize.height = lineMaxElementSize.height;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #if XXX_need_line_outside_children
 | |
|   // Compute LINE_OUTSIDE_CHILDREN state for this line. The bit is set
 | |
|   // if any child frame has outside children.
 | |
|   if ((aLine->mCombinedArea.x < aLine->mBounds.x) ||
 | |
|       (aLine->mCombinedArea.XMost() > aLine->mBounds.XMost()) ||
 | |
|       (aLine->mCombinedArea.y < aLine->mBounds.y) ||
 | |
|       (aLine->mCombinedArea.YMost() > aLine->mBounds.YMost())) {
 | |
|     aLine->SetOutsideChildren();
 | |
|   }
 | |
|   else {
 | |
|     aLine->ClearOutsideChildren();
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // Update xmost
 | |
|   nscoord xmost = aLine->mBounds.XMost();
 | |
|   if (xmost > aState.mKidXMost) {
 | |
|     aState.mKidXMost = xmost;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::DidPlaceLine(nsBlockReflowState& aState,
 | |
|                            nsLineBox* aLine,
 | |
|                            nscoord aTopMargin, nscoord aBottomMargin,
 | |
|                            PRBool aLineReflowStatus)
 | |
| {
 | |
|   // Place the outside list bullet, if we have one
 | |
|   if ((nsnull == mPrevInFlow) && (nsnull != mBullet) &&
 | |
|       ShouldPlaceBullet(aLine)) {
 | |
|     nscoord ascent = aState.mAscent;
 | |
|     if (aLine->IsBlock()) {
 | |
|       ascent = 0;
 | |
| 
 | |
|       // For bullets that are placed next to a child block, there will
 | |
|       // be no correct ascent value. Therefore, make one up...
 | |
|       const nsStyleFont* font;
 | |
|       nsresult rv;
 | |
|       rv = aLine->mFirstChild->GetStyleData(eStyleStruct_Font,
 | |
|                                             (const nsStyleStruct*&) font);
 | |
|       if (NS_SUCCEEDED(rv) && (nsnull != font)) {
 | |
|         nsIRenderingContext& rc = *aState.rendContext;
 | |
|         rc.SetFont(font->mFont);
 | |
|         nsIFontMetrics* fm;
 | |
|         rv = rc.GetFontMetrics(fm);
 | |
|         if (NS_SUCCEEDED(rv) && (nsnull != fm)) {
 | |
|           fm->GetMaxAscent(ascent);
 | |
|           NS_RELEASE(fm);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     PlaceBullet(aState, ascent, aTopMargin);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static nsresult
 | |
| FindFloatersIn(nsIFrame* aFrame, nsVoidArray*& aArray)
 | |
| {
 | |
|   const nsStyleDisplay* display;
 | |
|   aFrame->GetStyleData(eStyleStruct_Display,
 | |
|                        (const nsStyleStruct*&) display);
 | |
|   if (NS_STYLE_FLOAT_NONE != display->mFloats) {
 | |
|     if (nsnull == aArray) {
 | |
|       aArray = new nsVoidArray();
 | |
|       if (nsnull == aArray) {
 | |
|         return NS_ERROR_OUT_OF_MEMORY;
 | |
|       }
 | |
|     }
 | |
|     aArray->AppendElement(aFrame);
 | |
|   }
 | |
| 
 | |
|   if (NS_STYLE_DISPLAY_INLINE == display->mDisplay) {
 | |
|     nsIFrame* kid;
 | |
|     aFrame->FirstChild(nsnull, &kid);
 | |
|     while (nsnull != kid) {
 | |
|       nsresult rv = FindFloatersIn(kid, aArray);
 | |
|       if (NS_OK != rv) {
 | |
|         return rv;
 | |
|       }
 | |
|       kid->GetNextSibling(&kid);
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::FindFloaters(nsLineBox* aLine)
 | |
| {
 | |
|   nsVoidArray* floaters = aLine->mFloaters;
 | |
|   if (nsnull != floaters) {
 | |
|     // Empty floater array before proceeding
 | |
|     floaters->Clear();
 | |
|   }
 | |
| 
 | |
|   nsIFrame* frame = aLine->mFirstChild;
 | |
|   PRInt32 n = aLine->ChildCount();
 | |
|   while (--n >= 0) {
 | |
|     FindFloatersIn(frame, floaters);
 | |
|     frame->GetNextSibling(&frame);
 | |
|   }
 | |
| 
 | |
|   aLine->mFloaters = floaters;
 | |
| 
 | |
|   // Get rid of floater array if we don't need it
 | |
|   if (nsnull != floaters) {
 | |
|     if (0 == floaters->Count()) {
 | |
|       delete floaters;
 | |
|       aLine->mFloaters = nsnull;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::PushLines(nsBlockReflowState& aState)
 | |
| {
 | |
|   NS_ASSERTION(nsnull != aState.mPrevLine, "bad push");
 | |
| 
 | |
|   nsLineBox* lastLine = aState.mPrevLine;
 | |
|   nsLineBox* nextLine = lastLine->mNext;
 | |
| 
 | |
|   lastLine->mNext = nsnull;
 | |
|   mOverflowLines = nextLine;
 | |
| 
 | |
|   // Mark all the overflow lines dirty so that they get reflowed when
 | |
|   // they are pulled up by our next-in-flow.
 | |
|   while (nsnull != nextLine) {
 | |
|     nextLine->MarkDirty();
 | |
|     nextLine = nextLine->mNext;
 | |
|   }
 | |
| 
 | |
|   // Break frame sibling list
 | |
|   nsIFrame* lastFrame = lastLine->LastChild();
 | |
|   lastFrame->SetNextSibling(nsnull);
 | |
| 
 | |
|   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
 | |
|      ("nsBlockFrame::PushLines: line=%p prevInFlow=%p nextInFlow=%p",
 | |
|       mOverflowLines, mPrevInFlow, mNextInFlow));
 | |
| #ifdef NS_DEBUG
 | |
|   if (GetVerifyTreeEnable()) {
 | |
| //XXX    VerifyChildCount(mLines);
 | |
| //XXX    VerifyChildCount(mOverflowLines, PR_TRUE);
 | |
|   }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| PRBool
 | |
| nsBlockFrame::DrainOverflowLines()
 | |
| {
 | |
|   PRBool drained = PR_FALSE;
 | |
| 
 | |
|   // First grab the prev-in-flows overflow lines
 | |
|   nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
 | |
|   if (nsnull != prevBlock) {
 | |
|     nsLineBox* line = prevBlock->mOverflowLines;
 | |
|     if (nsnull != line) {
 | |
|       drained = PR_TRUE;
 | |
|       NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
 | |
|          ("nsBlockFrame::DrainOverflowLines: line=%p prevInFlow=%p",
 | |
|           line, prevBlock));
 | |
|       prevBlock->mOverflowLines = nsnull;
 | |
| 
 | |
|       // Make all the frames on the mOverflowLines list mine
 | |
|       nsIFrame* lastFrame = nsnull;
 | |
|       nsIFrame* frame = line->mFirstChild;
 | |
|       while (nsnull != frame) {
 | |
|         frame->SetParent(this);
 | |
|         lastFrame = frame;
 | |
|         frame->GetNextSibling(&frame);
 | |
|       }
 | |
| 
 | |
|       // Join the line lists
 | |
|       if (nsnull == mLines) {
 | |
|         mLines = line;
 | |
|       }
 | |
|       else {
 | |
|         // Join the sibling lists together
 | |
|         lastFrame->SetNextSibling(mLines->mFirstChild);
 | |
| 
 | |
|         // Place overflow lines at the front of our line list
 | |
|         nsLineBox* lastLine = nsLineBox::LastLine(line);
 | |
|         lastLine->mNext = mLines;
 | |
|         mLines = line;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Now grab our own overflow lines
 | |
|   if (nsnull != mOverflowLines) {
 | |
|     // This can happen when we reflow and not everything fits and then
 | |
|     // we are told to reflow again before a next-in-flow is created
 | |
|     // and reflows.
 | |
|     NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
 | |
|        ("nsBlockFrame::DrainOverflowLines: from me, line=%p",
 | |
|         mOverflowLines));
 | |
|     nsLineBox* lastLine = nsLineBox::LastLine(mLines);
 | |
|     if (nsnull == lastLine) {
 | |
|       mLines = mOverflowLines;
 | |
|     }
 | |
|     else {
 | |
|       lastLine->mNext = mOverflowLines;
 | |
|       nsIFrame* lastFrame = lastLine->LastChild();
 | |
|       lastFrame->SetNextSibling(mOverflowLines->mFirstChild);
 | |
| 
 | |
|       // Update our last-content-index now that we have a new last child
 | |
|       lastLine = nsLineBox::LastLine(mOverflowLines);
 | |
|     }
 | |
|     mOverflowLines = nsnull;
 | |
|     drained = PR_TRUE;
 | |
|   }
 | |
| 
 | |
| #ifdef NS_DEBUG
 | |
|   if (GetVerifyTreeEnable()) {
 | |
| //XXX    VerifyChildCount(mLines, PR_TRUE);
 | |
|   }
 | |
| #endif
 | |
|   return drained;
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////////
 | |
| // Frame list manipulation routines
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::AppendFrames(nsIPresContext& aPresContext,
 | |
|                             nsIPresShell&   aPresShell,
 | |
|                             nsIAtom*        aListName,
 | |
|                             nsIFrame*       aFrameList)
 | |
| {
 | |
|   if (nsnull != aListName) {
 | |
|     // XXX temporary until area frame code is updated
 | |
|     return nsFrame::AppendFrames(aPresContext, aPresShell, aListName, aFrameList);
 | |
|   }
 | |
|   nsresult rv = AppendNewFrames(aPresContext, aFrameList);
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
| //    RenumberLists(aState);
 | |
| //    rv = ComputeTextRuns(aState);
 | |
| 
 | |
|     nsIReflowCommand* reflowCmd = nsnull;
 | |
|     nsresult rv;
 | |
|     rv = NS_NewHTMLReflowCommand(&reflowCmd, this,
 | |
|                                  nsIReflowCommand::FrameAppended,
 | |
|                                  nsnull);
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|       if (nsnull != aListName) {
 | |
|         reflowCmd->SetChildListName(aListName);
 | |
|       }
 | |
|       aPresShell.AppendReflowCommand(reflowCmd);
 | |
|       NS_RELEASE(reflowCmd);
 | |
|     }
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsBlockFrame::AppendNewFrames(nsIPresContext& aPresContext,
 | |
|                                nsIFrame* aNewFrame)
 | |
| {
 | |
|   // Get our last line and then get its last child
 | |
|   nsIFrame* lastFrame;
 | |
|   nsLineBox* lastLine = nsLineBox::LastLine(mLines);
 | |
|   if (nsnull != lastLine) {
 | |
|     lastFrame = lastLine->LastChild();
 | |
|   } else {
 | |
|     lastFrame = nsnull;
 | |
|   }
 | |
| 
 | |
|   // Add the new frames to the sibling list; wrap any frames that
 | |
|   // require wrapping
 | |
|   if (nsnull != lastFrame) {
 | |
|     lastFrame->SetNextSibling(aNewFrame);
 | |
|   }
 | |
|   nsresult rv;
 | |
| 
 | |
|   // Make sure that new inlines go onto the end of the lastLine when
 | |
|   // the lastLine is mapping inline frames.
 | |
|   PRInt32 pendingInlines = 0;
 | |
|   if (nsnull != lastLine) {
 | |
|     if (!lastLine->IsBlock()) {
 | |
|       pendingInlines = 1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Now create some lines for the new frames
 | |
|   nsIFrame* prevFrame = lastFrame;
 | |
|   for (nsIFrame* frame = aNewFrame; nsnull != frame;
 | |
|        frame->GetNextSibling(&frame)) {
 | |
|     // See if the child is a block or non-block
 | |
|     const nsStyleDisplay* kidDisplay;
 | |
|     rv = frame->GetStyleData(eStyleStruct_Display,
 | |
|                              (const nsStyleStruct*&) kidDisplay);
 | |
|     if (NS_OK != rv) {
 | |
|       return rv;
 | |
|     }
 | |
|     const nsStylePosition* kidPosition;
 | |
|     rv = frame->GetStyleData(eStyleStruct_Position,
 | |
|                              (const nsStyleStruct*&) kidPosition);
 | |
|     if (NS_OK != rv) {
 | |
|       return rv;
 | |
|     }
 | |
|     PRBool isBlock = nsLineLayout::TreatFrameAsBlock(kidDisplay, kidPosition);
 | |
| 
 | |
|     // See if we need to move the frame outside of the flow, and insert a
 | |
|     // placeholder frame in its place
 | |
|     nsIFrame* placeholder;
 | |
|     if (MoveFrameOutOfFlow(aPresContext, frame, kidDisplay, kidPosition,
 | |
|                            placeholder)) {
 | |
|       // Reset the previous frame's next sibling pointer
 | |
|       if (nsnull != prevFrame) {
 | |
|         prevFrame->SetNextSibling(placeholder);
 | |
|       }
 | |
| 
 | |
|       // The placeholder frame is always inline
 | |
|       frame = placeholder;
 | |
|       isBlock = PR_FALSE;
 | |
|     }
 | |
|     else {
 | |
|       // Wrap the frame in a view if necessary
 | |
|       nsIStyleContext* kidSC;
 | |
|       frame->GetStyleContext(&kidSC);
 | |
|       rv = CreateViewForFrame(aPresContext, frame, kidSC, PR_FALSE);
 | |
|       NS_RELEASE(kidSC);
 | |
|       if (NS_OK != rv) {
 | |
|         return rv;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // If the child is an inline then add it to the lastLine (if it's
 | |
|     // an inline line, otherwise make a new line). If the child is a
 | |
|     // block then make a new line and put the child in that line.
 | |
|     if (isBlock) {
 | |
|       // If the previous line has pending inline data to be reflowed,
 | |
|       // do so now.
 | |
|       if (0 != pendingInlines) {
 | |
|         // Set this to true in case we don't end up reflowing all of the
 | |
|         // frames on the line (because they end up being pushed).
 | |
|         lastLine->MarkDirty();
 | |
|         pendingInlines = 0;
 | |
|       }
 | |
| 
 | |
|       // Create a line for the block
 | |
|       nsLineBox* line = new nsLineBox(frame, 1, LINE_IS_BLOCK);
 | |
|       if (nsnull == line) {
 | |
|         return NS_ERROR_OUT_OF_MEMORY;
 | |
|       }
 | |
|       if (nsnull == lastLine) {
 | |
|         mLines = line;
 | |
|       }
 | |
|       else {
 | |
|         lastLine->mNext = line;
 | |
|       }
 | |
|       lastLine = line;
 | |
|     }
 | |
|     else {
 | |
|       if (0 == pendingInlines) {
 | |
|         nsLineBox* line = new nsLineBox(frame, 0, 0);
 | |
|         if (nsnull == line) {
 | |
|           return NS_ERROR_OUT_OF_MEMORY;
 | |
|         }
 | |
|         if (nsnull == lastLine) {
 | |
|           mLines = line;
 | |
|         }
 | |
|         else {
 | |
|           lastLine->mNext = line;
 | |
|         }
 | |
|         lastLine = line;
 | |
|       }
 | |
|       lastLine->mChildCount++;
 | |
|       pendingInlines++;
 | |
|     }
 | |
| 
 | |
|     // Remember the previous frame
 | |
|     prevFrame = frame;
 | |
|   }
 | |
| 
 | |
|   if (0 != pendingInlines) {
 | |
|     // Set this to true in case we don't end up reflowing all of the
 | |
|     // frames on the line (because they end up being pushed).
 | |
|     lastLine->MarkDirty();
 | |
|   }
 | |
| 
 | |
|   MarkEmptyLines(aPresContext);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::InsertFrames(nsIPresContext& aPresContext,
 | |
|                             nsIPresShell&   aPresShell,
 | |
|                             nsIAtom*        aListName,
 | |
|                             nsIFrame*       aPrevFrame,
 | |
|                             nsIFrame*       aFrameList)
 | |
| {
 | |
|   if (nsnull != aListName) {
 | |
|     // XXX temporary until area frame code is updated
 | |
|     return nsFrame::InsertFrames(aPresContext, aPresShell, aListName, aPrevFrame, aFrameList);
 | |
|   }
 | |
|   nsresult rv = InsertNewFrames(aPresContext, aFrameList, aPrevFrame);
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
|     nsIReflowCommand* reflowCmd = nsnull;
 | |
|     nsresult rv;
 | |
|     rv = NS_NewHTMLReflowCommand(&reflowCmd, this,
 | |
|                                  nsIReflowCommand::ReflowDirty,
 | |
|                                  nsnull);
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|       if (nsnull != aListName) {
 | |
|         reflowCmd->SetChildListName(aListName);
 | |
|       }
 | |
|       aPresShell.AppendReflowCommand(reflowCmd);
 | |
|       NS_RELEASE(reflowCmd);
 | |
|     }
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| // XXX rewrite to deal with a list of frames
 | |
| nsresult
 | |
| nsBlockFrame::InsertNewFrames(nsIPresContext& aPresContext,
 | |
|                                nsIFrame*       aFrameList,
 | |
|                                nsIFrame*       aPrevSibling)
 | |
| {
 | |
|   if (nsnull == mLines) {
 | |
|     NS_ASSERTION(nsnull == aPrevSibling, "prev-sibling and empty line list!");
 | |
|     return AppendNewFrames(aPresContext, aFrameList);
 | |
|   }
 | |
| 
 | |
|   nsIFrame* newFrame = aFrameList;
 | |
|   while (nsnull != newFrame) {
 | |
|     nsIFrame* next;
 | |
|     newFrame->GetNextSibling(&next);
 | |
|     newFrame->SetNextSibling(nsnull);
 | |
| 
 | |
|     const nsStyleDisplay* display;
 | |
|     newFrame->GetStyleData(eStyleStruct_Display,
 | |
|                            (const nsStyleStruct*&) display);
 | |
|     const nsStylePosition* position;
 | |
|     newFrame->GetStyleData(eStyleStruct_Position,
 | |
|                            (const nsStyleStruct*&) position);
 | |
|     PRUint16 newFrameIsBlock =
 | |
|       nsLineLayout::TreatFrameAsBlock(display, position)
 | |
|       ? LINE_IS_BLOCK
 | |
|       : 0;
 | |
| 
 | |
|     // See if we need to move the frame outside of the flow, and insert a
 | |
|     // placeholder frame in its place
 | |
|     nsIFrame* placeholder;
 | |
|     if (MoveFrameOutOfFlow(aPresContext, newFrame, display, position,
 | |
|                            placeholder)) {
 | |
|       // Add the placeholder frame to the flow
 | |
|       newFrame = placeholder;
 | |
|       newFrameIsBlock = PR_FALSE;  // placeholder frame is always inline
 | |
|     }
 | |
|     else {
 | |
|       // Wrap the frame in a view if necessary
 | |
|       nsIStyleContext* kidSC;
 | |
|       newFrame->GetStyleContext(&kidSC);
 | |
|       nsresult rv = CreateViewForFrame(aPresContext, newFrame, kidSC, PR_FALSE);    
 | |
|       NS_RELEASE(kidSC);
 | |
|       if (NS_OK != rv) {
 | |
|         return rv;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Insert/append the frame into flows line list at the right spot
 | |
|     nsLineBox* newLine;
 | |
|     nsLineBox* line = mLines;
 | |
|     if (nsnull == aPrevSibling) {
 | |
|       // Insert new frame into the sibling list
 | |
|       newFrame->SetNextSibling(line->mFirstChild);
 | |
| 
 | |
|       if (line->IsBlock() || newFrameIsBlock) {
 | |
|         // Create a new line
 | |
|         newLine = new nsLineBox(newFrame, 1, newFrameIsBlock);
 | |
|         if (nsnull == newLine) {
 | |
|           return NS_ERROR_OUT_OF_MEMORY;
 | |
|         }
 | |
|         newLine->mNext = mLines;
 | |
|         mLines = newLine;
 | |
|       } else {
 | |
|         // Insert frame at the front of the line
 | |
|         line->mFirstChild = newFrame;
 | |
|         line->mChildCount++;
 | |
|         line->MarkDirty();
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       // Find line containing the previous sibling to the new frame
 | |
|       line = nsLineBox::FindLineContaining(line, aPrevSibling);
 | |
|       NS_ASSERTION(nsnull != line, "no line contains the previous sibling");
 | |
|       if (nsnull != line) {
 | |
|         if (line->IsBlock()) {
 | |
|           // Create a new line just after line
 | |
|           newLine = new nsLineBox(newFrame, 1, newFrameIsBlock);
 | |
|           if (nsnull == newLine) {
 | |
|             return NS_ERROR_OUT_OF_MEMORY;
 | |
|           }
 | |
|           newLine->mNext = line->mNext;
 | |
|           line->mNext = newLine;
 | |
|         }
 | |
|         else if (newFrameIsBlock) {
 | |
|           // Split line in two, if necessary. We can't allow a block to
 | |
|           // end up in an inline line.
 | |
|           if (line->IsLastChild(aPrevSibling)) {
 | |
|             // The new frame goes after prevSibling and prevSibling is
 | |
|             // the last frame on the line. Therefore we don't need to
 | |
|             // split the line, just create a new line.
 | |
|             newLine = new nsLineBox(newFrame, 1, newFrameIsBlock);
 | |
|             if (nsnull == newLine) {
 | |
|               return NS_ERROR_OUT_OF_MEMORY;
 | |
|             }
 | |
|             newLine->mNext = line->mNext;
 | |
|             line->mNext = newLine;
 | |
|           }
 | |
|           else {
 | |
|             // The new frame goes after prevSibling and prevSibling is
 | |
|             // somewhere in the line, but not at the end. Split the line
 | |
|             // just after prevSibling.
 | |
|             PRInt32 i, n = line->ChildCount();
 | |
|             nsIFrame* frame = line->mFirstChild;
 | |
|             for (i = 0; i < n; i++) {
 | |
|               if (frame == aPrevSibling) {
 | |
|                 nsIFrame* nextSibling;
 | |
|                 aPrevSibling->GetNextSibling(&nextSibling);
 | |
| 
 | |
|                 // Create new line to hold the remaining frames
 | |
|                 NS_ASSERTION(n - i - 1 > 0, "bad line count");
 | |
|                 newLine = new nsLineBox(nextSibling, n - i - 1, 0);
 | |
|                 if (nsnull == newLine) {
 | |
|                   return NS_ERROR_OUT_OF_MEMORY;
 | |
|                 }
 | |
|                 newLine->mNext = line->mNext;
 | |
|                 line->mNext = newLine;
 | |
|                 line->MarkDirty();
 | |
|                 line->mChildCount = i + 1;
 | |
|                 break;
 | |
|               }
 | |
|               frame->GetNextSibling(&frame);
 | |
|             }
 | |
| 
 | |
|             // Now create a new line to hold the block
 | |
|             newLine = new nsLineBox(newFrame, 1, newFrameIsBlock);
 | |
|             if (nsnull == newLine) {
 | |
|               return NS_ERROR_OUT_OF_MEMORY;
 | |
|             }
 | |
|             newLine->mNext = line->mNext;
 | |
|             line->mNext = newLine;
 | |
|           }
 | |
|         }
 | |
|         else {
 | |
|           // Insert frame into the line.
 | |
|           //XXX NS_ASSERTION(line->GetLastContentIsComplete(), "bad line LCIC");
 | |
|           line->mChildCount++;
 | |
|           line->MarkDirty();
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Insert new frame into the sibling list; note: this must be done
 | |
|       // after the above logic because the above logic depends on the
 | |
|       // sibling list being in the "before insertion" state.
 | |
|       nsIFrame* nextSibling;
 | |
|       aPrevSibling->GetNextSibling(&nextSibling);
 | |
|       newFrame->SetNextSibling(nextSibling);
 | |
|       aPrevSibling->SetNextSibling(newFrame);
 | |
|     }
 | |
|     aPrevSibling = newFrame;
 | |
|     newFrame = next;
 | |
|   }
 | |
| 
 | |
|   MarkEmptyLines(aPresContext);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::RemoveFrame(nsIPresContext& aPresContext,
 | |
|                            nsIPresShell&   aPresShell,
 | |
|                            nsIAtom*        aListName,
 | |
|                            nsIFrame*       aOldFrame)
 | |
| {
 | |
|   if (nsnull != aListName) {
 | |
|     // XXX temporary until area frame code is updated
 | |
|     return nsFrame::RemoveFrame(aPresContext, aPresShell, aListName, aOldFrame);
 | |
|   }
 | |
|   nsresult rv = DoRemoveFrame(aPresContext, aOldFrame);
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
|     nsIReflowCommand* reflowCmd = nsnull;
 | |
|     nsresult rv;
 | |
|     rv = NS_NewHTMLReflowCommand(&reflowCmd, this,
 | |
|                                  nsIReflowCommand::ReflowDirty,
 | |
|                                  nsnull);
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|       if (nsnull != aListName) {
 | |
|         reflowCmd->SetChildListName(aListName);
 | |
|       }
 | |
|       aPresShell.AppendReflowCommand(reflowCmd);
 | |
|       NS_RELEASE(reflowCmd);
 | |
|     }
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| // XXX need code in here to join two inline lines together if a block
 | |
| // is deleted between them.
 | |
| nsresult
 | |
| nsBlockFrame::DoRemoveFrame(nsIPresContext& aPresContext,
 | |
|                              nsIFrame* aDeletedFrame)
 | |
| {
 | |
|   // Find the line and the previous sibling that contains
 | |
|   // deletedFrame; we also find the pointer to the line.
 | |
|   nsBlockFrame* flow = this;
 | |
|   nsLineBox** linep = &flow->mLines;
 | |
|   nsLineBox* line = flow->mLines;
 | |
|   nsLineBox* prevLine = nsnull;
 | |
|   nsIFrame* prevSibling = nsnull;
 | |
|   while (nsnull != line) {
 | |
|     nsIFrame* frame = line->mFirstChild;
 | |
|     PRInt32 n = line->ChildCount();
 | |
|     while (--n >= 0) {
 | |
|       if (frame == aDeletedFrame) {
 | |
|         goto found_frame;
 | |
|       }
 | |
|       prevSibling = frame;
 | |
|       frame->GetNextSibling(&frame);
 | |
|     }
 | |
|     linep = &line->mNext;
 | |
|     prevLine = line;
 | |
|     line = line->mNext;
 | |
|   }
 | |
|  found_frame:;
 | |
| #ifdef NS_DEBUG
 | |
|   NS_ASSERTION(nsnull != line, "can't find deleted frame in lines");
 | |
|   if (nsnull != prevSibling) {
 | |
|     nsIFrame* tmp;
 | |
|     prevSibling->GetNextSibling(&tmp);
 | |
|     NS_ASSERTION(tmp == aDeletedFrame, "bad prevSibling");
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // Remove frame and all of its continuations
 | |
|   while (nsnull != aDeletedFrame) {
 | |
|     while ((nsnull != line) && (nsnull != aDeletedFrame)) {
 | |
| #ifdef NS_DEBUG
 | |
|       nsIFrame* parent;
 | |
|       aDeletedFrame->GetParent(&parent);
 | |
|       NS_ASSERTION(flow == parent, "messed up delete code");
 | |
| #endif
 | |
|       NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
 | |
|          ("nsBlockFrame::ContentDeleted: deadFrame=%p", aDeletedFrame));
 | |
| 
 | |
|       // See if the frame is a floater (actually, the floaters
 | |
|       // placeholder). If it is, then destroy the floated frame too.
 | |
|       const nsStyleDisplay* display;
 | |
|       nsresult rv = aDeletedFrame->GetStyleData(eStyleStruct_Display,
 | |
|                                                 (const nsStyleStruct*&)display);
 | |
|       if (NS_SUCCEEDED(rv) && (nsnull != display)) {
 | |
|         // XXX Sanitize "IsFloating" question *everywhere* (add a
 | |
|         // static method on nsFrame?)
 | |
|         if (NS_STYLE_FLOAT_NONE != display->mFloats) {
 | |
|           nsPlaceholderFrame* ph = (nsPlaceholderFrame*) aDeletedFrame;
 | |
|           nsIFrame* floater = ph->GetAnchoredItem();
 | |
|           if (nsnull != floater) {
 | |
|             floater->DeleteFrame(aPresContext);
 | |
|             if (nsnull != line->mFloaters) {
 | |
|               // Wipe out the floater array for this line. It will get
 | |
|               // recomputed during reflow anyway.
 | |
|               delete line->mFloaters;
 | |
|               line->mFloaters = nsnull;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Get the deleted frames next sibling
 | |
|       nsIFrame* nextFrame;
 | |
|       aDeletedFrame->GetNextSibling(&nextFrame);
 | |
| 
 | |
|       // Remove aDeletedFrame from the line
 | |
|       if (line->mFirstChild == aDeletedFrame) {
 | |
|         line->mFirstChild = nextFrame;
 | |
|         if (!line->IsBlock() && (nsnull != prevLine) && !prevLine->IsBlock()) {
 | |
|           // Make sure the previous line (if it's an inline line) gets
 | |
|           // a reflow too so that it can pullup from the line where we
 | |
|           // just removed the frame.
 | |
|           prevLine->MarkDirty();
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Take aDeletedFrame out of the sibling list. Note that
 | |
|       // prevSibling will only be nsnull when we are deleting the very
 | |
|       // first frame.
 | |
|       if (nsnull != prevSibling) {
 | |
|         prevSibling->SetNextSibling(nextFrame);
 | |
|       }
 | |
| 
 | |
|       // Destroy frame; capture its next-in-flow first in case we need
 | |
|       // to destroy that too.
 | |
|       nsIFrame* nextInFlow;
 | |
|       aDeletedFrame->GetNextInFlow(nextInFlow);
 | |
|       if (nsnull != nextInFlow) {
 | |
|         aDeletedFrame->BreakFromNextFlow();
 | |
|       }
 | |
|       aDeletedFrame->DeleteFrame(aPresContext);
 | |
|       aDeletedFrame = nextInFlow;
 | |
| 
 | |
|       // If line is empty, remove it now
 | |
|       nsLineBox* next = line->mNext;
 | |
|       if (0 == --line->mChildCount) {
 | |
|         *linep = next;
 | |
|         line->mNext = nsnull;
 | |
|         delete line;
 | |
|       }
 | |
|       else {
 | |
|         line->MarkDirty();
 | |
|         linep = &line->mNext;
 | |
|       }
 | |
|       prevLine = line;
 | |
|       line = next;
 | |
| 
 | |
|       // See if we should keep looking in the current flow's line list.
 | |
|       if (nsnull != aDeletedFrame) {
 | |
|         if (aDeletedFrame != nextFrame) {
 | |
|           // The deceased frames continuation is not the next frame in
 | |
|           // the current flow's frame list. Therefore we know that the
 | |
|           // continuation is in a different parent. So break out of
 | |
|           // the loop so that we advance to the next parent.
 | |
| #ifdef NS_DEBUG
 | |
|           nsIFrame* parent;
 | |
|           aDeletedFrame->GetParent(&parent);
 | |
|           NS_ASSERTION(parent != flow, "strange continuation");
 | |
| #endif
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Advance to next flow block if the frame has more continuations
 | |
|     if (nsnull != aDeletedFrame) {
 | |
|       flow = (nsBlockFrame*) flow->mNextInFlow;
 | |
|       NS_ASSERTION(nsnull != flow, "whoops, continuation without a parent");
 | |
|       prevLine = nsnull;
 | |
|       line = flow->mLines;
 | |
|       prevSibling = nsnull;
 | |
|     }
 | |
|   }
 | |
|   MarkEmptyLines(aPresContext);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| static PRBool
 | |
| IsEmptyLine(nsIPresContext& aPresContext, nsLineBox* aLine)
 | |
| {
 | |
|   PRInt32 i, n = aLine->ChildCount();
 | |
|   nsIFrame* frame = aLine->mFirstChild;
 | |
|   for (i = 0; i < n; i++) {
 | |
|     nsIContent* content;
 | |
|     nsresult rv = frame->GetContent(&content);
 | |
|     if (NS_FAILED(rv) || (nsnull == content)) {
 | |
|       // If it doesn't have any content then this can't be an empty line
 | |
|       return PR_FALSE;
 | |
|     }
 | |
|     nsITextContent* tc;
 | |
|     rv = content->QueryInterface(kITextContentIID, (void**) &tc);
 | |
|     if (NS_FAILED(rv) || (nsnull == tc)) {
 | |
|       // If it's not text content then this can't be an empty line
 | |
|       NS_RELEASE(content);
 | |
|       return PR_FALSE;
 | |
|     }
 | |
| 
 | |
|     const nsTextFragment* frag;
 | |
|     PRInt32 numFrags;
 | |
|     rv = tc->GetText(frag, numFrags);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       NS_RELEASE(content);
 | |
|       NS_RELEASE(tc);
 | |
|       return PR_FALSE;
 | |
|     }
 | |
| 
 | |
|     // If the text has any non-whitespace characters in it then the
 | |
|     // line is not an empty line.
 | |
|     while (--numFrags >= 0) {
 | |
|       PRInt32 len = frag->GetLength();
 | |
|       if (frag->Is2b()) {
 | |
|         const PRUnichar* cp = frag->Get2b();
 | |
|         const PRUnichar* end = cp + len;
 | |
|         while (cp < end) {
 | |
|           PRUnichar ch = *cp++;
 | |
|           if (!XP_IS_SPACE(ch)) {
 | |
|             NS_RELEASE(tc);
 | |
|             NS_RELEASE(content);
 | |
|             return PR_FALSE;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       else {
 | |
|         const char* cp = frag->Get1b();
 | |
|         const char* end = cp + len;
 | |
|         while (cp < end) {
 | |
|           char ch = *cp++;
 | |
|           if (!XP_IS_SPACE(ch)) {
 | |
|             NS_RELEASE(tc);
 | |
|             NS_RELEASE(content);
 | |
|             return PR_FALSE;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       frag++;
 | |
|     }
 | |
| 
 | |
|     NS_RELEASE(tc);
 | |
|     NS_RELEASE(content);
 | |
|     frame->GetNextSibling(&frame);
 | |
|   }
 | |
|   return PR_TRUE;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::MarkEmptyLines(nsIPresContext& aPresContext)
 | |
| {
 | |
|   // PRE-formatted content considers whitespace significant
 | |
|   const nsStyleText* text;
 | |
|   GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) text);
 | |
|   if (NS_STYLE_WHITESPACE_PRE == text->mWhiteSpace) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   PRBool afterBlock = PR_TRUE;
 | |
|   nsLineBox* line = mLines;
 | |
|   while (nsnull != line) {
 | |
|     if (line->IsBlock()) {
 | |
|       afterBlock = PR_TRUE;
 | |
|     }
 | |
|     else if (afterBlock) {
 | |
|       afterBlock = PR_FALSE;
 | |
| 
 | |
|       // This is an inline line and it is immediately after a block
 | |
|       // (or its our first line). See if it contains nothing but
 | |
|       // collapsible text.
 | |
|       PRBool isEmpty = IsEmptyLine(aPresContext, line);
 | |
|       line->SetIsEmptyLine(isEmpty);
 | |
|     }
 | |
|     else {
 | |
|       line->SetIsEmptyLine(PR_FALSE);
 | |
|     }
 | |
|     line = line->mNext;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::DeleteChildsNextInFlow(nsIPresContext& aPresContext,
 | |
|                                      nsIFrame* aChild)
 | |
| {
 | |
|   NS_PRECONDITION(IsChild(aChild), "bad geometric parent");
 | |
| 
 | |
|   nsIFrame* nextInFlow;
 | |
|   nsBlockFrame* parent;
 | |
|    
 | |
|   aChild->GetNextInFlow(nextInFlow);
 | |
|   NS_PRECONDITION(nsnull != nextInFlow, "null next-in-flow");
 | |
|   nextInFlow->GetParent((nsIFrame**)&parent);
 | |
| 
 | |
|   // If the next-in-flow has a next-in-flow then delete it, too (and
 | |
|   // delete it first).
 | |
|   nsIFrame* nextNextInFlow;
 | |
|   nextInFlow->GetNextInFlow(nextNextInFlow);
 | |
|   if (nsnull != nextNextInFlow) {
 | |
|     parent->DeleteChildsNextInFlow(aPresContext, nextInFlow);
 | |
|   }
 | |
| 
 | |
|   // Disconnect the next-in-flow from the flow list
 | |
|   nextInFlow->BreakFromPrevFlow();
 | |
| 
 | |
|   // Remove nextInFlow from the parents line list. Also remove it from
 | |
|   // the sibling list.
 | |
|   if (RemoveChild(parent->mLines, nextInFlow)) {
 | |
|     NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
 | |
|        ("nsBlockFrame::DeleteNextInFlowsFor: frame=%p (from mLines)",
 | |
|         nextInFlow));
 | |
|     goto done;
 | |
|   }
 | |
| 
 | |
|   // If we get here then we didn't find the child on the line list. If
 | |
|   // it's not there then it has to be on the overflow lines list.
 | |
|   if (nsnull != mOverflowLines) {
 | |
|     if (RemoveChild(parent->mOverflowLines, nextInFlow)) {
 | |
|       NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
 | |
|          ("nsBlockFrame::DeleteNextInFlowsFor: frame=%p (from overflow)",
 | |
|           nextInFlow));
 | |
|       goto done;
 | |
|     }
 | |
|   }
 | |
|   NS_NOTREACHED("can't find next-in-flow in overflow list");
 | |
| 
 | |
|  done:;
 | |
|   // If the parent is us then we will finish reflowing and update the
 | |
|   // content offsets of our parents when we are a pseudo-frame; if the
 | |
|   // parent is not us then it's a next-in-flow which means it will get
 | |
|   // reflowed by our parent and fix its content offsets. So there.
 | |
| 
 | |
|   // Delete the next-in-flow frame and adjust its parents child count
 | |
|   nextInFlow->DeleteFrame(aPresContext);
 | |
| 
 | |
| #ifdef NS_DEBUG
 | |
|   aChild->GetNextInFlow(nextInFlow);
 | |
|   NS_POSTCONDITION(nsnull == nextInFlow, "non null next-in-flow");
 | |
| #endif
 | |
| }
 | |
| 
 | |
| PRBool
 | |
| nsBlockFrame::RemoveChild(nsLineBox* aLines, nsIFrame* aChild)
 | |
| {
 | |
|   nsLineBox* line = aLines;
 | |
|   nsIFrame* prevChild = nsnull;
 | |
|   while (nsnull != line) {
 | |
|     nsIFrame* child = line->mFirstChild;
 | |
|     PRInt32 n = line->ChildCount();
 | |
|     while (--n >= 0) {
 | |
|       nsIFrame* nextChild;
 | |
|       child->GetNextSibling(&nextChild);
 | |
|       if (child == aChild) {
 | |
|         NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
 | |
|            ("nsBlockFrame::RemoveChild: line=%p frame=%p",
 | |
|             line, aChild));
 | |
|         // Continuations HAVE to be at the start of a line
 | |
|         NS_ASSERTION(child == line->mFirstChild, "bad continuation");
 | |
|         line->mFirstChild = nextChild;
 | |
|         if (0 == --line->mChildCount) {
 | |
|           line->mFirstChild = nsnull;
 | |
|         }
 | |
|         if (nsnull != prevChild) {
 | |
|           // When nextInFlow and it's continuation are in the same
 | |
|           // container then we remove the nextInFlow from the sibling
 | |
|           // list.
 | |
|           prevChild->SetNextSibling(nextChild);
 | |
|         }
 | |
|         return PR_TRUE;
 | |
|       }
 | |
|       prevChild = child;
 | |
|       child = nextChild;
 | |
|     }
 | |
|     line = line->mNext;
 | |
|   }
 | |
|   return PR_FALSE;
 | |
| }
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////
 | |
| // Floater support
 | |
| 
 | |
| void
 | |
| nsBlockFrame::ReflowFloater(nsIPresContext& aPresContext,
 | |
|                             nsBlockReflowState& aState,
 | |
|                             nsIFrame* aFloaterFrame,
 | |
|                             nsHTMLReflowState& aFloaterReflowState)
 | |
| {
 | |
|   // If either dimension is constrained then get the border and
 | |
|   // padding values in advance.
 | |
|   nsMargin bp(0, 0, 0, 0);
 | |
|   if (aFloaterReflowState.HaveFixedContentWidth() ||
 | |
|       aFloaterReflowState.HaveFixedContentHeight()) {
 | |
|     nsHTMLReflowState::ComputeBorderPaddingFor(aFloaterFrame, &aState, bp);
 | |
|   }
 | |
| 
 | |
|   // Compute the available width for the floater
 | |
|   if (aFloaterReflowState.HaveFixedContentWidth()) {
 | |
|     // When the floater has a contrained width, give it just enough
 | |
|     // space for its styled width plus its borders and paddings.
 | |
|     aFloaterReflowState.availableWidth = aFloaterReflowState.computedWidth + bp.left + bp.right;
 | |
|   }
 | |
|   else {
 | |
|     // CSS2 section 10.3.5: Floating non-replaced elements with an
 | |
|     // auto width have the computed value of zero. Therefore, don't
 | |
|     // bother reflowing them.
 | |
|     if (!NS_FRAME_IS_REPLACED(aFloaterReflowState.frameType)) {
 | |
|       // XXX Tables are weird and special, so check for them here...
 | |
|       const nsStyleDisplay* floaterDisplay;
 | |
|       aFloaterFrame->GetStyleData(eStyleStruct_Display,
 | |
|                                   (const nsStyleStruct*&)floaterDisplay);
 | |
|       if (NS_STYLE_DISPLAY_TABLE != floaterDisplay->mDisplay) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
|     aFloaterReflowState.availableWidth = NS_UNCONSTRAINEDSIZE;
 | |
|   }
 | |
| 
 | |
|   // Compute the available height for the floater
 | |
|   if (aFloaterReflowState.HaveFixedContentHeight()) {
 | |
|     aFloaterReflowState.availableHeight = aFloaterReflowState.computedHeight + bp.top + bp.bottom;
 | |
|   }
 | |
|   else {
 | |
|     aFloaterReflowState.availableHeight = NS_UNCONSTRAINEDSIZE;
 | |
|   }
 | |
| 
 | |
|   // Resize reflow the anchored item into the available space
 | |
|   nsIHTMLReflow*  floaterReflow;
 | |
|   if (NS_OK == aFloaterFrame->QueryInterface(kIHTMLReflowIID,
 | |
|                                              (void**)&floaterReflow)) {
 | |
|     nsHTMLReflowMetrics desiredSize(nsnull);
 | |
|     nsReflowStatus status;
 | |
|     floaterReflow->WillReflow(aPresContext);
 | |
|     floaterReflow->Reflow(aPresContext, desiredSize, aFloaterReflowState,
 | |
|                           status);
 | |
|     aFloaterFrame->SizeTo(desiredSize.width, desiredSize.height);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
 | |
| {
 | |
|   // Set the geometric parent of the floater
 | |
|   nsIFrame* floater = aPlaceholder->GetAnchoredItem();
 | |
|   floater->SetParent(mBlock);
 | |
| 
 | |
|   // Then add the floater to the current line and place it when
 | |
|   // appropriate
 | |
|   AddFloater(aPlaceholder, PR_TRUE);
 | |
| }
 | |
| 
 | |
| // This is called by the line layout's AddFloater method when a
 | |
| // place-holder frame is reflowed in a line. If the floater is a
 | |
| // left-most child (it's x coordinate is at the line's left margin)
 | |
| // then the floater is place immediately, otherwise the floater
 | |
| // placement is deferred until the line has been reflowed.
 | |
| void
 | |
| nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
 | |
|                                PRBool aInitialReflow)
 | |
| {
 | |
|   // Update the current line's floater array
 | |
|   NS_ASSERTION(nsnull != mCurrentLine, "null ptr");
 | |
|   if (nsnull == mCurrentLine->mFloaters) {
 | |
|     mCurrentLine->mFloaters = new nsVoidArray();
 | |
|   }
 | |
|   mCurrentLine->mFloaters->AppendElement(aPlaceholder);
 | |
| 
 | |
|   // Reflow the floater
 | |
|   nsIFrame* floater = aPlaceholder->GetAnchoredItem();
 | |
|   nsSize kidAvailSize(0, 0);
 | |
|   nsHTMLReflowState reflowState(mPresContext, floater, *this, kidAvailSize);
 | |
|   reflowState.lineLayout = nsnull;
 | |
|   if ((nsnull == reflowCommand) || (floater != mNextRCFrame)) {
 | |
|     // Stub out reflowCommand and repair reason in the reflowState
 | |
|     // when incremental reflow doesn't apply to the floater.
 | |
|     reflowState.reflowCommand = nsnull;
 | |
|     reflowState.reason = ((reason == eReflowReason_Initial) || aInitialReflow)
 | |
|       ? eReflowReason_Initial
 | |
|       : eReflowReason_Resize;
 | |
|   }
 | |
|   mBlock->ReflowFloater(mPresContext, *this, floater, reflowState);
 | |
| 
 | |
|   // Now place the floater immediately if possible. Otherwise stash it
 | |
|   // away in mPendingFloaters and place it later.
 | |
|   if (0 == mLineLayout->GetPlacedFrames()) {
 | |
|     NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
 | |
|        ("nsBlockReflowState::AddFloater: IsLeftMostChild, placeHolder=%p",
 | |
|         aPlaceholder));
 | |
| 
 | |
|     // Flush out pending bottom margin before placing floater
 | |
|     if (0 != mPrevBottomMargin) {
 | |
|       mY += mPrevBottomMargin;
 | |
|       mPrevBottomMargin = 0;
 | |
|     }
 | |
| 
 | |
|     // Because we are in the middle of reflowing a placeholder frame
 | |
|     // within a line (and possibly nested in an inline frame or two
 | |
|     // that's a child of our block) we need to restore the space
 | |
|     // manager's translation to the space that the block resides in
 | |
|     // before placing the floater.
 | |
|     PRBool isLeftFloater;
 | |
|     nscoord ox, oy;
 | |
|     mSpaceManager->GetTranslation(ox, oy);
 | |
|     nscoord dx = ox - mSpaceManagerX;
 | |
|     nscoord dy = oy - mSpaceManagerY;
 | |
|     mSpaceManager->Translate(-dx, -dy);
 | |
|     PlaceFloater(aPlaceholder, isLeftFloater);
 | |
| 
 | |
|     // Pass on updated available space to the current inline reflow engine
 | |
|     GetAvailableSpace();
 | |
|     mLineLayout->UpdateInlines(mAvailSpaceRect.x + mBorderPadding.left,
 | |
|                                mY,
 | |
|                                mAvailSpaceRect.width,
 | |
|                                mAvailSpaceRect.height,
 | |
|                                isLeftFloater);
 | |
| 
 | |
|     // Restore coordinate system
 | |
|     mSpaceManager->Translate(dx, dy);
 | |
|   }
 | |
|   else {
 | |
|     // This floater will be placed after the line is done (it is a
 | |
|     // below current line floater).
 | |
|     NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
 | |
|        ("nsBlockReflowState::AddFloater: pending, placeHolder=%p",
 | |
|         aPlaceholder));
 | |
|     mPendingFloaters.AppendElement(aPlaceholder);
 | |
|   }
 | |
| }
 | |
| 
 | |
| PRBool
 | |
| nsBlockReflowState::IsLeftMostChild(nsIFrame* aFrame)
 | |
| {
 | |
|   for (;;) {
 | |
|     nsIFrame* parent;
 | |
|     aFrame->GetParent(&parent);
 | |
|     if (parent == mBlock) {
 | |
|       nsIFrame* child = mCurrentLine->mFirstChild;
 | |
|       PRInt32 n = mCurrentLine->ChildCount();
 | |
|       while ((nsnull != child) && (aFrame != child) && (--n >= 0)) {
 | |
|         nsSize  size;
 | |
| 
 | |
|         // Is the child zero-sized?
 | |
|         child->GetSize(size);
 | |
|         if (size.width > 0) {
 | |
|           // We found a non-zero sized child frame that precedes aFrame
 | |
|           return PR_FALSE;
 | |
|         }
 | |
|         child->GetNextSibling(&child);
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     else {
 | |
|       // See if there are any non-zero sized child frames that precede
 | |
|       // aFrame in the child list
 | |
|       nsIFrame* child;
 | |
|       parent->FirstChild(nsnull, &child);
 | |
|       while ((nsnull != child) && (aFrame != child)) {
 | |
|         nsSize  size;
 | |
| 
 | |
|         // Is the child zero-sized?
 | |
|         child->GetSize(size);
 | |
|         if (size.width > 0) {
 | |
|           // We found a non-zero sized child frame that precedes aFrame
 | |
|           return PR_FALSE;
 | |
|         }
 | |
|         child->GetNextSibling(&child);
 | |
|       }
 | |
|     }
 | |
|   
 | |
|     // aFrame is the left-most non-zero sized frame in its geometric parent.
 | |
|     // Walk up one level and check that its parent is left-most as well
 | |
|     aFrame = parent;
 | |
|   }
 | |
|   return PR_TRUE;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockReflowState::PlaceFloater(nsPlaceholderFrame* aPlaceholder,
 | |
|                                  PRBool& aIsLeftFloater)
 | |
| {
 | |
|   // Save away the Y coordinate before placing the floater. We will
 | |
|   // restore mY at the end after placing the floater. This is
 | |
|   // necessary because any adjustments to mY during the floater
 | |
|   // placement are for the floater only, not for any non-floating
 | |
|   // content.
 | |
|   nscoord saveY = mY;
 | |
|   nsIFrame* floater = aPlaceholder->GetAnchoredItem();
 | |
| 
 | |
|   // Get the type of floater
 | |
|   const nsStyleDisplay* floaterDisplay;
 | |
|   const nsStyleSpacing* floaterSpacing;
 | |
|   floater->GetStyleData(eStyleStruct_Display,
 | |
|                         (const nsStyleStruct*&)floaterDisplay);
 | |
|   floater->GetStyleData(eStyleStruct_Spacing,
 | |
|                         (const nsStyleStruct*&)floaterSpacing);
 | |
| 
 | |
|   // See if the floater should clear any preceeding floaters...
 | |
|   if (NS_STYLE_CLEAR_NONE != floaterDisplay->mBreakType) {
 | |
|     ClearFloaters(mY, floaterDisplay->mBreakType);
 | |
|   }
 | |
|   else {
 | |
|     // Get the band of available space
 | |
|     GetAvailableSpace();
 | |
|   }
 | |
| 
 | |
|   // Get the floaters bounding box and margin information
 | |
|   nsRect region;
 | |
|   floater->GetRect(region);
 | |
|   nsMargin floaterMargin;
 | |
|   ComputeMarginFor(floater, this, floaterMargin);
 | |
| 
 | |
|   // Adjust the floater size by its margin. That's the area that will
 | |
|   // impact the space manager.
 | |
|   region.width += floaterMargin.left + floaterMargin.right;
 | |
|   region.height += floaterMargin.top + floaterMargin.bottom;
 | |
| 
 | |
|   // Find a place to place the floater. The CSS2 spec doesn't want
 | |
|   // floaters overlapping each other or sticking out of the containing
 | |
|   // block (CSS2 spec section 9.5.1, see the rule list).
 | |
|   NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) ||
 | |
|                (NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats),
 | |
|                "invalid float type");
 | |
| 
 | |
|   // While there is not enough room for the floater, clear past
 | |
|   // other floaters until there is room (or the band is not impacted
 | |
|   // by a floater).
 | |
|   while ((mAvailSpaceRect.width < region.width) &&
 | |
|          (mAvailSpaceRect.width < mContentArea.width)) {
 | |
|     // The CSS2 spec says that floaters should be placed as high as
 | |
|     // possible. We accomodate this easily by noting that if the band
 | |
|     // is not the full width of the content area then it must have
 | |
|     // been impacted by a floater. And we know that the height of the
 | |
|     // band will be the height of the shortest floater, therefore we
 | |
|     // adjust mY by that distance and keep trying until we have enough
 | |
|     // space for this floater.
 | |
| #ifdef NOISY_FLOATER_CLEARING
 | |
|     mBlock->ListTag(stdout);
 | |
|     printf(": clearing floater during floater placement: ");
 | |
|     printf("availWidth=%d regionWidth=%d,%d(w/o margins) contentWidth=%d\n",
 | |
|            mAvailSpaceRect.width, region.width,
 | |
|            region.width - floaterMargin.left - floaterMargin.right,
 | |
|            mContentArea.width);
 | |
| #endif
 | |
|     mY += mAvailSpaceRect.height;
 | |
|     GetAvailableSpace();
 | |
|   }
 | |
| 
 | |
|   // Assign an x and y coordinate to the floater. Note that the x,y
 | |
|   // coordinates are computed <b>relative to the translation in the
 | |
|   // spacemanager</b> which means that the impacted region will be
 | |
|   // <b>inside</b> the border/padding area.
 | |
|   if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
 | |
|     aIsLeftFloater = PR_TRUE;
 | |
|     region.x = mAvailSpaceRect.x;
 | |
|   }
 | |
|   else {
 | |
|     aIsLeftFloater = PR_FALSE;
 | |
|     region.x = mAvailSpaceRect.XMost() - region.width;
 | |
| 
 | |
|     // In case the floater is too big, don't go past the left edge
 | |
|     if (region.x < mAvailSpaceRect.x) {
 | |
|       region.x = mAvailSpaceRect.x;
 | |
|     }
 | |
|   }
 | |
|   region.y = mY - mBorderPadding.top;
 | |
|   if (region.y < 0) {
 | |
|     // CSS2 spec, 9.5.1 rule [4]: A floating box's outer top may not
 | |
|     // be higher than the top of its containing block.
 | |
| 
 | |
|     // XXX It's not clear if it means the higher than the outer edge
 | |
|     // or the border edge or the inner edge?
 | |
|     region.y = 0;
 | |
|   }
 | |
| 
 | |
|   // Place the floater in the space manager
 | |
|   mSpaceManager->AddRectRegion(floater, region);
 | |
| 
 | |
|   // Set the origin of the floater frame, in frame coordinates. These
 | |
|   // coordinates are <b>not</b> relative to the spacemanager
 | |
|   // translation, therefore we have to factor in our border/padding.
 | |
|   floater->MoveTo(mBorderPadding.left + floaterMargin.left + region.x,
 | |
|                   mBorderPadding.top + floaterMargin.top + region.y);
 | |
| 
 | |
|   // Now restore mY
 | |
|   mY = saveY;
 | |
| 
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
|   if (reason == eReflowReason_Incremental) {
 | |
|     nsRect r;
 | |
|     floater->GetRect(r);
 | |
|     nsFrame::IndentBy(stdout, gNoiseIndent);
 | |
|     printf("placed floater: ");
 | |
|     ((nsFrame*)floater)->ListTag(stdout);
 | |
|     printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height);
 | |
|   }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Place below-current-line floaters.
 | |
|  */
 | |
| void
 | |
| nsBlockReflowState::PlaceBelowCurrentLineFloaters(nsVoidArray* aFloaters)
 | |
| {
 | |
|   NS_PRECONDITION(aFloaters->Count() > 0, "no floaters");
 | |
| 
 | |
|   PRInt32 numFloaters = aFloaters->Count();
 | |
|   for (PRInt32 i = 0; i < numFloaters; i++) {
 | |
|     nsPlaceholderFrame* placeholderFrame = (nsPlaceholderFrame*)
 | |
|       aFloaters->ElementAt(i);
 | |
|     if (!IsLeftMostChild(placeholderFrame)) {
 | |
|       PRBool isLeftFloater;
 | |
|       PlaceFloater(placeholderFrame, isLeftFloater);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Place current-line floaters.
 | |
|  */
 | |
| void
 | |
| nsBlockReflowState::PlaceCurrentLineFloaters(nsVoidArray* aFloaters)
 | |
| {
 | |
|   NS_PRECONDITION(aFloaters->Count() > 0, "no floaters");
 | |
| 
 | |
|   PRInt32 numFloaters = aFloaters->Count();
 | |
|   for (PRInt32 i = 0; i < numFloaters; i++) {
 | |
|     nsPlaceholderFrame* placeholderFrame = (nsPlaceholderFrame*)
 | |
|       aFloaters->ElementAt(i);
 | |
|     if (IsLeftMostChild(placeholderFrame)) {
 | |
|       PRBool isLeftFloater;
 | |
|       PlaceFloater(placeholderFrame, isLeftFloater);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockReflowState::ClearFloaters(nscoord aY, PRUint8 aBreakType)
 | |
| {
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
|   if (reason == eReflowReason_Incremental) {
 | |
|     nsFrame::IndentBy(stdout, gNoiseIndent);
 | |
|     printf("clear floaters: in: mY=%d aY=%d(%d)\n",
 | |
|            mY, aY, aY - mBorderPadding.top);
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   nscoord newY = mCurrentBand.ClearFloaters(aY - mBorderPadding.top,
 | |
|                                             aBreakType);
 | |
|   mY = newY + mBorderPadding.top;
 | |
|   GetAvailableSpace();
 | |
| 
 | |
| #ifdef NOISY_INCREMENTAL_REFLOW
 | |
|   if (reason == eReflowReason_Incremental) {
 | |
|     nsFrame::IndentBy(stdout, gNoiseIndent);
 | |
|     printf("clear floaters: out: mY=%d(%d)\n",
 | |
|            mY, mY - mBorderPadding.top);
 | |
|   }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////////
 | |
| // Painting, event handling
 | |
| 
 | |
| PRIntn
 | |
| nsBlockFrame::GetSkipSides() const
 | |
| {
 | |
|   PRIntn skip = 0;
 | |
|   if (nsnull != mPrevInFlow) {
 | |
|     skip |= 1 << NS_SIDE_TOP;
 | |
|   }
 | |
|   if (nsnull != mNextInFlow) {
 | |
|     skip |= 1 << NS_SIDE_BOTTOM;
 | |
|   }
 | |
|   return skip;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::Paint(nsIPresContext&      aPresContext,
 | |
|                      nsIRenderingContext& aRenderingContext,
 | |
|                      const nsRect&        aDirtyRect,
 | |
|                      nsFramePaintLayer    aWhichLayer)
 | |
| {
 | |
|   const nsStyleDisplay* disp = (const nsStyleDisplay*)
 | |
|     mStyleContext->GetStyleData(eStyleStruct_Display);
 | |
| 
 | |
|   // Only paint the border and background if we're visible
 | |
|   if (disp->mVisible && (eFramePaintLayer_Underlay == aWhichLayer)) {
 | |
|     PRIntn skipSides = GetSkipSides();
 | |
|     const nsStyleColor* color = (const nsStyleColor*)
 | |
|       mStyleContext->GetStyleData(eStyleStruct_Color);
 | |
|     const nsStyleSpacing* spacing = (const nsStyleSpacing*)
 | |
|       mStyleContext->GetStyleData(eStyleStruct_Spacing);
 | |
| 
 | |
|     // Paint background and border
 | |
|     nsRect rect(0, 0, mRect.width, mRect.height);
 | |
|     nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this,
 | |
|                                     aDirtyRect, rect, *color, *spacing, 0, 0);
 | |
|     nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
 | |
|                                 aDirtyRect, rect, *spacing, mStyleContext,
 | |
|                                 skipSides);
 | |
|   }
 | |
| 
 | |
|   // If overflow is hidden then set the clip rect so that children
 | |
|   // don't leak out of us
 | |
|   if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
 | |
|     PRBool clipState;
 | |
|     aRenderingContext.PushState();
 | |
|     aRenderingContext.SetClipRect(nsRect(0, 0, mRect.width, mRect.height),
 | |
|                                   nsClipCombine_kIntersect, clipState);
 | |
|   }
 | |
| 
 | |
|   // Child elements have the opportunity to override the visibility
 | |
|   // property and display even if the parent is hidden
 | |
|   PaintFloaters(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
 | |
|   PaintChildren(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
 | |
| 
 | |
|   if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
 | |
|     PRBool clipState;
 | |
|     aRenderingContext.PopState(clipState);
 | |
|   }
 | |
| 
 | |
|   if (eFramePaintLayer_Overlay == aWhichLayer) {
 | |
|     // XXX CSS2's outline handling goes here
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::PaintFloaters(nsIPresContext& aPresContext,
 | |
|                              nsIRenderingContext& aRenderingContext,
 | |
|                              const nsRect& aDirtyRect,
 | |
|                              nsFramePaintLayer aWhichLayer)
 | |
| {
 | |
|   for (nsLineBox* line = mLines; nsnull != line; line = line->mNext) {
 | |
|     nsVoidArray* floaters = line->mFloaters;
 | |
|     if (nsnull == floaters) {
 | |
|       continue;
 | |
|     }
 | |
|     PRInt32 i, n = floaters->Count();
 | |
|     for (i = 0; i < n; i++) {
 | |
|       nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
 | |
|       PaintChild(aPresContext, aRenderingContext, aDirtyRect,
 | |
|                  ph->GetAnchoredItem(), aWhichLayer);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::PaintChildren(nsIPresContext& aPresContext,
 | |
|                              nsIRenderingContext& aRenderingContext,
 | |
|                              const nsRect& aDirtyRect,
 | |
|                              nsFramePaintLayer aWhichLayer)
 | |
| {
 | |
|   for (nsLineBox* line = mLines; nsnull != line; line = line->mNext) {
 | |
|     // If the line has outside children or if the line intersects the
 | |
|     // dirty rect then paint the children in the line.
 | |
|     if (!((line->mCombinedArea.YMost() <= aDirtyRect.y) ||
 | |
|           (line->mCombinedArea.y >= aDirtyRect.YMost()))) {
 | |
|       nsIFrame* kid = line->mFirstChild;
 | |
|       PRInt32 n = line->ChildCount();
 | |
|       while (--n >= 0) {
 | |
|         PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid,
 | |
|                    aWhichLayer);
 | |
|         kid->GetNextSibling(&kid);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (eFramePaintLayer_Content == aWhichLayer) {
 | |
|     if (nsnull != mBullet) {
 | |
|       // Paint outside bullets manually
 | |
|       const nsStyleList* list = (const nsStyleList*)
 | |
|         mStyleContext->GetStyleData(eStyleStruct_List);
 | |
|       if (NS_STYLE_LIST_STYLE_POSITION_OUTSIDE == list->mListStylePosition) {
 | |
|         PaintChild(aPresContext, aRenderingContext, aDirtyRect, mBullet,
 | |
|                    aWhichLayer);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::GetFrameForPoint(const nsPoint& aPoint, nsIFrame** aFrame)
 | |
| {
 | |
|   nsresult rv = GetFrameForPointUsing(aPoint, nsnull, aFrame);
 | |
|   if (NS_OK == rv) {
 | |
|     return NS_OK;
 | |
|   }
 | |
|   if (nsnull != mBullet) {
 | |
|     rv = GetFrameForPointUsing(aPoint, nsLayoutAtoms::bulletList, aFrame);
 | |
|     if (NS_OK == rv) {
 | |
|       return NS_OK;
 | |
|     }
 | |
|   }
 | |
|   if (mFloaters.NotEmpty()) {
 | |
|     rv = GetFrameForPointUsing(aPoint, nsLayoutAtoms::floaterList, aFrame);
 | |
|     if (NS_OK == rv) {
 | |
|       return NS_OK;
 | |
|     }
 | |
|   }
 | |
|   *aFrame = this;
 | |
|   return NS_ERROR_FAILURE;
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////////
 | |
| // Debugging
 | |
| 
 | |
| #ifdef NS_DEBUG
 | |
| static PRBool
 | |
| InLineList(nsLineBox* aLines, nsIFrame* aFrame)
 | |
| {
 | |
|   while (nsnull != aLines) {
 | |
|     nsIFrame* frame = aLines->mFirstChild;
 | |
|     PRInt32 n = aLines->ChildCount();
 | |
|     while (--n >= 0) {
 | |
|       if (frame == aFrame) {
 | |
|         return PR_TRUE;
 | |
|       }
 | |
|       frame->GetNextSibling(&frame);
 | |
|     }
 | |
|     aLines = aLines->mNext;
 | |
|   }
 | |
|   return PR_FALSE;
 | |
| }
 | |
| 
 | |
| static PRBool
 | |
| InSiblingList(nsLineBox* aLine, nsIFrame* aFrame)
 | |
| {
 | |
|   if (nsnull != aLine) {
 | |
|     nsIFrame* frame = aLine->mFirstChild;
 | |
|     while (nsnull != frame) {
 | |
|       if (frame == aFrame) {
 | |
|         return PR_TRUE;
 | |
|       }
 | |
|       frame->GetNextSibling(&frame);
 | |
|     }
 | |
|   }
 | |
|   return PR_FALSE;
 | |
| }
 | |
| 
 | |
| PRBool
 | |
| nsBlockFrame::IsChild(nsIFrame* aFrame)
 | |
| {
 | |
|   nsIFrame* parent;
 | |
|   aFrame->GetParent(&parent);
 | |
|   if (parent != (nsIFrame*)this) {
 | |
|     return PR_FALSE;
 | |
|   }
 | |
|   if (InLineList(mLines, aFrame) && InSiblingList(mLines, aFrame)) {
 | |
|     return PR_TRUE;
 | |
|   }
 | |
|   if (InLineList(mOverflowLines, aFrame) &&
 | |
|       InSiblingList(mOverflowLines, aFrame)) {
 | |
|     return PR_TRUE;
 | |
|   }
 | |
|   return PR_FALSE;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::VerifyTree() const
 | |
| {
 | |
|   // XXX rewrite this
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::SetInitialChildList(nsIPresContext& aPresContext,
 | |
|                                   nsIAtom*        aListName,
 | |
|                                   nsIFrame*       aChildList)
 | |
| {
 | |
|   nsresult rv = AppendNewFrames(aPresContext, aChildList);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   // Create list bullet if this is a list-item. Note that this is done
 | |
|   // here so that RenumberLists will work (it needs the bullets to
 | |
|   // store the bullet numbers).
 | |
|   const nsStyleDisplay* styleDisplay;
 | |
|   GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) styleDisplay);
 | |
|   if ((nsnull == mPrevInFlow) &&
 | |
|       (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) &&
 | |
|       (nsnull == mBullet)) {
 | |
|     // Resolve style for the bullet frame
 | |
|     nsIStyleContext* kidSC;
 | |
|     kidSC = aPresContext.ResolvePseudoStyleContextFor(mContent, 
 | |
|                 nsHTMLAtoms::bulletPseudo, mStyleContext);
 | |
| 
 | |
|     // Create bullet frame
 | |
|     mBullet = new nsBulletFrame;
 | |
|     if (nsnull == mBullet) {
 | |
|       NS_RELEASE(kidSC);
 | |
|       return NS_ERROR_OUT_OF_MEMORY;
 | |
|     }
 | |
|     mBullet->Init(aPresContext, mContent, this, kidSC);
 | |
|     NS_RELEASE(kidSC);
 | |
| 
 | |
|     // If the list bullet frame should be positioned inside then add
 | |
|     // it to the flow now.
 | |
|     const nsStyleList* styleList;
 | |
|     GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList);
 | |
|     if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) {
 | |
|       InsertNewFrames(aPresContext, mBullet, nsnull);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Lookup up the two pseudo style contexts
 | |
|   if (nsnull == mPrevInFlow) {
 | |
|     mFirstLineStyle = aPresContext.
 | |
|       ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLinePseudo,
 | |
|                                  mStyleContext);
 | |
|     mFirstLetterStyle = aPresContext.
 | |
|       ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLetterPseudo,
 | |
|                                  (nsnull != mFirstLineStyle
 | |
|                                   ? mFirstLineStyle
 | |
|                                   : mStyleContext));
 | |
| #ifdef NOISY_FIRST_LETTER
 | |
|     if (nsnull != mFirstLetterStyle) {
 | |
|       printf("block(%d)@%p: first-letter style found\n",
 | |
|              ContentIndexInContainer(this), this);
 | |
|     }
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsBlockFrame::CreateContinuingFrame(nsIPresContext& aPresContext,
 | |
|                                     nsIFrame* aParent,
 | |
|                                     nsIStyleContext* aStyleContext,
 | |
|                                     nsIFrame*& aContinuingFrame)
 | |
| {
 | |
|   nsBlockFrame* cf = new nsBlockFrame;
 | |
|   if (nsnull == cf) {
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
|   cf->Init(aPresContext, mContent, aParent, aStyleContext);
 | |
|   cf->SetFlags(mFlags);
 | |
|   cf->AppendToFlow(this);
 | |
|   aContinuingFrame = cf;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::RenumberLists(nsBlockReflowState& aState)
 | |
| {
 | |
|   // Setup initial list ordinal value
 | |
|   PRInt32 ordinal = 1;
 | |
|   nsIHTMLContent* hc;
 | |
|   if (mContent && (NS_OK == mContent->QueryInterface(kIHTMLContentIID, (void**) &hc))) {
 | |
|     nsHTMLValue value;
 | |
|     if (NS_CONTENT_ATTR_HAS_VALUE ==
 | |
|         hc->GetHTMLAttribute(nsHTMLAtoms::start, value)) {
 | |
|       if (eHTMLUnit_Integer == value.GetUnit()) {
 | |
|         ordinal = value.GetIntValue();
 | |
|         if (ordinal <= 0) {
 | |
|           ordinal = 1;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     NS_RELEASE(hc);
 | |
|   }
 | |
|   aState.mNextListOrdinal = ordinal;
 | |
| 
 | |
|   // Get to first-in-flow
 | |
|   nsBlockFrame* block = this;
 | |
|   while (nsnull != block->mPrevInFlow) {
 | |
|     block = (nsBlockFrame*) block->mPrevInFlow;
 | |
|   }
 | |
| 
 | |
|   // For each flow-block...
 | |
|   while (nsnull != block) {
 | |
|     // For each frame in the flow-block...
 | |
|     nsIFrame* frame = block->mLines ? block->mLines->mFirstChild : nsnull;
 | |
|     while (nsnull != frame) {
 | |
|       // If the frame is a list-item and the frame implements our
 | |
|       // block frame API then get it's bullet and set the list item
 | |
|       // ordinal.
 | |
|       const nsStyleDisplay* display;
 | |
|       frame->GetStyleData(eStyleStruct_Display,
 | |
|                           (const nsStyleStruct*&) display);
 | |
|       if (NS_STYLE_DISPLAY_LIST_ITEM == display->mDisplay) {
 | |
|         // Make certain that the frame isa block-frame in case
 | |
|         // something foriegn has crept in.
 | |
|         nsBlockFrame* listItem;
 | |
|         if (NS_OK == frame->QueryInterface(kBlockFrameCID,
 | |
|                                            (void**) &listItem)) {
 | |
|           if (nsnull != listItem->mBullet) {
 | |
|             aState.mNextListOrdinal =
 | |
|               listItem->mBullet->SetListItemOrdinal(aState.mNextListOrdinal);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       frame->GetNextSibling(&frame);
 | |
|     }
 | |
|     block = (nsBlockFrame*) block->mNextInFlow;
 | |
|   }
 | |
| }
 | |
| 
 | |
| PRBool
 | |
| nsBlockFrame::ShouldPlaceBullet(nsLineBox* aLine)
 | |
| {
 | |
|   PRBool ok = PR_FALSE;
 | |
|   const nsStyleList* list;
 | |
|   GetStyleData(eStyleStruct_List, (const nsStyleStruct*&)list);
 | |
|   if (NS_STYLE_LIST_STYLE_POSITION_OUTSIDE == list->mListStylePosition) {
 | |
|     nsLineBox* line = mLines;
 | |
|     while (nsnull != line) {
 | |
|       if (line->mBounds.height > 0) {
 | |
|         if (aLine == line) {
 | |
|           ok = PR_TRUE;
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       if (aLine == line) {
 | |
|         break;
 | |
|       }
 | |
|       line = line->mNext;
 | |
|     }
 | |
|   }
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::PlaceBullet(nsBlockReflowState& aState,
 | |
|                           nscoord aMaxAscent,
 | |
|                           nscoord aTopMargin)
 | |
| {
 | |
|   // Reflow the bullet now
 | |
|   nsSize availSize;
 | |
|   availSize.width = NS_UNCONSTRAINEDSIZE;
 | |
|   availSize.height = NS_UNCONSTRAINEDSIZE;
 | |
|   nsHTMLReflowState reflowState(aState.mPresContext, mBullet, aState,
 | |
|                                 availSize, aState.mLineLayout);
 | |
|   nsHTMLReflowMetrics metrics(nsnull);
 | |
|   nsIHTMLReflow* htmlReflow;
 | |
|   nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
|     nsReflowStatus  status;
 | |
|     htmlReflow->WillReflow(aState.mPresContext);
 | |
|     htmlReflow->Reflow(aState.mPresContext, metrics, reflowState, status);
 | |
|     htmlReflow->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED);
 | |
|   }
 | |
| 
 | |
|   // Place the bullet now; use its right margin to distance it
 | |
|   // from the rest of the frames in the line
 | |
|   nsMargin margin;
 | |
|   nsHTMLReflowState::ComputeMarginFor(mBullet, &aState, margin);
 | |
|   nscoord x = aState.mBorderPadding.left - margin.right - metrics.width;
 | |
|   // XXX This calculation may be wrong, especially if
 | |
|   // vertical-alignment occurs on the line!
 | |
|   nscoord y = aState.mBorderPadding.top + aMaxAscent -
 | |
|     metrics.ascent + aTopMargin;
 | |
|   mBullet->SetRect(nsRect(x, y, metrics.width, metrics.height));
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::BuildFloaterList()
 | |
| {
 | |
|   nsIFrame* head = nsnull;
 | |
|   nsIFrame* current = nsnull;
 | |
|   nsLineBox* line = mLines;
 | |
|   while (nsnull != line) {
 | |
|     if (nsnull != line->mFloaters) {
 | |
|       nsVoidArray& array = *line->mFloaters;
 | |
|       PRInt32 i, n = array.Count();
 | |
|       for (i = 0; i < n; i++) {
 | |
|         nsPlaceholderFrame* ph = (nsPlaceholderFrame*) array[i];
 | |
|         nsIFrame* floater = ph->GetAnchoredItem();
 | |
|         if (nsnull == head) {
 | |
|           current = head = floater;
 | |
|         }
 | |
|         else {
 | |
|           current->SetNextSibling(floater);
 | |
|           current = floater;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     line = line->mNext;
 | |
|   }
 | |
| 
 | |
|   // Terminate end of floater list just in case a floater was removed
 | |
|   if (nsnull != current) {
 | |
|     current->SetNextSibling(nsnull);
 | |
|   }
 | |
|   mFloaters.SetFrames(head);
 | |
| }
 | |
| 
 | |
| // XXX keep the text-run data in the first-in-flow of the block
 | |
| nsresult
 | |
| nsBlockFrame::ComputeTextRuns(nsBlockReflowState& aState)
 | |
| {
 | |
|   // Destroy old run information first
 | |
|   nsTextRun::DeleteTextRuns(mTextRuns);
 | |
|   mTextRuns = nsnull;
 | |
|   aState.mLineLayout->ResetTextRuns();
 | |
| 
 | |
|   // Ask each child that implements nsIInlineReflow to find its text runs
 | |
|   nsLineLayout& ll = *aState.mLineLayout;
 | |
|   nsLineBox* line = mLines;
 | |
|   while (nsnull != line) {
 | |
|     if (!line->IsBlock()) {
 | |
|       nsIFrame* frame = line->mFirstChild;
 | |
|       PRInt32 n = line->ChildCount();
 | |
|       while (--n >= 0) {
 | |
|         nsIHTMLReflow* hr;
 | |
|         if (NS_OK == frame->QueryInterface(kIHTMLReflowIID, (void**)&hr)) {
 | |
|           nsresult rv = hr->FindTextRuns(ll);
 | |
|           if (NS_OK != rv) {
 | |
|             return rv;
 | |
|           }
 | |
|         }
 | |
|         else {
 | |
|           // A frame that doesn't implement nsIHTMLReflow isn't text
 | |
|           // therefore it will end an open text run.
 | |
|           ll.EndTextRun();
 | |
|         }
 | |
|         frame->GetNextSibling(&frame);
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       // A frame that doesn't implement nsIInlineReflow isn't text
 | |
|       // therefore it will end an open text run.
 | |
|       ll.EndTextRun();
 | |
|     }
 | |
|     line = line->mNext;
 | |
|   }
 | |
|   ll.EndTextRun();
 | |
| 
 | |
|   // Now take the text-runs away from the line layout engine.
 | |
|   mTextRuns = ll.TakeTextRuns();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsBlockFrame::TakeRunInFrames(nsBlockFrame* aRunInFrame)
 | |
| {
 | |
|   // Simply steal the run-in-frame's line list and make it our
 | |
|   // own. XXX Very similar to the logic in DrainOverflowLines...
 | |
|   nsLineBox* line = aRunInFrame->mLines;
 | |
| 
 | |
|   // Make all the frames on the mOverflowLines list mine
 | |
|   nsIFrame* lastFrame = nsnull;
 | |
|   nsIFrame* frame = line->mFirstChild;
 | |
|   while (nsnull != frame) {
 | |
|     frame->SetParent(this);
 | |
|     lastFrame = frame;
 | |
|     frame->GetNextSibling(&frame);
 | |
|   }
 | |
| 
 | |
|   // Join the line lists
 | |
|   if (nsnull == mLines) {
 | |
|     mLines = line;
 | |
|   }
 | |
|   else {
 | |
|     // Join the sibling lists together
 | |
|     lastFrame->SetNextSibling(mLines->mFirstChild);
 | |
| 
 | |
|     // Place overflow lines at the front of our line list
 | |
|     nsLineBox* lastLine = nsLineBox::LastLine(line);
 | |
|     lastLine->mNext = mLines;
 | |
|     mLines = line;
 | |
|   }
 | |
| 
 | |
|   aRunInFrame->mLines = nsnull;
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| 
 | |
| nsresult
 | |
| NS_NewAnonymousBlockFrame(nsIFrame*& aNewFrame)
 | |
| {
 | |
|   nsBlockFrame* it = new nsAnonymousBlockFrame;
 | |
|   if (nsnull == it) {
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
|   aNewFrame = it;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsAnonymousBlockFrame::nsAnonymousBlockFrame()
 | |
| {
 | |
| }
 | |
| 
 | |
| nsAnonymousBlockFrame::~nsAnonymousBlockFrame()
 | |
| {
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsAnonymousBlockFrame::AppendFrames(nsIPresContext& aPresContext,
 | |
|                                     nsIPresShell&   aPresShell,
 | |
|                                     nsIAtom*        aListName,
 | |
|                                     nsIFrame*       aFrameList)
 | |
| {
 | |
|   return mParent->AppendFrames(aPresContext, aPresShell, aListName,
 | |
|                                aFrameList);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsAnonymousBlockFrame::InsertFrames(nsIPresContext& aPresContext,
 | |
|                                     nsIPresShell&   aPresShell,
 | |
|                                     nsIAtom*        aListName,
 | |
|                                     nsIFrame*       aPrevFrame,
 | |
|                                     nsIFrame*       aFrameList)
 | |
| {
 | |
|   return mParent->InsertFrames(aPresContext, aPresShell, aListName,
 | |
|                                aPrevFrame, aFrameList);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsAnonymousBlockFrame::RemoveFrame(nsIPresContext& aPresContext,
 | |
|                                    nsIPresShell&   aPresShell,
 | |
|                                    nsIAtom*        aListName,
 | |
|                                    nsIFrame*       aOldFrame)
 | |
| {
 | |
|   return mParent->RemoveFrame(aPresContext, aPresShell, aListName,
 | |
|                               aOldFrame);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsAnonymousBlockFrame::AppendFrames2(nsIPresContext& aPresContext,
 | |
|                                      nsIPresShell&   aPresShell,
 | |
|                                      nsIAtom*        aListName,
 | |
|                                      nsIFrame*       aFrameList)
 | |
| {
 | |
|   return nsAnonymousBlockFrameSuper::AppendFrames(aPresContext, aPresShell,
 | |
|                                                   aListName, aFrameList);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsAnonymousBlockFrame::InsertFrames2(nsIPresContext& aPresContext,
 | |
|                                      nsIPresShell&   aPresShell,
 | |
|                                      nsIAtom*        aListName,
 | |
|                                      nsIFrame*       aPrevFrame,
 | |
|                                      nsIFrame*       aFrameList)
 | |
| {
 | |
|   return nsAnonymousBlockFrameSuper::InsertFrames(aPresContext, aPresShell,
 | |
|                                                   aListName, aPrevFrame,
 | |
|                                                   aFrameList);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsAnonymousBlockFrame::RemoveFrame2(nsIPresContext& aPresContext,
 | |
|                                     nsIPresShell&   aPresShell,
 | |
|                                     nsIAtom*        aListName,
 | |
|                                     nsIFrame*       aOldFrame)
 | |
| {
 | |
|   return nsAnonymousBlockFrameSuper::RemoveFrame(aPresContext, aPresShell,
 | |
|                                                  aListName, aOldFrame);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsAnonymousBlockFrame::RemoveFirstFrame()
 | |
| {
 | |
|   nsLineBox* line = mLines;
 | |
|   if (nsnull != line) {
 | |
|     nsIFrame* firstChild = line->mFirstChild;
 | |
| 
 | |
|     // If the line has floaters on it, see if the frame being removed
 | |
|     // is a placeholder frame. If it is, then remove it from the lines
 | |
|     // floater array and from the block frames floater child list.
 | |
|     if (nsnull != line->mFloaters) {
 | |
|       // XXX UNTESTED!
 | |
|       nsPlaceholderFrame* placeholderFrame;
 | |
|       nsVoidArray& floaters = *line->mFloaters;
 | |
|       PRInt32 i, n = floaters.Count();
 | |
|       for (i = 0; i < n; i++) {
 | |
|         placeholderFrame = (nsPlaceholderFrame*) floaters[i];
 | |
|         if (firstChild == placeholderFrame) {
 | |
|           // Remove placeholder from the line's floater array
 | |
|           floaters.RemoveElementAt(i);
 | |
|           if (0 == floaters.Count()) {
 | |
|             delete line->mFloaters;
 | |
|             line->mFloaters = nsnull;
 | |
|           }
 | |
| 
 | |
|           // Remove the floater from the block frames mFloaters list too
 | |
|           mFloaters.RemoveFrame(placeholderFrame->GetAnchoredItem());
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (1 == line->mChildCount) {
 | |
|       // Remove line when last frame goes away
 | |
|       mLines = line->mNext;
 | |
|       delete line;
 | |
|     }
 | |
|     else {
 | |
|       // Remove frame from line and mark the line dirty
 | |
|       --line->mChildCount;
 | |
|       line->MarkDirty();
 | |
|       firstChild->GetNextSibling(&line->mFirstChild);
 | |
|     }
 | |
| 
 | |
|     // Break linkage to next child after stolen frame
 | |
|     firstChild->SetNextSibling(nsnull);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsAnonymousBlockFrame::RemoveFramesFrom(nsIFrame* aFrame)
 | |
| {
 | |
|   nsLineBox* line = mLines;
 | |
|   if (nsnull != line) {
 | |
|     // Chop the child sibling list into two pieces
 | |
|     nsFrameList tmp(line->mFirstChild);
 | |
|     nsIFrame* prevSibling = tmp.GetPrevSiblingFor(aFrame);
 | |
|     if (nsnull != prevSibling) {
 | |
|       // Chop the sibling list into two pieces
 | |
|       prevSibling->SetNextSibling(nsnull);
 | |
| 
 | |
|       nsLineBox* prevLine = nsnull;
 | |
|       while (nsnull != line) {
 | |
|         nsIFrame* frame = line->mFirstChild;
 | |
|         PRInt32 i, n = line->mChildCount;
 | |
|         PRBool done = PR_FALSE;
 | |
|         for (i = 0; i < n; i++) {
 | |
|           if (frame == aFrame) {
 | |
|             // We just found the target frame (and the line its in and
 | |
|             // the previous line)
 | |
|             if (frame == line->mFirstChild) {
 | |
|               // No more children on this line, so let it get removed
 | |
|               prevLine->mNext = nsnull;
 | |
|             }
 | |
|             else {
 | |
|               // The only frames that remain on this line are the
 | |
|               // frames preceeding aFrame. Adjust the count to
 | |
|               // indicate that fact.
 | |
|               line->mChildCount = i;
 | |
| 
 | |
|               // Remove the lines that follow this line
 | |
|               prevLine = line;
 | |
|               line = line->mNext;
 | |
|               prevLine->mNext = nsnull;
 | |
|             }
 | |
|             done = PR_TRUE;
 | |
|             break;
 | |
|           }
 | |
|           frame->GetNextSibling(&frame);
 | |
|         }
 | |
|         if (done) {
 | |
|           break;
 | |
|         }
 | |
|         prevLine = line;
 | |
|         line = line->mNext;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Remove all of the remaining lines
 | |
|     while (nsnull != line) {
 | |
|       nsLineBox* next = line->mNext;
 | |
|       delete line;
 | |
|       line = next;
 | |
|     }
 | |
|   }
 | |
| }
 | 
