/* -*- 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 "nsCOMPtr.h" #include "nsBlockFrame.h" #include "nsBlockReflowContext.h" #include "nsBlockBandData.h" #include "nsBulletFrame.h" #include "nsLineBox.h" #include "nsInlineFrame.h" #include "nsLineLayout.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" // XXX for IsEmptyLine #include "nsTextFragment.h" // XXX HTML:P's that are empty yet have style indicating they should // clear floaters - we need to ignore the clear behavior. #ifdef NS_DEBUG #undef NOISY_FIRST_LINE #undef REALLY_NOISY_FIRST_LINE #undef NOISY_FIRST_LETTER #undef NOISY_MAX_ELEMENT_SIZE #undef NOISY_FLOATER_CLEARING #undef NOISY_INCREMENTAL_REFLOW #undef NOISY_FINAL_SIZE #undef NOISY_REMOVE_FRAME #undef NOISY_DAMAGE_REPAIR #undef NOISY_COMBINED_AREA #undef NOISY_VERTICAL_MARGINS #undef NOISY_REFLOW_REASON #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_FLOATER_CLEARING #undef NOISY_INCREMENTAL_REFLOW #undef NOISY_FINAL_SIZE #undef NOISY_REMOVE_FRAME #undef NOISY_DAMAGE_REPAIR #undef NOISY_COMBINED_AREA #undef NOISY_VERTICAL_MARGINS #undef NOISY_REFLOW_REASON #undef REFLOW_STATUS_COVERAGE #endif //---------------------------------------------------------------------- // Debugging support code #ifdef DEBUG static PRBool gShowDirtyLines = PR_FALSE; #endif #ifdef NOISY_INCREMENTAL_REFLOW static PRInt32 gNoiseIndent; static const char* kReflowCommandType[] = { "FrameAppended", "FrameInserted", "FrameRemoved", "ContentChanged", "StyleChanged", "PullupReflow", "PushReflow", "CheckPullupReflow", "ReflowDirty", "UserDefined", }; #endif #ifdef REALLY_NOISY_FIRST_LINE static void DumpStyleGeneaology(nsIFrame* aFrame, const char* gap) { fputs(gap, stdout); nsFrame::ListTag(stdout, aFrame); 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 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 //---------------------------------------------------------------------- inline void CombineRects(const nsRect& r1, nsRect& r2) { nscoord xa = r2.x; nscoord ya = r2.y; nscoord xb = xa + r2.width; nscoord yb = ya + r2.height; nscoord x = r1.x; nscoord y = r1.y; nscoord xmost = x + r1.width; nscoord ymost = y + r1.height; if (x < xa) { xa = x; } if (xmost > xb) { xb = xmost; } if (y < ya) { ya = y; } if (ymost > yb) { yb = ymost; } r2.x = xa; r2.y = ya; r2.width = xb - xa; r2.height = yb - ya; } //---------------------------------------------------------------------- class nsBlockReflowState { public: nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, const nsHTMLReflowMetrics& aMetrics, nsLineLayout* aLineLayout); ~nsBlockReflowState(); /** * Update our state when aLine is skipped over during incremental * reflow. */ void RecoverStateFrom(nsLineBox* aLine, PRBool aPrevLineWasClean); /** * 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 GetAvailableSpace() { #ifdef DEBUG // Verify that the caller setup the coordinate system properly nscoord wx, wy; mSpaceManager->GetTranslation(wx, wy); NS_ASSERTION((wx == mSpaceManagerX) && (wy == mSpaceManagerY), "bad coord system"); #endif mBand.GetAvailableSpace(mY - BorderPadding().top, mAvailSpaceRect); #ifdef NOISY_INCREMENTAL_REFLOW if (mReflowState.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, mBand.GetTrapezoidCount()); } #endif } void InitFloater(nsPlaceholderFrame* aPlaceholderFrame); void AddFloater(nsPlaceholderFrame* aPlaceholderFrame, PRBool aInitialReflow); PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats); void PlaceFloater(nsPlaceholderFrame* aFloater, const nsMargin& aFloaterMargins, PRBool* aIsLeftFloater, nsPoint* aNewOrigin); void PlaceBelowCurrentLineFloaters(nsVoidArray* aFloaters, PRBool aReflowFloaters); void PlaceCurrentLineFloaters(nsVoidArray* aFloaters); void ClearFloaters(nscoord aY, PRUint8 aBreakType); PRBool ClearPastFloaters(PRUint8 aBreakType); PRBool IsLeftMostChild(nsIFrame* aFrame); PRBool IsAdjacentWithTop() const { return mY == mReflowState.mComputedBorderPadding.top; } const nsMargin& BorderPadding() const { return mReflowState.mComputedBorderPadding; } void UpdateMaxElementSize(const nsSize& aMaxElementSize) { #ifdef NOISY_MAX_ELEMENT_SIZE nsSize oldSize = mMaxElementSize; #endif if (aMaxElementSize.width > mMaxElementSize.width) { mMaxElementSize.width = aMaxElementSize.width; } if (aMaxElementSize.height > mMaxElementSize.height) { mMaxElementSize.height = aMaxElementSize.height; } #ifdef NOISY_MAX_ELEMENT_SIZE if ((mMaxElementSize.width != oldSize.width) || (mMaxElementSize.height != oldSize.height)) { nsFrame::IndentBy(stdout, GetDepth()); if (NS_UNCONSTRAINEDSIZE == mReflowState.availableWidth) { printf("PASS1 "); } nsFrame::ListTag(stdout, mBlock); printf(": old max-element-size=%d,%d new=%d,%d\n", oldSize.width, oldSize.height, mMaxElementSize.width, mMaxElementSize.height); } #endif } void RecoverVerticalMargins(nsLineBox* aLine, PRBool aApplyTopMargin, nscoord* aTopMarginResult, nscoord* aBottomMarginResult); void ComputeBlockAvailSpace(nsSplittableType aSplitType, nsRect& aResult); void RecoverStateFrom(nsLineBox* aLine, PRBool aApplyTopMargin, nscoord aDeltaY); //---------------------------------------- // This state is the "global" state computed once for the reflow of // the block. // The block frame that is using this object nsBlockFrame* mBlock; nsIPresContext* mPresContext; const nsHTMLReflowState& mReflowState; nsLineLayout* mLineLayout; nsISpaceManager* mSpaceManager; // The coordinates within the spacemanager where the block is being // placed after taking into account the blocks border and // padding. This, therefore, represents the inner "content area" (in // spacemanager coordinates) where child frames will be placed, // including child blocks and floaters. nscoord mSpaceManagerX, mSpaceManagerY; // XXX get rid of this nsReflowStatus mReflowStatus; nscoord mBottomEdge; PRBool mUnconstrainedWidth; PRBool mUnconstrainedHeight; // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE // if the container reflowing this frame has given the frame an // unconstrained area. nsSize mContentArea; // Our wrapping behavior PRBool mNoWrap; // Is this frame a root for top/bottom margin collapsing? PRBool mIsTopMarginRoot, mIsBottomMarginRoot; // See ShouldApplyTopMargin PRBool mApplyTopMargin; //---------------------------------------- // This state is "running" state updated by the reflow of each line // in the block. This same state is "recovered" when a line is not // dirty and is passed over during incremental reflow. // The current line being reflowed nsLineBox* mCurrentLine; // The previous line just reflowed nsLineBox* mPrevLine; // The current Y coordinate in the block nscoord mY; // The available space within the current band. nsRect mAvailSpaceRect; // The maximum x-most of each line nscoord mKidXMost; // The combined area of all floaters placed so far nsRect mFloaterCombinedArea; // Previous child. This is used when pulling up a frame to update // the sibling list. nsIFrame* mPrevChild; // The next immediate child frame that is the target of an // incremental reflow command. Once that child has been reflowed we // null this slot out. nsIFrame* mNextRCFrame; // The previous child frames collapsed bottom margin value. nscoord mPrevBottomMargin; // The current next-in-flow for the block. When lines are pulled // from a next-in-flow, this is used to know which next-in-flow to // pull from. When a next-in-flow is emptied of lines, we advance // this to the next next-in-flow. nsBlockFrame* mNextInFlow; // The current band data for the current Y coordinate nsBlockBandData mBand; //---------------------------------------- // Temporary line-reflow state. This state is used during the reflow // of a given line, but doesn't have meaning before or after. nsVoidArray mPendingFloaters; PRBool mComputeMaxElementSize; nsSize mMaxElementSize; }; // XXX This is vile. Make it go away void nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame) { mBlockRS->InitFloater(aFrame); } void nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) { mBlockRS->AddFloater(aFrame, PR_FALSE); } //---------------------------------------------------------------------- nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, const nsHTMLReflowMetrics& aMetrics, nsLineLayout* aLineLayout) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), mIsTopMarginRoot(PR_FALSE), mIsBottomMarginRoot(PR_FALSE), mApplyTopMargin(PR_FALSE), mNextRCFrame(nsnull), mPrevBottomMargin(0) { mLineLayout = aLineLayout; mSpaceManager = aReflowState.spaceManager; // Translate into our content area and then save the // coordinate system origin for later. const nsMargin& borderPadding = BorderPadding(); mSpaceManager->Translate(borderPadding.left, borderPadding.top); mSpaceManager->GetTranslation(mSpaceManagerX, mSpaceManagerY); mReflowStatus = NS_FRAME_COMPLETE; mPresContext = aPresContext; mBlock->GetNextInFlow((nsIFrame**)&mNextInFlow); mKidXMost = 0; // Compute content area width (the content area is inside the border // and padding) mUnconstrainedWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.computedWidth) { mContentArea.width = aReflowState.computedWidth; } else { if (NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth) { mContentArea.width = NS_UNCONSTRAINEDSIZE; mUnconstrainedWidth = PR_TRUE; } else { nscoord lr = borderPadding.left + borderPadding.right; mContentArea.width = aReflowState.availableWidth - lr; } } // Compute content area height. Unlike the width, if we have a // specified style height we ignore it since extra content is // managed by the "overflow" property. When we don't have a // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). mUnconstrainedHeight = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't // include either border or padding edge. mBottomEdge = aReflowState.availableHeight - borderPadding.bottom; mContentArea.height = mBottomEdge - borderPadding.top; } else { // When we are not in a paginated situation then we always use // an constrained height. mUnconstrainedHeight = PR_TRUE; mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; } mY = borderPadding.top; mBand.Init(mSpaceManager, mContentArea); mPrevChild = nsnull; mCurrentLine = nsnull; mPrevLine = nsnull; const nsStyleText* styleText; mBlock->GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) styleText); switch (styleText->mWhiteSpace) { case NS_STYLE_WHITESPACE_PRE: case NS_STYLE_WHITESPACE_NOWRAP: mNoWrap = PR_TRUE; break; default: mNoWrap = PR_FALSE; break; } mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;; mMaxElementSize.SizeTo(0, 0); if (0 != borderPadding.top) { mIsTopMarginRoot = PR_TRUE; } if (0 != borderPadding.bottom) { mIsBottomMarginRoot = PR_TRUE; } } nsBlockReflowState::~nsBlockReflowState() { // Restore the coordinate system const nsMargin& borderPadding = BorderPadding(); mSpaceManager->Translate(-borderPadding.left, -borderPadding.top); } // Compute the amount of available space for reflowing a block frame // at the current Y coordinate. This method assumes that // GetAvailableSpace has already been called. void nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType, nsRect& aResult) { nscoord availHeight = mUnconstrainedHeight ? NS_UNCONSTRAINEDSIZE : mBottomEdge - mY; const nsMargin& borderPadding = BorderPadding(); nscoord availX, availWidth; if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) { // Frames that know how to do non-rectangular splitting are given // the entire available space, including space consumed by // floaters. availX = borderPadding.left; availWidth = mUnconstrainedWidth ? NS_UNCONSTRAINEDSIZE : mContentArea.width; } else { // Assume the frame is clueless about the space manager and only // give it free space. availX = mAvailSpaceRect.x + borderPadding.left; availWidth = mAvailSpaceRect.width; } aResult.SetRect(availX, mY, availWidth, availHeight); } PRBool nsBlockReflowState::ClearPastFloaters(PRUint8 aBreakType) { nscoord saveY, deltaY; PRBool applyTopMargin = PR_FALSE; switch (aBreakType) { case NS_STYLE_CLEAR_LEFT: case NS_STYLE_CLEAR_RIGHT: case NS_STYLE_CLEAR_LEFT_AND_RIGHT: // Apply the previous margin before clearing saveY = mY + mPrevBottomMargin; ClearFloaters(saveY, aBreakType); // Determine how far we just moved. If we didn't move then there // was nothing to clear to don't mess with the normal margin // collapsing behavior. In either case we need to restore the Y // coordinate to what it was before the clear. deltaY = mY - saveY; if (0 != deltaY) { // Pretend that the distance we just moved is a previous // blocks bottom margin. Note that GetAvailableSpace has been // done so that the available space calculations will be done // after clearing the appropriate floaters. // // What we are doing here is applying CSS2 section 9.5.2's // rules for clearing - "The top margin of the generated box // is increased enough that the top border edge is below the // bottom outer edge of the floating boxes..." // // What this will do is cause the top-margin of the block // frame we are about to reflow to be collapsed with that // distance. mPrevBottomMargin = deltaY; mY = saveY; // Force margin to be applied in this circumstance applyTopMargin = PR_TRUE; } else { // Put aState.mY back to its original value since no clearing // happened. That way the previous blocks bottom margin will // be applied properly. mY = saveY - mPrevBottomMargin; } break; } return applyTopMargin; } // Recover the collapsed vertical margin values for aLine. Note that // the values are not collapsed with aState.mPrevBottomMargin, nor are // they collapsed with each other when the line height is zero. void nsBlockReflowState::RecoverVerticalMargins(nsLineBox* aLine, PRBool aApplyTopMargin, nscoord* aTopMarginResult, nscoord* aBottomMarginResult) { if (aLine->IsBlock()) { // Update band data GetAvailableSpace(); // Setup reflow state to compute the block childs top and bottom // margins nsIFrame* frame = aLine->mFirstChild; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; frame->IsSplittable(splitType); nsRect availSpaceRect; ComputeBlockAvailSpace(splitType, availSpaceRect); nsSize availSpace(availSpaceRect.width, availSpaceRect.height); nsHTMLReflowState reflowState(*mPresContext, mReflowState, frame, availSpace); // Compute collapsed top margin nscoord topMargin = 0; if (aApplyTopMargin) { topMargin = nsBlockReflowContext::ComputeCollapsedTopMargin(mPresContext, reflowState); } // Compute collapsed bottom margin nscoord bottomMargin = reflowState.computedMargin.bottom; bottomMargin = nsBlockReflowContext::MaxMargin(bottomMargin, aLine->mCarriedOutBottomMargin); *aTopMarginResult = topMargin; *aBottomMarginResult = bottomMargin; } else { // XXX_ib *aTopMarginResult = 0; *aBottomMarginResult = 0; } } void nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, PRBool aApplyTopMargin, nscoord aDeltaY) { // Make the line being recovered the current line mCurrentLine = aLine; // Update aState.mPrevChild as if we had reflowed all of the frames // in this line. mPrevChild = aLine->LastChild(); // Recover mKidXMost nscoord xmost = aLine->mBounds.XMost(); if (xmost > mKidXMost) { #ifdef DEBUG if (CRAZY_WIDTH(xmost)) { nsFrame::ListTag(stdout, mBlock); printf(": WARNING: xmost:%d\n", xmost); } #endif mKidXMost = xmost; } // The line may have clear before semantics. if (NS_STYLE_CLEAR_NONE != aLine->mBreakType) { // Clear past floaters before the block if the clear style is not none aApplyTopMargin = ClearPastFloaters(aLine->mBreakType); } // Recover mPrevBottomMargin and calculate the line's new Y // coordinate (newLineY) nscoord newLineY = mY; if (0 == aLine->mBounds.height) { if (aLine->IsBlock() && nsBlockReflowContext::IsHTMLParagraph(aLine->mFirstChild)) { // Empty HTML paragraphs disappear entirely - their margins go // to zero. } else { // The line's top and bottom margin values need to be collapsed // with the mPrevBottomMargin to determine a new // mPrevBottomMargin value. nscoord topMargin, bottomMargin; RecoverVerticalMargins(aLine, aApplyTopMargin, &topMargin, &bottomMargin); nscoord m = nsBlockReflowContext::MaxMargin(topMargin, bottomMargin); m = nsBlockReflowContext::MaxMargin(m, mPrevBottomMargin); mPrevBottomMargin = m; } } else { // Recover the top and bottom marings for this line nscoord topMargin, bottomMargin; RecoverVerticalMargins(aLine, aApplyTopMargin, &topMargin, &bottomMargin); // Compute the collapsed top margin value nscoord collapsedTopMargin = nsBlockReflowContext::MaxMargin(topMargin, mPrevBottomMargin); // The lineY is just below the collapsed top margin value. The // mPrevBottomMargin gets set to the bottom margin value for the // line. newLineY += collapsedTopMargin; mPrevBottomMargin = bottomMargin; } nscoord finalDeltaY = newLineY - aLine->mBounds.y; if (0 != finalDeltaY) { // Slide the frames in the line by the computed delta. This also // updates the lines Y coordinate and the combined area's Y // coordinate. mBlock->SlideLine(mPresContext, mSpaceManager, aLine, finalDeltaY); } // Now that the line's Y coordinate is updated, place floaters back // into the space manager if (nsnull != aLine->mFloaters) { mY = aLine->mBounds.y; if (0 == aLine->mBounds.height) { mY += mPrevBottomMargin; } PlaceCurrentLineFloaters(aLine->mFloaters); mY = aLine->mBounds.YMost(); PlaceBelowCurrentLineFloaters(aLine->mFloaters, PR_FALSE); } // XXX Recover floater combined area // XXX not necessary at the moment because // PlaceCurrentLineFloaters and PlaceBelowCurrentLineFloaters // always reflow the floaters... // Recover mY mY = aLine->mBounds.YMost(); // It's possible that the line has clear after semantics if (!aLine->IsBlock() && (NS_STYLE_CLEAR_NONE != aLine->mBreakType)) { switch (aLine->mBreakType) { case NS_STYLE_CLEAR_LEFT: case NS_STYLE_CLEAR_RIGHT: case NS_STYLE_CLEAR_LEFT_AND_RIGHT: ClearFloaters(mY, aLine->mBreakType); break; } } } //---------------------------------------------------------------------- const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID; nsresult NS_NewBlockFrame(nsIFrame** aNewFrame) { NS_PRECONDITION(aNewFrame, "null OUT ptr"); if (nsnull == aNewFrame) { return NS_ERROR_NULL_POINTER; } nsBlockFrame* it = new nsBlockFrame; if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } *aNewFrame = it; return NS_OK; } nsBlockFrame::nsBlockFrame() { } nsBlockFrame::~nsBlockFrame() { nsTextRun::DeleteTextRuns(mTextRuns); } NS_IMETHODIMP nsBlockFrame::DeleteFrame(nsIPresContext& aPresContext) { // Outside bullets are not in our child-list so check for them here // and delete them when present. if (HaveOutsideBullet()) { 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; } if (aIID.Equals(nsILineIterator::GetIID())) { nsLineIterator* it = new nsLineIterator; if (!it) { *aInstancePtr = nsnull; return NS_ERROR_OUT_OF_MEMORY; } const nsStyleDisplay* display; GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display); nsresult rv = it->Init(mLines, display->mDirection == NS_STYLE_DIRECTION_RTL); if (NS_FAILED(rv)) { delete it; return rv; } return it->QueryInterface(aIID, aInstancePtr); } return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr); } static nsresult ReResolveLineList(nsIPresContext* aPresContext, nsLineBox* aLine, nsIStyleContext* aStyleContext, PRInt32 aParentChange, nsStyleChangeList* aChangeList) { nsresult rv = NS_OK; PRInt32 childChange; while (nsnull != aLine) { nsIFrame* child = aLine->mFirstChild; PRInt32 n = aLine->mChildCount; while ((--n >= 0) && NS_SUCCEEDED(rv)) { rv = child->ReResolveStyleContext(aPresContext, aStyleContext, aParentChange, aChangeList, &childChange); child->GetNextSibling(&child); } aLine = aLine->mNext; } return rv; } NS_IMETHODIMP nsBlockFrame::ReResolveStyleContext(nsIPresContext* aPresContext, nsIStyleContext* aParentContext, PRInt32 aParentChange, nsStyleChangeList* aChangeList, PRInt32* aLocalChange) { // NOTE: using nsFrame's ReResolveStyleContext method to avoid // useless version in base classes. PRInt32 ourChange = aParentChange; nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext, ourChange, aChangeList, &ourChange); if (NS_FAILED(rv)) { return rv; } if (NS_COMFALSE != rv) { if (HaveOutsideBullet() && mBullet) { nsIStyleContext* newBulletSC; nsIStyleContext* oldBulletSC = nsnull; mBullet->GetStyleContext(&oldBulletSC); aPresContext->ResolvePseudoStyleContextFor(mContent, nsHTMLAtoms::mozListBulletPseudo, mStyleContext, PR_FALSE, &newBulletSC); rv = mBullet->SetStyleContext(aPresContext, newBulletSC); CaptureStyleChangeFor(this, oldBulletSC, newBulletSC, ourChange, aChangeList, &ourChange); NS_RELEASE(oldBulletSC); NS_RELEASE(newBulletSC); } if (aLocalChange) { *aLocalChange = ourChange; } // Update the child frames on each line rv = ReResolveLineList(aPresContext, mLines, mStyleContext, ourChange, aChangeList); // Update any overflow lines too if (NS_SUCCEEDED(rv) && (nsnull != mOverflowLines)) { rv = ReResolveLineList(aPresContext, mOverflowLines, mStyleContext, ourChange, aChangeList); } // Just in case, we update the prev-in-flow's overflow lines too if (NS_SUCCEEDED(rv) && (nsnull != mPrevInFlow)) { nsLineBox* lines = ((nsBlockFrame*)mPrevInFlow)->mOverflowLines; if (nsnull != lines) { rv = ReResolveLineList(aPresContext, lines, mStyleContext, ourChange, aChangeList); } } } 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 { IndentBy(out, aIndent); ListTag(out); nsIView* view; GetView(&view); if (nsnull != view) { fprintf(out, " [view=%p]", view); } if (nsnull != mNextSibling) { fprintf(out, " next=%p", mNextSibling); } // 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 fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); if (0 != mState) { fprintf(out, " [state=%08x]", mState); } if (0 != mFlags) { fprintf(out, " [flags=%x]", mFlags); } fprintf(out, " sc=%p<\n", mStyleContext); 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) { IndentBy(out, aIndent); fputs("text-runs <\n", out); ListTextRuns(out, aIndent + 1, mTextRuns); IndentBy(out, aIndent); fputs(">\n", out); } aIndent--; IndentBy(out, aIndent); fputs(">\n", out); return NS_OK; } NS_IMETHODIMP nsBlockFrame::GetFrameName(nsString& aResult) const { return MakeFrameName("Block", aResult); } NS_IMETHODIMP nsBlockFrame::GetFrameType(nsIAtom** aType) const { NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer"); *aType = nsLayoutAtoms::blockFrame; NS_ADDREF(*aType); return NS_OK; } ///////////////////////////////////////////////////////////////////////////// // 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) { if (HaveOutsideBullet()) { *aFirstChild = mBullet; } else { *aFirstChild = nsnull; } 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 NS_IMETHODIMP nsBlockFrame::Reflow(nsIPresContext& aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { nsLineLayout lineLayout(aPresContext, aReflowState.spaceManager, &aReflowState, nsnull != aMetrics.maxElementSize); nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics, &lineLayout); lineLayout.Init(&state); if (NS_BLOCK_MARGIN_ROOT & mFlags) { state.mIsTopMarginRoot = PR_TRUE; state.mIsBottomMarginRoot = PR_TRUE; } if (state.mIsTopMarginRoot) { state.mApplyTopMargin = PR_TRUE; } if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(); ComputeTextRuns(&aPresContext); } nsresult rv = NS_OK; nsIFrame* target; switch (aReflowState.reason) { case eReflowReason_Initial: #ifdef NOISY_REFLOW_REASON ListTag(stdout); printf(": reflow=initial\n"); #endif DrainOverflowLines(); rv = PrepareInitialReflow(state); mState &= ~NS_FRAME_FIRST_REFLOW; break; case eReflowReason_Incremental: aReflowState.reflowCommand->GetTarget(target); if (this == target) { nsIReflowCommand::ReflowType type; aReflowState.reflowCommand->GetType(type); #ifdef NOISY_REFLOW_REASON ListTag(stdout); printf(": reflow=incremental type=%d\n", type); #endif switch (type) { case nsIReflowCommand::FrameAppended: case nsIReflowCommand::FrameInserted: case nsIReflowCommand::FrameRemoved: NS_NOTREACHED("invalid reflow command"); 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 aReflowState.reflowCommand->GetNext(state.mNextRCFrame); #ifdef NOISY_REFLOW_REASON ListTag(stdout); printf(": reflow=incremental"); if (state.mNextRCFrame) { printf(" next="); nsFrame::ListTag(stdout, state.mNextRCFrame); } printf("\n"); #endif // Now do the reflow rv = PrepareChildIncrementalReflow(state); } break; case eReflowReason_Resize: default: #ifdef NOISY_REFLOW_REASON ListTag(stdout); printf(": reflow=resize (%d)\n", aReflowState.reason); #endif DrainOverflowLines(); rv = PrepareResizeReflow(state); break; } // Now reflow... rv = ReflowDirtyLines(state); aStatus = state.mReflowStatus; if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { if (NS_STYLE_OVERFLOW_HIDDEN == aReflowState.mStyleDisplay->mOverflow) { aStatus = NS_FRAME_COMPLETE; } else { #ifdef DEBUG_kipp ListTag(stdout); printf(": block is not complete\n"); #endif } } // XXX_pref get rid of this! BuildFloaterList(); // Compute our final size ComputeFinalSize(aReflowState, state, aMetrics); #ifdef DEBUG if (gShowDirtyLines && (eReflowReason_Resize == aReflowState.reason)) { nsLineBox* line = mLines; while (nsnull != line) { line->ClearWasDirty(); line = line->mNext; } } #endif #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": availSize=%d,%d computed=%d,%d metrics=%d,%d carriedMargin=%d\n", aReflowState.availableWidth, aReflowState.availableHeight, aReflowState.computedWidth, aReflowState.computedHeight, aMetrics.width, aMetrics.height, aMetrics.mCarriedOutBottomMargin); #endif return rv; } void nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nsBlockReflowState& aState, nsHTMLReflowMetrics& aMetrics) { const nsMargin& borderPadding = aState.BorderPadding(); // Special check for zero sized content: If our content is zero // sized then we collapse into nothingness. // // Consensus after discussion with a few CSS folks is that html's // notion of collapsing
's should take precedence over non
  // auto-sided block elements. Therefore we don't honor the width,
  // height, border or padding attributes (the parent has to not apply
  // a margin for us also).
  //
  // Note that this is only done for html paragraphs. Its not
  // appropriate to apply it to other containers, especially XML
  // content!
  PRBool isHTMLParagraph = 0 != (mState & NS_BLOCK_IS_HTML_PARAGRAPH);
  if (isHTMLParagraph &&
      (aReflowState.mStyleDisplay->mDisplay == NS_STYLE_DISPLAY_BLOCK) &&
      (((0 == aState.mKidXMost) ||
        (0 == aState.mKidXMost - borderPadding.left)) &&
       (0 == aState.mY - borderPadding.top))) {
    // Zero out the works
    aMetrics.width = 0;
    aMetrics.height = 0;
    aMetrics.ascent = 0;
    aMetrics.descent = 0;
    aMetrics.mCarriedOutBottomMargin = 0;
    if (nsnull != aMetrics.maxElementSize) {
      aMetrics.maxElementSize->width = 0;
      aMetrics.maxElementSize->height = 0;
    }
  }
  else {
    // Compute final width
    nscoord maxWidth = 0, maxHeight = 0;
    nscoord minWidth = aState.mKidXMost + borderPadding.right;
    if (!aState.mUnconstrainedWidth && aReflowState.HaveFixedContentWidth()) {
      // Use style defined width
      aMetrics.width = borderPadding.left + aReflowState.computedWidth +
        borderPadding.right;
      // XXX quote css1 section here
      if ((0 == aReflowState.computedWidth) && (aMetrics.width < minWidth)) {
        aMetrics.width = minWidth;
      }
      // When style defines the width use it for the max-element-size
      // because we can't shrink any smaller.
      maxWidth = aMetrics.width;
    }
    else {
      nscoord computedWidth = minWidth;
      PRBool compact = PR_FALSE;
#if 0
      if (NS_STYLE_DISPLAY_COMPACT == aReflowState.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)
             /*XXX && (computedWidth <= aState.mCompactMarginWidth) */
              )) {
          compact = PR_TRUE;
        }
      }
#endif
      // There are two options here. We either shrink wrap around our
      // contents or we fluff out to the maximum block width. Note:
      // We always shrink wrap when given an unconstrained width.
      if ((0 == (NS_BLOCK_SHRINK_WRAP & mFlags)) &&
          !aState.mUnconstrainedWidth &&
          !compact) {
        // Set our width to the max width if we aren't already that
        // wide. Note that the max-width has nothing to do with our
        // contents (CSS2 section XXX)
        computedWidth = borderPadding.left + aState.mContentArea.width +
          borderPadding.right;
//        maxWidth = aState.mMaxElementSize.width +
//          borderPadding.left + borderPadding.right;
      }
      else if (aState.mComputeMaxElementSize) {
        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 in
          maxWidth = aState.mKidXMost + borderPadding.right;
        }
        else {
          // Add in border and padding dimensions to already computed
          // max-element-size values.
          maxWidth = aState.mMaxElementSize.width +
            borderPadding.left + borderPadding.right;
        }
        // See if our max-element-size width is larger than our
        // computed-width. This happens when we are impacted by a
        // floater. When this does happen, our desired size needs to
        // include room for the floater.
        if (computedWidth < maxWidth) {
#ifdef DEBUG_kipp
          ListTag(stdout);
          printf(": adjusting width from %d to %d\n", computedWidth,
                 maxWidth);
#endif
          computedWidth = maxWidth;
        }
      }
      // Apply min/max values
      if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) {
        nscoord computedMaxWidth = aReflowState.mComputedMaxWidth +
          borderPadding.left + borderPadding.right;
        if (computedWidth > computedMaxWidth) {
          computedWidth = aReflowState.mComputedMaxWidth;
        }
      }
      if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) {
        nscoord computedMinWidth = aReflowState.mComputedMinWidth +
          borderPadding.left + borderPadding.right;
        if (computedWidth < computedMinWidth) {
          computedWidth = computedMinWidth;
        }
      }
      aMetrics.width = computedWidth;
    }
    // Compute final height
    if (NS_UNCONSTRAINEDSIZE != aReflowState.computedHeight) {
      // Use style defined height
      aMetrics.height = borderPadding.top + aReflowState.computedHeight +
        borderPadding.bottom;
      // When style defines the height use it for the max-element-size
      // because we can't shrink any smaller.
      maxHeight = aMetrics.height;
    }
    else {
      nscoord autoHeight = aState.mY;
      // Shrink wrap our height around our contents.
      if (aState.mIsBottomMarginRoot) {
        // When we are a bottom-margin root make sure that our last
        // childs bottom margin is fully applied.
        // XXX check for a fit
        autoHeight += aState.mPrevBottomMargin;
      }
      autoHeight += borderPadding.bottom;
      // Apply min/max values
      if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxHeight) {
        nscoord computedMaxHeight = aReflowState.mComputedMaxHeight +
          borderPadding.top + borderPadding.bottom;
        if (autoHeight > computedMaxHeight) {
          autoHeight = computedMaxHeight;
        }
      }
      if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinHeight) {
        nscoord computedMinHeight = aReflowState.mComputedMinHeight +
          borderPadding.top + borderPadding.bottom;
        if (autoHeight < computedMinHeight) {
          autoHeight = computedMinHeight;
        }
      }
      aMetrics.height = autoHeight;
      if (aState.mComputeMaxElementSize) {
        maxHeight = aState.mMaxElementSize.height +
          borderPadding.top + borderPadding.bottom;
      }
    }
    aMetrics.ascent = aMetrics.height;
    aMetrics.descent = 0;
    if (aState.mComputeMaxElementSize) {
      // Store away the final value
      aMetrics.maxElementSize->width = maxWidth;
      aMetrics.maxElementSize->height = maxHeight;
    }
    // Return bottom margin information
    aMetrics.mCarriedOutBottomMargin =
      aState.mIsBottomMarginRoot ? 0 : aState.mPrevBottomMargin;
#ifdef DEBUG
    if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) {
      ListTag(stdout);
      printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height);
    }
    if (aState.mComputeMaxElementSize &&
        ((maxWidth > aMetrics.width) || (maxHeight > aMetrics.height))) {
      ListTag(stdout);
      printf(": WARNING: max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n",
             maxWidth, maxHeight, aMetrics.width, aMetrics.height,
             aState.mReflowState.availableWidth,
             aState.mReflowState.availableHeight);
    }
#endif
#ifdef NOISY_MAX_ELEMENT_SIZE
    if (aState.mComputeMaxElementSize) {
      IndentBy(stdout, GetDepth());
      if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) {
        printf("PASS1 ");
      }
      ListTag(stdout);
      printf(": max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n",
             maxWidth, maxHeight, aMetrics.width, aMetrics.height,
             aState.mReflowState.availableWidth,
             aState.mReflowState.availableHeight);
    }
#endif
  }
  // Compute the combined area of our children
  // XXX take into account the overflow->clip property!
// XXX_perf: This can be done incrementally
  nscoord xa = 0, ya = 0, xb = aMetrics.width, yb = 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 < xa) {
      xa = x;
    }
    if (xmost > xb) {
      xb = xmost;
    }
    if (y < ya) {
      ya = y;
    }
    if (ymost > yb) {
      yb = ymost;
    }
    line = line->mNext;
  }
#ifdef NOISY_COMBINED_AREA
  IndentBy(stdout, GetDepth());
  ListTag(stdout);
  printf(": ca=%d,%d,%d,%d\n", xa, ya, xb-xa, yb-ya);
#endif
  // If the combined area of our children exceeds our bounding box
  // then set the NS_FRAME_OUTSIDE_CHILDREN flag, otherwise clear it.
  aMetrics.mCombinedArea.x = xa;
  aMetrics.mCombinedArea.y = ya;
  aMetrics.mCombinedArea.width = xb - xa;
  aMetrics.mCombinedArea.height = yb - ya;
  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;
  }
}
nsresult
nsBlockFrame::PrepareInitialReflow(nsBlockReflowState& aState)
{
  PrepareResizeReflow(aState);
  return NS_OK;
}
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* prevLine;
  nsLineBox* line = FindLineFor(aState.mNextRCFrame, &prevLine, &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);
  }
  // If the line that was affected is a block then just mark it dirty
  // so that we reflow it.
  if (line->IsBlock()) {
    line->MarkDirty();
#ifdef NOISY_INCREMENTAL_REFLOW
    ListTag(stdout);
    printf(": mark line %p dirty\n", line);
#endif
  }
  else {
    // Mark previous line dirty if its an inline line so that it can
    // maybe pullup something from the line just affected.
    if (prevLine && !prevLine->IsBlock()) {
      prevLine->MarkDirty();
#ifdef NOISY_INCREMENTAL_REFLOW
      ListTag(stdout);
      printf(": mark prev-line %p dirty\n", prevLine);
#endif
    }
    else {
      line->MarkDirty();
#ifdef NOISY_INCREMENTAL_REFLOW
      ListTag(stdout);
      printf(": mark line %p dirty\n", line);
#endif
    }
  }
  return NS_OK;
}
void
nsBlockFrame::UpdateBulletPosition()
{
  if (nsnull == mBullet) {
    // Don't bother if there is no bullet
    return;
  }
  const nsStyleList* styleList;
  GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList);
  if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) {
    if (HaveOutsideBullet()) {
      // We now have an inside bullet, but used to have an outside
      // bullet.  Adjust the frame lists and mark the first line
      // dirty.
      if (nsnull != mLines) {
        mBullet->SetNextSibling(mLines->mFirstChild);
        mLines->mFirstChild = mBullet;
        mLines->mChildCount++;
        mLines->MarkDirty();
      }
    }
    mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
  }
  else {
    if (!HaveOutsideBullet()) {
      // We now have an outside bullet, but used to have an inside
      // bullet. Take the bullet frame out of the first lines frame
      // list.
      if ((nsnull != mLines) && (mBullet == mLines->mFirstChild)) {
        nsIFrame* next;
        mBullet->GetNextSibling(&next);
        mBullet->SetNextSibling(nsnull);
        NS_ASSERTION(mLines->mChildCount > 0, "empty line w/o bullet");
        if (--mLines->mChildCount == 0) {
          nsLineBox* nextLine = mLines->mNext;
          delete mLines;
          mLines = nextLine;
          if (nsnull != nextLine) {
            nextLine->MarkDirty();
          }
        }
        else {
          mLines->mFirstChild = next;
          mLines->MarkDirty();
        }
      }
    }
    mState |= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
  }
#ifdef DEBUG
  VerifyLines(PR_TRUE);
#endif
}
nsresult
nsBlockFrame::PrepareStyleChangedReflow(nsBlockReflowState& aState)
{
  UpdateBulletPosition();
  // Mark everything dirty
  nsLineBox* line = mLines;
  while (nsnull != line) {
    line->MarkDirty();
    line = line->mNext;
  }
  return NS_OK;
}
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,
                          nsLineBox** aPrevLineResult,
                          PRBool* aIsFloaterResult)
{
  nsLineBox* prevLine = nsnull;
  nsLineBox* line = mLines;
  PRBool isFloater = PR_FALSE;
  while (nsnull != line) {
    if (line->Contains(aFrame)) {
      break;
    }
    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->GetOutOfFlowFrame()) {
          isFloater = PR_TRUE;
          goto done;
        }
      }
    }
    prevLine = line;
    line = line->mNext;
  }
 done:
  *aIsFloaterResult = isFloater;
  *aPrevLineResult = prevLine;
  return line;
}
void
nsBlockFrame::RecoverStateFrom(nsBlockReflowState& aState,
                               nsLineBox* aLine,
                               nscoord aDeltaY)
{
  PRBool applyTopMargin = PR_FALSE;
  if (aLine->IsBlock()) {
    nsIFrame* framePrevInFlow;
    aLine->mFirstChild->GetPrevInFlow(&framePrevInFlow);
    if (nsnull == framePrevInFlow) {
      applyTopMargin = ShouldApplyTopMargin(aState, aLine);
    }
  }
  aState.RecoverStateFrom(aLine, applyTopMargin, aDeltaY);
}
/**
 * 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 impactYA = aLine->mCombinedArea.y;
    nscoord impactYB = aLine->mCombinedArea.YMost();
#ifdef NOISY_INCREMENTAL_REFLOW
    if (aState.mReflowState.reason == eReflowReason_Incremental) {
      IndentBy(stdout, gNoiseIndent);
      printf("impactYA=%d impactYB=%d deltaY=%d\n",
             impactYA, impactYB, 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 lineYA = next->mBounds.y + aDeltaY;
      nscoord lineYB = lineYA + next->mBounds.height;
      if ((lineYA < impactYB) && (impactYA < lineYB)) {
#ifdef NOISY_INCREMENTAL_REFLOW
        if (aState.mReflowState.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.mReflowState.reason == eReflowReason_Incremental) {
    nsIReflowCommand::ReflowType type;
    aState.mReflowState.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.mReflowState.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.x, line->mBounds.y,
             line->mBounds.width, line->mBounds.height,
             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.mReflowState.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.x, line->mBounds.y,
             line->mBounds.width, line->mBounds.height,
             deltaY);
    }
#endif
    // If this is an inline frame then its time to stop
    aState.mPrevLine = line;
    line = line->mNext;
    aState.mLineLayout->AdvanceToNextLine();
  }
  // 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);
      // When pushing and pulling frames we need to check for whether any
      // views need to be reparented
      nsHTMLContainerFrame::ReparentFrameView(frame, mNextInFlow, 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->AdvanceToNextLine();
    }
  }
#ifdef NOISY_INCREMENTAL_REFLOW
  if (aState.mReflowState.reason == eReflowReason_Incremental) {
    gNoiseIndent--;
    IndentBy(stdout, gNoiseIndent);
    ListTag(stdout);
    printf(": done reflowing dirty lines (status=%x)\n",
           aState.mReflowStatus);
  }
#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;
  }
}
/**
 * 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* aKeepReflowGoing)
{
  nsresult rv = NS_OK;
#ifdef DEBUG
  aLine->MarkWasDirty();
#endif
  // 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()) {
#ifdef DEBUG_kipp
    printf("XXX: ");
    ListTag(stdout);
    printf(": huh? reflow-line with empty line!\n");
#endif
    nsIFrame* frame;
    rv = PullFrame(aState, aLine, frame);
    if (NS_FAILED(rv)) {
      return rv;
    }
    if (nsnull == frame) {
      *aKeepReflowGoing = PR_FALSE;
      return rv;
    }
  }
  // Setup the line-layout for the new line
  aState.mCurrentLine = aLine;
  aLine->ClearDirty();
  aLine->SetNeedDidReflow();
  // Now that we know what kind of line we have, reflow it
  if (aLine->IsBlock()) {
    NS_ASSERTION(nsnull == aLine->mFloaters, "bad line");
    rv = ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
  }
  else {
    rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
  }
  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 of 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");
      NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters");
    }
    if (0 != --fromLine->mChildCount) {
      NS_ASSERTION(fromLine->mChildCount < 10000, "bad line count");
      // 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) {
      // Before we set the new parent frame get the current parent
      nsIFrame* oldParentFrame;
      frame->GetParent(&oldParentFrame);
      frame->SetParent(this);
      // When pushing and pulling frames we need to check for whether any
      // views need to be reparented
      NS_ASSERTION(oldParentFrame != this, "unexpected parent frame");
      nsHTMLContainerFrame::ReparentFrameView(frame, oldParentFrame, 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);
    }
    // Stop pulling because we found a frame to pull
    aStopPulling = PR_TRUE;
    aFrameResult = frame;
#ifdef DEBUG
    VerifyLines(PR_TRUE);
#endif
  }
  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::SlideLine(nsIPresContext* aPresContext,
                        nsISpaceManager* aSpaceManager,
                        nsLineBox* aLine, nscoord aDY)
{
#if 0
ListTag(stdout); printf(": SlideLine: line=%p dy=%d\n", aLine, 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)
{
  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->GetOutOfFlowFrame();
      floater->GetRect(r);
      r.y += aDY;
      floater->SetRect(r);
    }
  }
}
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->GetOutOfFlowFrame();
        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;
}
NS_IMETHODIMP 
nsBlockFrame::AttributeChanged(nsIPresContext* aPresContext,
                               nsIContent*     aChild,
                               nsIAtom*        aAttribute,
                               PRInt32         aHint)
{
  nsresult rv = nsBlockFrameSuper::AttributeChanged(aPresContext, aChild,
                                                    aAttribute, aHint);
  if (NS_OK != rv) {
    return rv;
  }
  if (nsHTMLAtoms::start == aAttribute) {
    // XXX Not sure if this is necessary anymore
    RenumberLists();
    nsCOMPtr ... ). This is where
      // the second case can happen.
      if (HaveOutsideBullet() &&
          ((aLine == mLines) ||
           ((0 == mLines->mBounds.height) && (aLine == mLines->mNext)))) {
        // Reflow the bullet
        nsHTMLReflowMetrics metrics(nsnull);
        ReflowBullet(aState, metrics);
        // For bullets that are placed next to a child block, there will
        // be no correct ascent value. Therefore, make one up...
        nscoord ascent = 0;
        const nsStyleFont* font;
        frame->GetStyleData(eStyleStruct_Font,
                            (const nsStyleStruct*&) font);
        nsIRenderingContext& rc = *aState.mReflowState.rendContext;
        rc.SetFont(font->mFont);
        nsIFontMetrics* fm;
        rv = rc.GetFontMetrics(fm);
        if (NS_SUCCEEDED(rv) && (nsnull != fm)) {
          fm->GetMaxAscent(ascent);
          NS_RELEASE(fm);
        }
        rv = NS_OK;
        // Tall bullets won't look particularly nice here...
        nsRect bbox;
        mBullet->GetRect(bbox);
        nscoord topMargin = applyTopMargin ? collapsedBottomMargin : 0;
        bbox.y = aState.BorderPadding().top + ascent -
          metrics.ascent + topMargin;
        mBullet->SetRect(bbox);
      }
    }
    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;
      }
    }
  }
#ifdef DEBUG
  VerifyLines(PR_TRUE);
#endif
  return rv;
}
#define LINE_REFLOW_OK   0
#define LINE_REFLOW_STOP 1
#define LINE_REFLOW_REDO 2
nsresult
nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
                                 nsLineBox* aLine,
                                 PRBool* aKeepReflowGoing)
{
  nsresult rv = NS_OK;
  *aKeepReflowGoing = PR_TRUE;
#ifdef DEBUG
  PRInt32 spins = 0;
#endif
  for (;;) {
    if (nsnull != aLine->mFloaters) {
      // Forget all of the floaters on the line
      aLine->mFloaters->Clear();
    }
    aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0);
    aState.mPendingFloaters.Clear();
    // Setup initial coordinate system for reflowing the inline frames
    // into. Apply a previous block frame's bottom margin first.
    aState.mY += aState.mPrevBottomMargin;
    aState.GetAvailableSpace();
    const nsMargin& borderPadding = aState.BorderPadding();
    nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
    nscoord availWidth = aState.mAvailSpaceRect.width;
    nscoord availHeight;
    if (aState.mUnconstrainedHeight) {
      availHeight = NS_UNCONSTRAINEDSIZE;
    }
    else {
      /* XXX get the height right! */
      availHeight = aState.mAvailSpaceRect.height;
    }
    PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount();
    nsLineLayout* lineLayout = aState.mLineLayout;
    lineLayout->BeginLineReflow(x, aState.mY,
                                availWidth, availHeight,
                                impactedByFloaters,
                                PR_FALSE /*XXX isTopOfPage*/);
    if ((0 == lineLayout->GetLineNumber()) &&
        (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
      lineLayout->SetFirstLetterStyleOK(PR_TRUE);
    }
    // Reflow the frames that are already on the line first
    PRUint8 lineReflowStatus = LINE_REFLOW_OK;
    PRInt32 i;
    nsIFrame* frame = aLine->mFirstChild;
    for (i = 0; i < aLine->ChildCount(); i++) {
      rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
      if (NS_FAILED(rv)) {
        return rv;
      }
      if (LINE_REFLOW_OK != lineReflowStatus) {
        // It is possible that one or more of next lines are empty
        // (because of DeleteChildsNextInFlow). If so, delete them now
        // in case we are finished.
        nsLineBox* nextLine = aLine->mNext;
        while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
          // XXX Is this still necessary now that DeleteChildsNextInFlow
          // uses DoRemoveFrame?
          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 (LINE_REFLOW_OK == lineReflowStatus) {
      rv = PullFrame(aState, aLine, frame);
      if (NS_FAILED(rv)) {
        return rv;
      }
      if (nsnull == frame) {
        break;
      }
      while (LINE_REFLOW_OK == lineReflowStatus) {
        PRInt32 oldCount = aLine->ChildCount();
        rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
        if (NS_FAILED(rv)) {
          return rv;
        }
        if (aLine->ChildCount() != oldCount) {
          // We just created a continuation for aFrame AND its going
          // to end up on this line (e.g. :first-letter
          // situation). Therefore we have to loop here before trying
          // to pull another frame.
          frame->GetNextSibling(&frame);
        }
        else {
          break;
        }
      }
    }
    if (LINE_REFLOW_REDO == lineReflowStatus) {
      // This happens only when we have a line that is impacted by
      // floaters and the first element in the line doesn't fit with
      // the floaters.
      //
      // What we do is to advance past the first floater we find and
      // then reflow the line all over again.
      NS_ASSERTION(aState.mBand.GetFloaterCount(),
                   "redo line on totally empty line");
      NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
                   "unconstrained height on totally empty line");
      aState.mY += aState.mAvailSpaceRect.height;
      // XXX: a small optimization can be done here when paginating:
      // if the new Y coordinate is past the end of the block then
      // push the line and return now instead of later on after we are
      // past the floater.
      lineLayout->EndLineReflow();
#ifdef DEBUG
      spins++;
      if (1000 == spins) {
        ListTag(stdout);
        printf(": yikes! spinning on a line over 1000 times!\n");
        NS_ABORT();
      }
#endif
      continue;
    }
    // If we are propogating out a break-before status then there is
    // no point in placing the line.
    NS_ASSERTION(aLine->mChildCount < 10000, "bad line child count");
    if (NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
      lineLayout->EndLineReflow();
    }
    else {
      rv = PlaceLine(aState, aLine, aKeepReflowGoing);
    }
    break;
  }
  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,
                                PRUint8* aLineReflowStatus)
{
  *aLineReflowStatus = LINE_REFLOW_OK;
  // 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();
#ifdef NOISY_FIRST_LETTER
  ListTag(stdout);
  printf(": reflowing ");
  nsFrame::ListTag(stdout, aFrame);
  printf(" reflowingFirstLetter=%s\n", reflowingFirstLetter ? "on" : "off");
#endif
  // Reflow the inline frame
  nsLineLayout* lineLayout = aState.mLineLayout;
  nsReflowStatus frameReflowStatus;
  nsresult rv = lineLayout->ReflowFrame(aFrame, &aState.mNextRCFrame,
                                        frameReflowStatus);
  if (NS_FAILED(rv)) {
    return rv;
  }
#ifdef REALLY_NOISY_REFLOW_CHILD
  nsFrame::ListTag(stdout, aFrame);
  printf(": status=%x\n", frameReflowStatus);
#endif
#if defined(REFLOW_STATUS_COVERAGE)
  RecordReflowStatus(PR_FALSE, frameReflowStatus);
#endif
  // Send post-reflow notification
  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).
    *aLineReflowStatus = LINE_REFLOW_STOP;
    // 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) {
        // If we break before the first frame on the line then we must
        // be trying to place content where theres no room (e.g. on a
        // line with wide floaters). Inform the caller to reflow the
        // line after skipping past a floater.
        *aLineReflowStatus = LINE_REFLOW_REDO;
      }
      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
      nsIFrame* nextFrame;
      aFrame->GetNextSibling(&nextFrame);
      rv = SplitLine(aState, aLine, nextFrame);
      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;
    }
    // If we are reflowing the first letter frame then don't split the
    // line and don't stop the line reflow...
    PRBool splitLine = !reflowingFirstLetter;
    if (reflowingFirstLetter) {
      if (aLine->IsFirstLine()) {
        splitLine = PR_TRUE;
      }
      else {
        nsIAtom* frameType;
        if (NS_SUCCEEDED(aFrame->GetFrameType(&frameType)) && frameType) {
          if (frameType == nsLayoutAtoms::inlineFrame) {
            splitLine = PR_TRUE;
          }
          NS_RELEASE(frameType);
        }
      }
    }
    if (splitLine) {
      // Split line after the current frame
      *aLineReflowStatus = LINE_REFLOW_STOP;
      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 DEBUG
  VerifyLines(PR_FALSE);
#endif
  return rv;
}
nsresult
nsBlockFrame::SplitLine(nsBlockReflowState& aState,
                        nsLineBox* aLine,
                        nsIFrame* aFrame)
{
  nsLineLayout* lineLayout = aState.mLineLayout;
  PRInt32 pushCount = aLine->ChildCount() - lineLayout->GetCurrentSpanCount();
  NS_ASSERTION(pushCount >= 0, "bad push count"); 
//printf("BEFORE (pushCount=%d):\n", pushCount);
//aLine->List(stdout, 0);
  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());
    to->SetIsFirstLine(aLine->IsFirstLine());
    aLine->mChildCount -= pushCount;
    // Let line layout know that some frames are no longer part of its
    // state.
    if (!aLine->IsBlock()) {
      lineLayout->SplitLineTo(aLine->ChildCount());
    }
#ifdef DEBUG
    VerifyLines(PR_TRUE);
#endif
  }
  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;
  // Vertically align the frames on this line.
  //
  // According to the CSS2 spec, section 12.6.1, the "marker" box
  // participates in the height calculation of the list-item box's
  // first line box.
  //
  // There are exactly two places a bullet can be placed: near the
  // first or second line. Its only placed on the second line in a
  // rare case: an empty first line followed by a second line that
  // contains a block (example:  ... ).
  //
  // For this code, only the first case is possible because this
  // method is used for placing a line of inline frames. If the rare
  // case is happening then the worst that will happen is that the
  // bullet frame will be reflowed twice.
  nsLineLayout* lineLayout = aState.mLineLayout;
  PRBool addedBullet = PR_FALSE;
  if (HaveOutsideBullet() && (aLine == mLines) &&
      !lineLayout->IsZeroHeight()) {
    nsHTMLReflowMetrics metrics(nsnull);
    ReflowBullet(aState, metrics);
    lineLayout->AddBulletFrame(mBullet, metrics);
    addedBullet = PR_TRUE;
  }
  nsSize maxElementSize;
  lineLayout->VerticalAlignFrames(aLine->mBounds, maxElementSize);
#ifdef DEBUG
	{
		static nscoord lastHeight = 0;
	  if (CRAZY_HEIGHT(aLine->mBounds.y)) {
	  	lastHeight = aLine->mBounds.y;
	  	if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) {
		    nsFrame::ListTag(stdout);
		    printf(": line=%p y=%d line.bounds.height=%d\n",
		           aLine, aLine->mBounds.y, aLine->mBounds.height);
	    }
	  }
	  else {
	  	lastHeight = 0;
	  }
  }
#endif
  // Only block frames horizontally align their children because
  // inline frames "shrink-wrap" around their children (therefore
  // there is no extra horizontal space).
#if XXX_fix_me
  PRBool allowJustify = PR_TRUE;
  if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) {
    allowJustify = ShouldJustifyLine(aState, aLine);
  }
#else
  PRBool allowJustify = PR_FALSE;
#endif
  lineLayout->TrimTrailingWhiteSpace(aLine->mBounds);
  lineLayout->HorizontalAlignFrames(aLine->mBounds, allowJustify);
  lineLayout->RelativePositionFrames(aLine->mCombinedArea);
  if (addedBullet) {
    lineLayout->RemoveBulletFrame(mBullet);
  }
  // Inline lines do not have margins themselves; however they are
  // impacted by prior block margins. If this line ends up having some
  // height then we zero out the previous bottom margin value that was
  // already applied to the line's starting Y coordinate. Otherwise we
  // leave it be so that the previous blocks bottom margin can be
  // collapsed with a block that follows.
  nscoord newY;
  if (aLine->mBounds.height > 0) {
    // This line has some height. Therefore the application of the
    // previous-bottom-margin should stick.
    aState.mPrevBottomMargin = 0;
    newY = aLine->mBounds.YMost();
  }
  else {
    // Don't let the previous-bottom-margin value affect the newY
    // coordinate (it was applied in ReflowInlineFrames speculatively)
    // since the line is empty.
    nscoord dy = -aState.mPrevBottomMargin;
    newY = aState.mY + dy;
    aLine->mCombinedArea.y += dy;
    aLine->mBounds.y += dy;
  }
  aLine->mCarriedOutBottomMargin = 0;/* XXX_ib */
  // See if the line fit. If it doesn't we need to push it. Our first
  // line will always fit.
  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;
    }
    lineLayout->EndLineReflow();
    return rv;
  }
  aState.mY = newY;
  if (aState.mComputeMaxElementSize) {
    if (0 != aState.mBand.GetFloaterCount()) {
      // Add in floater impacts to the lines max-element-size
      ComputeLineMaxElementSize(aState, aLine, &maxElementSize);
    }
  }
  PostPlaceLine(aState, aLine, maxElementSize);
  // Any below current line floaters to place?
  if (0 != aState.mPendingFloaters.Count()) {
    // Now is the time that we reflow them and update the computed
    // line combined area.
    aState.PlaceBelowCurrentLineFloaters(&aState.mPendingFloaters, PR_TRUE);
    aState.mPendingFloaters.Clear();
  }
  // When a line has floaters, factor them into the combined-area
  // computations.
  if (nsnull != aLine->mFloaters) {
    // Combine the floater combined area (stored in aState) and the
    // value computed by the line layout code.
    CombineRects(aState.mFloaterCombinedArea, aLine->mCombinedArea);
  }
  // Apply break-after clearing if necessary
  switch (aLine->mBreakType) {
  case NS_STYLE_CLEAR_LEFT:
  case NS_STYLE_CLEAR_RIGHT:
  case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
    aState.ClearFloaters(aState.mY, aLine->mBreakType);
    break;
  }
  lineLayout->EndLineReflow();
  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.mBand.GetMaxElementSize(&maxWidth, &maxHeight);
#ifdef NOISY_MAX_ELEMENT_SIZE
  IndentBy(stdout, GetDepth());
  if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) {
    printf("PASS1 ");
  }
  ListTag(stdout);
  printf(": maxFloaterSize=%d,%d\n", maxWidth, maxHeight);
#endif
  // If the floaters are wider than the content, then use the maximum
  // floater width as the maximum width.
  //
  // It used to be the case that we would always place some content
  // next to a floater, regardless of the amount of available space
  // after subtracing off the floaters sizes. This can lead to content
  // overlapping floaters, so we no longer do this (and pass CSS2's
  // conformance tests). This is not how navigator 4-1 used to do
  // things.
  if (maxWidth > aMaxElementSize->width) {
    aMaxElementSize->width = maxWidth;
  }
  // Only update the max-element-size's height value if the floater is
  // part of the current line.
  if ((nsnull != aLine->mFloaters) &&
      (0 != aLine->mFloaters->Count())) {
    // 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) {
    aState.UpdateMaxElementSize(aMaxElementSize);
  }
#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) {
#ifdef DEBUG
    if (CRAZY_WIDTH(xmost)) {
      ListTag(stdout);
      printf(": line=%p xmost=%d\n", aLine, xmost);
    }
#endif
    aState.mKidXMost = xmost;
  }
}
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);
#ifdef DEBUG
  VerifyOverflowSituation();
#endif
}
PRBool
nsBlockFrame::DrainOverflowLines()
{
#ifdef DEBUG
  VerifyOverflowSituation();
#endif
  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;
      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);
        // When pushing and pulling frames we need to check for whether any
        // views need to be reparented
        nsHTMLContainerFrame::ReparentFrameView(frame, prevBlock, this);
        // Get the next frame
        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.
    nsLineBox* lastLine = nsLineBox::LastLine(mLines);
    if (nsnull == lastLine) {
      mLines = mOverflowLines;
    }
    else {
      lastLine->mNext = mOverflowLines;
      nsIFrame* lastFrame = lastLine->LastChild();
      lastFrame->SetNextSibling(mOverflowLines->mFirstChild);
    }
    mOverflowLines = nsnull;
    drained = PR_TRUE;
  }
  return drained;
}
//////////////////////////////////////////////////////////////////////
// Frame list manipulation routines
nsIFrame*
nsBlockFrame::LastChild()
{
  if (mLines) {
    nsLineBox* line = nsLineBox::LastLine(mLines);
    return line->LastChild();
  }
  return nsnull;
}
nsresult
nsBlockFrame::WrapFramesInFirstLineFrame(nsIPresContext* aPresContext)
{
  nsLineBox* line = mLines;
  if (!line) {
    return NS_OK;
  }
  // Find the first and last inline line that has frames that aren't
  // yet in the first-line-frame.
  nsLineBox* prevLine = nsnull;
  nsIFrame* firstInlineFrame = nsnull;
  nsFirstLineFrame* lineFrame = nsnull;
  nsIFrame* nextSib = nsnull;
  nsresult rv = NS_OK;
  while (line) {
    if (line->IsBlock()) {
      nextSib = line->mFirstChild;
      break;
    }
    if (line->IsFirstLine()) {
      NS_ASSERTION(1 == line->mChildCount, "bad first line");
      lineFrame = (nsFirstLineFrame*) line->mFirstChild;
      prevLine = line;
      line = line->mNext;
    }
    else {
      if (!firstInlineFrame) {
        firstInlineFrame = line->mFirstChild;
      }
      if (prevLine) {
        prevLine->mNext = line->mNext;
        delete line;
        line = prevLine->mNext;
      }
      else {
        prevLine = line;
        line = line->mNext;
      }
    }
  }
  if (!firstInlineFrame) {
    // All of the inline frames are already where they should be
    return NS_OK;
  }
  // Make the last inline frame thats going into the first-line-frame
  // have no next sibling.
  if (line) {
    nsFrameList frames(firstInlineFrame);
    nsIFrame* lastInlineFrame = frames.GetPrevSiblingFor(line->mFirstChild);
    lastInlineFrame->SetNextSibling(nsnull);
  }
  // If there is no first-line-frame currently, we create it
  if (!lineFrame) {
    nsIStyleContext* firstLineStyle = GetFirstLineStyle(aPresContext);
    // Create line frame
    rv = NS_NewFirstLineFrame((nsIFrame**) &lineFrame);
    if (NS_FAILED(rv)) {
      return rv;
    }
    rv = lineFrame->Init(*aPresContext, mContent, this,
                         firstLineStyle, nsnull);
    if (NS_FAILED(rv)) {
      return rv;
    }
    line = mLines;
    line->mFirstChild = lineFrame;
    line->mChildCount = 1;
    line->SetIsFirstLine(PR_TRUE);
    NS_RELEASE(firstLineStyle);
  }
  // Connect last first-line-frame to the remaining frames
  lineFrame->SetNextSibling(nextSib);
  // Put the new children into the line-frame
  lineFrame->AppendFrames2(aPresContext, firstInlineFrame);
  // Mark the first-line frames dirty
  line = mLines;
  while (line && line->IsFirstLine()) {
    line->MarkDirty();
    line = line->mNext;
  }
  return rv;
}
NS_IMETHODIMP
nsBlockFrame::AppendFrames(nsIPresContext& aPresContext,
                           nsIPresShell&   aPresShell,
                           nsIAtom*        aListName,
                           nsIFrame*       aFrameList)
{
  if (nsnull == aFrameList) {
    return NS_OK;
  }
  if (nsLayoutAtoms::floaterList == aListName) {
    // XXX we don't *really* care about this right now because we are
    // BuildFloaterList ing still
    mFloaters.AppendFrames(nsnull, aFrameList);
    return NS_OK;
  }
  else if (nsLayoutAtoms::absoluteList == aListName) {
    // XXX temporary until area frame is updated
    return nsFrame::AppendFrames(aPresContext, aPresShell, aListName,
                                 aFrameList);
  }
  else if (nsnull != aListName) {
    return NS_ERROR_INVALID_ARG;
  }
  // Find the proper last-child for where the append should go
  nsIFrame* lastKid = nsnull;
  nsLineBox* lastLine = nsLineBox::LastLine(mLines);
  if (lastLine) {
    lastKid = lastLine->LastChild();
    if (lastLine->IsFirstLine()) {
      // Get last frame in the nsFirstLineFrame
      lastKid->FirstChild(nsnull, &lastKid);
      nsFrameList frames(lastKid);
      lastKid = frames.LastChild();
    }
  }
  // Add frames after the last child
#ifdef NOISY_REFLOW_REASON
  ListTag(stdout);
  printf(": append ");
  nsFrame::ListTag(stdout, aFrameList);
  if (lastKid) {
    printf(" after ");
    nsFrame::ListTag(stdout, lastKid);
  }
  printf("\n");
#endif
  nsresult rv = AddFrames(&aPresContext, aFrameList, lastKid);
  if (NS_SUCCEEDED(rv)) {
    // Generate reflow command to reflow the dirty lines
    nsIReflowCommand* reflowCmd = nsnull;
    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;
}
NS_IMETHODIMP
nsBlockFrame::InsertFrames(nsIPresContext& aPresContext,
                           nsIPresShell&   aPresShell,
                           nsIAtom*        aListName,
                           nsIFrame*       aPrevFrame,
                           nsIFrame*       aFrameList)
{
  if (nsLayoutAtoms::floaterList == aListName) {
    // XXX we don't *really* care about this right now because we are
    // BuildFloaterList ing still
    mFloaters.AppendFrames(nsnull, aFrameList);
    return NS_OK;
  }
  else if (nsLayoutAtoms::absoluteList == aListName) {
    // XXX temporary until area frame and floater code is updated
    return nsFrame::InsertFrames(aPresContext, aPresShell, aListName,
                                 aPrevFrame, aFrameList);
  }
  else if (nsnull != aListName) {
    return NS_ERROR_INVALID_ARG;
  }
#ifdef NOISY_REFLOW_REASON
  ListTag(stdout);
  printf(": insert ");
  nsFrame::ListTag(stdout, aFrameList);
  if (aPrevFrame) {
    printf(" after ");
    nsFrame::ListTag(stdout, aPrevFrame);
  }
  printf("\n");
#endif
  nsresult rv = AddFrames(&aPresContext, aFrameList, aPrevFrame);
  if (NS_SUCCEEDED(rv)) {
    // Generate reflow command to reflow the dirty lines
    nsIReflowCommand* reflowCmd = nsnull;
    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;
}
nsresult
nsBlockFrame::AddFrames(nsIPresContext* aPresContext,
                        nsIFrame* aFrameList,
                        nsIFrame* aPrevSibling)
{
  if (nsnull == aFrameList) {
    return NS_OK;
  }
  // Attempt to find the line that contains the previous sibling
  nsLineBox* prevSibLine = nsnull;
  PRInt32 prevSiblingIndex = -1;
  if (aPrevSibling) {
    // Its possible we have an nsFirstLineFrame managing some of our
    // child frames. If we do and the AddFrames is targetted at it,
    // use AddFirstLineFrames to get the frames properly placed.
    nsIFrame* prevSiblingParent;
    aPrevSibling->GetParent(&prevSiblingParent);
    if (prevSiblingParent != this) {
      // We are attempting an insert into a nsFirstLineFrame. Mark the
      // first-line's dirty. Not exactly optimial, but it will
      // guarantee a correct reflow.
      nsLineBox* line = mLines;
      while (line) {
        if (!line->IsFirstLine()) {
          break;
        }
        line->MarkDirty();
        line = line->mNext;
      }
      return AddFirstLineFrames(aPresContext,
                                (nsFirstLineFrame*)prevSiblingParent,
                                aFrameList, aPrevSibling);
    }
    else {
      // Find the line that contains the previous sibling
      prevSibLine = nsLineBox::FindLineContaining(mLines, aPrevSibling,
                                                  &prevSiblingIndex);
      NS_ASSERTION(nsnull != prevSibLine, "prev sibling not in line list");
      if (nsnull == prevSibLine) {
        // Note: defensive code! FindLineContaining must not return
        // null in this case, so if it does...
        aPrevSibling = nsnull;
      }
    }
  }
  else if (mLines && mLines->IsFirstLine()) {
    mLines->MarkDirty();
    return AddFirstLineFrames(aPresContext,
                              (nsFirstLineFrame*)mLines->mFirstChild,
                              aFrameList, nsnull);
  }
  // Find the frame following aPrevSibling so that we can join up the
  // two lists of frames.
  nsIFrame* prevSiblingNextFrame = nsnull;
  if (aPrevSibling) {
    aPrevSibling->GetNextSibling(&prevSiblingNextFrame);
    // Split line containing aPrevSibling in two if the insertion
    // point is somewhere in the middle of the line.
    PRInt32 rem = prevSibLine->mChildCount - prevSiblingIndex - 1;
    if (rem) {
      // Split the line in two where the frame(s) are being inserted.
      nsLineBox* line = new nsLineBox(prevSiblingNextFrame, rem, 0);
      if (!line) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      line->mNext = prevSibLine->mNext;
      prevSibLine->mNext = line;
      prevSibLine->mChildCount -= rem;
      prevSibLine->MarkDirty();
    }
    // Now (partially) join the sibling lists together
    aPrevSibling->SetNextSibling(aFrameList);
  }
  else if (mLines) {
    prevSiblingNextFrame = mLines->mFirstChild;
  }
  // Walk through the new frames being added and update the line data
  // structures to fit.
  nsIFrame* newFrame = aFrameList;
  while (newFrame) {
    PRUint32 isBlock = nsLineLayout::TreatFrameAsBlock(newFrame)
      ? LINE_IS_BLOCK
      : 0;
    // If the frame is a block frame, or if there is no previous line
    // or if the previous line is a block line then make a new line.
    if (isBlock || !prevSibLine || prevSibLine->IsBlock()) {
      // Create a new line for the frame and add its line to the line
      // list.
      nsLineBox* line = new nsLineBox(newFrame, 1, isBlock);
      if (!line) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      if (prevSibLine) {
        // Append new line after prevSibLine
        line->mNext = prevSibLine->mNext;
        prevSibLine->mNext = line;
      }
      else {
        // New line is going before the other lines
        line->mNext = mLines;
        mLines = line;
      }
      prevSibLine = line;
    }
    else {
      prevSibLine->mChildCount++;
      prevSibLine->MarkDirty();
    }
    aPrevSibling = newFrame;
    newFrame->GetNextSibling(&newFrame);
  }
  if (prevSiblingNextFrame) {
    // Connect the last new frame to the remainder of the sibling list
    aPrevSibling->SetNextSibling(prevSiblingNextFrame);
  }
  // Fixup any frames that should be in a first-line frame but aren't
  if ((NS_BLOCK_HAS_FIRST_LINE_STYLE & mState) &&
      (nsnull != mLines) && !mLines->IsBlock()) {
    // We just added one or more frame(s) to the first line.
    WrapFramesInFirstLineFrame(aPresContext);
  }
#ifdef DEBUG
  VerifyLines(PR_TRUE);
#endif
  MarkEmptyLines(aPresContext);
  return NS_OK;
}
nsresult
nsBlockFrame::AddFirstLineFrames(nsIPresContext* aPresContext,
                                 nsFirstLineFrame* aLineFrame,
                                 nsIFrame* aFrameList,
                                 nsIFrame* aPrevSibling)
{
  // The first run of inline frames being added always go into the
  // line frame. If we hit a block frame then we need to chop the line
  // frame into two pieces.
  nsIFrame* frame = aFrameList;
  nsIFrame* lastAddedFrame = frame;
  nsIFrame* firstInlineFrame = nsnull;
  PRInt32 pendingInlines = 0;
  while (frame) {
    if (nsLineLayout::TreatFrameAsBlock(frame)) {
      // There are 3 cases. The block is going to the front of the
      // line-frames children, in the middle or at the end.
      if (aPrevSibling) {
        nsIFrame* next;
        aPrevSibling->GetNextSibling(&next);
        // Take kids from the line frame starting at next.
        nsIFrame* kids;
        if (nsnull == next) {
          // The block goes in front of aLineFrame's continuation, if
          // it has one.
          nsFirstLineFrame* nextLineFrame;
          aLineFrame->GetNextInFlow((nsIFrame**) &nextLineFrame);
          if (!nextLineFrame) {
            // No continuation, therefore the block goes after aLineFrame
            FixParentAndView(aPresContext, frame);
            return AddFrames(aPresContext, frame, aLineFrame);
          }
          // Use nextLineFrame and take away all its kids
          aLineFrame = nextLineFrame;
        }
        kids = TakeKidsFromLineFrame(aLineFrame, next);
        // We will leave the line-frame and its continuations in
        // place but mark the lines dirty so that they are reflowed
        // and the empty line-frames (if any) are cleaned up.
        nsLineBox* line = mLines;
        nsIFrame* lastLineFrame = aLineFrame;
        while (line && line->IsFirstLine()) {
          line->MarkDirty();
          lastLineFrame = line->mFirstChild;
          line = line->mNext;
        }
        // Join the taken kids onto the *end* of the frames being
        // added.
        nsFrameList newFrames(frame);
        newFrames.AppendFrames(this, kids);
        FixParentAndView(aPresContext, frame);
        return AddFrames(aPresContext, frame, lastLineFrame);
      }
      else {
        // Block is trying to go to the front of the line frame (and
        // therefore in front of all the line-frames children and its
        // continuations children). Therefore, we don't need a line
        // frame anymore.
        nsIFrame* kids = TakeKidsFromLineFrame(aLineFrame, nsnull);
        // Join the kids onto the end of the frames being added
        nsFrameList newFrames(frame);
        newFrames.AppendFrames(this, kids);
        FixParentAndView(aPresContext, newFrames.FirstChild());
        // Remove the line frame (and its continuations). This also
        // removes the nsLineBox's that pointed to the line-frame.  Do
        // this after FixParentAndView because FixParentAndView needs
        // a valid old-parent to work.
        DoRemoveFrame(aPresContext, aLineFrame);
        // Re-enter AddFrames, this time there won't be any first-line
        // frames so we will use the normal path.
        return AddFrames(aPresContext, newFrames.FirstChild(), nsnull);
      }
    }
    else {
      if (0 == pendingInlines) {
        firstInlineFrame = frame;
      }
      pendingInlines++;
    }
    lastAddedFrame = frame;
    frame->GetNextSibling(&frame);
  }
  // All of the frames being added are inline frames
  if (pendingInlines) {
    return aLineFrame->InsertFrames2(aPresContext, aPrevSibling, aFrameList);
  }
  return NS_OK;
}
nsIFrame*
nsBlockFrame::TakeKidsFromLineFrame(nsFirstLineFrame* aLineFrame,
                                    nsIFrame* aFromKid)
{
  nsFrameList kids;
  nsIFrame* lastKid;
  if (aFromKid) {
    kids.SetFrames(aFromKid);
    aLineFrame->RemoveFramesFrom(aFromKid);
  }
  else {
    aLineFrame->FirstChild(nsnull, &lastKid);
    kids.SetFrames(lastKid);
    aLineFrame->RemoveAllFrames();
  }
  lastKid = kids.LastChild();
  // Capture the next-in-flows kids as well.
  for (;;) {
    aLineFrame->GetNextInFlow((nsIFrame**) &aLineFrame);
    if (!aLineFrame) {
      break;
    }
    aLineFrame->FirstChild(nsnull, &aFromKid);
    aLineFrame->RemoveAllFrames();
    lastKid->SetNextSibling(aFromKid);
    nsFrameList tmp(aFromKid);
    lastKid = tmp.LastChild();
  }
  return kids.FirstChild();
}
void
nsBlockFrame::FixParentAndView(nsIPresContext* aPresContext, nsIFrame* aFrame)
{
  while (aFrame) {
    nsIFrame* oldParent;
    aFrame->GetParent(&oldParent);
    aFrame->SetParent(this);
    if (this != oldParent) {
      nsHTMLContainerFrame::ReparentFrameView(aFrame, oldParent, this);
    }
    aFrame->ReResolveStyleContext(aPresContext, mStyleContext, 
                                  NS_STYLE_HINT_REFLOW,
                                  nsnull, nsnull);
    aFrame->GetNextSibling(&aFrame);
  }
}
NS_IMETHODIMP
nsBlockFrame::RemoveFrame(nsIPresContext& aPresContext,
                          nsIPresShell&   aPresShell,
                          nsIAtom*        aListName,
                          nsIFrame*       aOldFrame)
{
  nsresult rv = NS_OK;
  if (nsLayoutAtoms::floaterList == aListName) {
    // Remove floater from the floater list first
    mFloaters.RemoveFrame(aOldFrame);
    // Find which line contains the floater
    nsLineBox* line = mLines;
    while (nsnull != line) {
      nsVoidArray* floaters = line->mFloaters;
      if (nsnull != floaters) {
        PRInt32 i, count = floaters->Count();
        for (i = 0; i < count; i++) {
          nsPlaceholderFrame* ph = (nsPlaceholderFrame*)floaters->ElementAt(i);
          if (ph->GetOutOfFlowFrame() == aOldFrame) {
            // Note: the placeholder is part of the line's child list
            // and will be removed later.
            // XXX stop storing pointers to the placeholder in the line list???
            ph->SetOutOfFlowFrame(nsnull);
            floaters->RemoveElementAt(i);
            aOldFrame->DeleteFrame(aPresContext);
            goto found_it;
          }
        }
      }
      line = line->mNext;
    }
   found_it:
    // Mark every line at and below the line where the floater was dirty
    while (nsnull != line) {
      line->MarkDirty();
      line = line->mNext;
    }
    // We will reflow *after* removing the placeholder (which is done 2nd)
    return NS_OK;
  }
  else if (nsLayoutAtoms::absoluteList == aListName) {
    // XXX temporary until area frame code is updated
    return nsFrame::RemoveFrame(aPresContext, aPresShell, aListName,
                                aOldFrame);
  }
  else if (nsnull != aListName) {
    return NS_ERROR_INVALID_ARG;
  }
#ifdef NOISY_REFLOW_REASON
  ListTag(stdout);
  printf(": remove ");
  nsFrame::ListTag(stdout, aOldFrame);
  printf("\n");
#endif
  rv = DoRemoveFrame(&aPresContext, aOldFrame);
  if (NS_SUCCEEDED(rv)) {
    // Generate reflow command to reflow the dirty lines
    nsIReflowCommand* reflowCmd = nsnull;
    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;
}
nsresult
nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext,
                            nsIFrame* aDeletedFrame)
{
  nsIFrame* parent;
  aDeletedFrame->GetParent(&parent);
  if (parent != this) {
    return RemoveFirstLineFrame(aPresContext, (nsFirstLineFrame*)parent,
                                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
      aDeletedFrame->GetParent(&parent);
      NS_ASSERTION(flow == parent, "messed up delete code");
      NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
#endif
      // See if the frame being deleted is the last one on the line
      PRBool isLastFrameOnLine = PR_FALSE;
      if (1 == line->mChildCount) {
        isLastFrameOnLine = PR_TRUE;
      }
      else if (line->LastChild() == aDeletedFrame) {
        isLastFrameOnLine = PR_TRUE;
      }
      // Remove aDeletedFrame from the line
      nsIFrame* nextFrame;
      aDeletedFrame->GetNextSibling(&nextFrame);
      if (line->mFirstChild == aDeletedFrame) {
        line->mFirstChild = nextFrame;
      }
      if (prevLine && !prevLine->IsBlock()) {
        // Since we just removed a frame that follows some inline
        // frames, we need to reflow the previous line.
        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);
      nsSplittableType st;
      aDeletedFrame->IsSplittable(st);
      if (NS_FRAME_NOT_SPLITTABLE != st) {
        aDeletedFrame->RemoveFromFlow();
      }
#ifdef NOISY_REMOVE_FRAME
      printf("DoRemoveFrame: prevLine=%p line=%p frame=",
             prevLine, line);
      nsFrame::ListTag(stdout, aDeletedFrame);
      printf(" prevSibling=%p nextInFlow=%p\n", prevSibling, nextInFlow);
#endif
      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;
        line = next;
      }
      else {
        NS_ASSERTION(line->mChildCount < 10000, "bad line count"); 
        // Make the line that just lost a frame dirty
        line->MarkDirty();
        // If we just removed the last frame on the line then we need
        // to advance to the next line.
        if (isLastFrameOnLine) {
          prevLine = line;
          linep = &line->mNext;
          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* checkParent;
          aDeletedFrame->GetParent(&checkParent);
          NS_ASSERTION(checkParent != 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;
      linep = &flow->mLines;
      prevSibling = nsnull;
    }
  }
  // Fixup any frames that should be in a first-line frame but aren't
  if ((NS_BLOCK_HAS_FIRST_LINE_STYLE & mState) &&
      (nsnull != mLines) && !mLines->IsBlock()) {
    // We just added one or more frame(s) to the first line, or we
    // removed a block that preceeded the first line.
    WrapFramesInFirstLineFrame(aPresContext);
  }
#ifdef DEBUG
  VerifyLines(PR_TRUE);
#endif
  MarkEmptyLines(aPresContext);
  return NS_OK;
}
nsresult
nsBlockFrame::RemoveFirstLineFrame(nsIPresContext* aPresContext,
                                   nsFirstLineFrame* aLineFrame,
                                   nsIFrame* aDeletedFrame)
{
  // Strip deleted frame out of the nsFirstLineFrame
  aLineFrame->RemoveFrame2(aPresContext, aDeletedFrame);
  aDeletedFrame->DeleteFrame(*aPresContext);
  // See if the line-frame and its continuations are now empty
  nsFirstLineFrame* lf = (nsFirstLineFrame*) aLineFrame->GetFirstInFlow();
  nsFirstLineFrame* lf0 = lf;
  PRBool empty = PR_TRUE;
  while (lf) {
    nsIFrame* kids;
    lf->FirstChild(nsnull, &kids);
    if (kids) {
      empty = PR_FALSE;
      break;
    }
    lf->GetNextInFlow((nsIFrame**) &lf);
  }
  if (empty) {
    return DoRemoveFrame(aPresContext, lf0);
  }
  // Mark first-line lines dirty
  nsLineBox* line = mLines;
  while (line && line->IsFirstLine()) {
    line->MarkDirty();
    line = line->mNext;
  }
  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) ||
      (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == 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;
  aChild->GetNextInFlow(&nextInFlow);
  NS_PRECONDITION(nsnull != nextInFlow, "null next-in-flow");
  nsBlockFrame* parent;
  nextInFlow->GetParent((nsIFrame**)&parent);
  NS_PRECONDITION(nsnull != parent, "next-in-flow with no parent");
  NS_PRECONDITION(nsnull != parent->mLines, "next-in-flow with weird parent");
  NS_PRECONDITION(nsnull == parent->mOverflowLines, "parent with overflow");
  parent->DoRemoveFrame(&aPresContext, nextInFlow);
}
////////////////////////////////////////////////////////////////////////
// Floater support
nsresult
nsBlockFrame::ReflowFloater(nsBlockReflowState& aState,
                            nsPlaceholderFrame* aPlaceholder,
                            nsRect& aCombinedRect,
                            nsMargin& aMarginResult)
{
  // Reflow the floater. Since floaters are continued we given them an
  // unbounded height. Floaters with an auto width are sized to zero
  // according to the css2 spec.
  nsRect availSpace(0, 0, aState.mAvailSpaceRect.width, NS_UNCONSTRAINEDSIZE);
  nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame();
  PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
  // Setup block reflow state to reflow the floater
  nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState,
                           aState.mComputeMaxElementSize);
  brc.SetNextRCFrame(aState.mNextRCFrame);
  // Reflow the floater
  nsReflowStatus frameReflowStatus;
  nsMargin computedOffsets;
  nsresult rv = brc.ReflowBlock(floater, availSpace, PR_TRUE,
                                0, isAdjacentWithTop,
                                computedOffsets, frameReflowStatus);
  if (NS_FAILED(rv)) {
    return rv;
  }
  // Capture the margin information for the caller
  const nsMargin& m = brc.GetMargin();
  aMarginResult.top = brc.GetTopMargin();
  aMarginResult.right = m.right;
  aMarginResult.bottom = 
    nsBlockReflowContext::MaxMargin(brc.GetCarriedOutBottomMargin(),
                                    m.bottom);
  aMarginResult.left = m.left;
  const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
  aCombinedRect = metrics.mCombinedArea;
  floater->SizeTo(metrics.width, metrics.height);
  return NS_OK;
}
void
nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
{
  // Set the geometric parent of the floater
  nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame();
  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)
{
  NS_PRECONDITION(nsnull != mCurrentLine, "null ptr");
  // Update the current line's floater array
  if (nsnull == mCurrentLine->mFloaters) {
    mCurrentLine->mFloaters = new nsVoidArray();
  }
  mCurrentLine->mFloaters->AppendElement(aPlaceholder);
  // Now place the floater immediately if possible. Otherwise stash it
  // away in mPendingFloaters and place it later.
  if (mLineLayout->CanPlaceFloaterNow()) {
    nsRect combinedArea;
    nsMargin floaterMargins;
    mBlock->ReflowFloater(*this, aPlaceholder, combinedArea, floaterMargins);
    // 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);
    nsPoint origin;
    PlaceFloater(aPlaceholder, floaterMargins, &isLeftFloater, &origin);
    // Update the floater combined-area
    combinedArea.x += origin.x;
    combinedArea.y += origin.y;
    CombineRects(combinedArea, mFloaterCombinedArea);
    // Pass on updated available space to the current inline reflow engine
    GetAvailableSpace();
    mLineLayout->UpdateBand(mAvailSpaceRect.x + BorderPadding().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).
    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;
}
PRBool
nsBlockReflowState::CanPlaceFloater(const nsRect& aFloaterRect,
                                    PRUint8 aFloats)
{
  // If the current Y coordinate is not impacted by any floaters
  // then by definition the floater fits.
  PRBool result = PR_TRUE;
  if (0 != mBand.GetFloaterCount()) {
    if (mAvailSpaceRect.width < aFloaterRect.width) {
      // The available width is too narrow (and its been impacted by a
      // prior floater)
      result = PR_FALSE;
    }
    else {
      // At this point we know that there is enough horizontal space for
      // the floater (somewhere). Lets see if there is enough vertical
      // space.
      if (mAvailSpaceRect.height < aFloaterRect.height) {
        // The available height is too short. However, its possible that
        // there is enough open space below which is not impacted by a
        // floater.
        //
        // Compute the X coordinate for the floater based on its float
        // type, assuming its placed on the current line. This is
        // where the floater will be placed horizontally if it can go
        // here.
        nscoord xa;
        if (NS_STYLE_FLOAT_LEFT == aFloats) {
          xa = mAvailSpaceRect.x;
        }
        else {
          xa = mAvailSpaceRect.XMost() - aFloaterRect.width;
          // In case the floater is too big, don't go past the left edge
          if (xa < mAvailSpaceRect.x) {
            xa = mAvailSpaceRect.x;
          }
        }
        nscoord xb = xa + aFloaterRect.width;
        // Calculate the top and bottom y coordinates, again assuming
        // that the floater is placed on the current line.
        const nsMargin& borderPadding = BorderPadding();
        nscoord ya = mY - borderPadding.top;
        if (ya < 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?
          ya = 0;
        }
        nscoord yb = ya + aFloaterRect.height;
        nscoord saveY = mY;
        for (;;) {
          // Get the available space at the new Y coordinate
          mY += mAvailSpaceRect.height;
          GetAvailableSpace();
          if (0 == mBand.GetFloaterCount()) {
            // Winner. This band has no floaters on it, therefore
            // there can be no overlap.
            break;
          }
          // Check and make sure the floater won't intersect any
          // floaters on this band. The floaters starting and ending
          // coordinates must be entirely in the available space.
          if ((xa < mAvailSpaceRect.x) || (xb > mAvailSpaceRect.XMost())) {
            // The floater can't go here.
            result = PR_FALSE;
            break;
          }
          // See if there is now enough height for the floater.
          if (yb < mY + mAvailSpaceRect.height) {
            // Winner. The bottom Y coordinate of the floater is in
            // this band.
            break;
          }
        }
        // Restore Y coordinate and available space information
        // regardless of the outcome.
        mY = saveY;
        GetAvailableSpace();
      }
    }
  }
  return result;
}
void
nsBlockReflowState::PlaceFloater(nsPlaceholderFrame* aPlaceholder,
                                 const nsMargin& aFloaterMargins,
                                 PRBool* aIsLeftFloater,
                                 nsPoint* aNewOrigin)
{
  // 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->GetOutOfFlowFrame();
  // 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);
  // Adjust the floater size by its margin. That's the area that will
  // impact the space manager.
  region.width += aFloaterMargins.left + aFloaterMargins.right;
  region.height += aFloaterMargins.top + aFloaterMargins.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 if possible (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).
  // 
  // Note: The CSS2 spec says that floaters should be placed as high
  // as possible.
  while (!CanPlaceFloater(region, floaterDisplay->mFloats)) {
    mY += mAvailSpaceRect.height;
    GetAvailableSpace();
  }
  // Assign an x and y coordinate to the floater. Note that the x,y
  // coordinates are computed relative to the translation in the
  // spacemanager which means that the impacted region will be
  // inside 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;
  }
  const nsMargin& borderPadding = BorderPadding();
  region.y = mY - borderPadding.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 not relative to the spacemanager
  // translation, therefore we have to factor in our border/padding.
  nscoord x = borderPadding.left + aFloaterMargins.left + region.x;
  nscoord y = borderPadding.top + aFloaterMargins.top + region.y;
  floater->MoveTo(x, y);
  if (aNewOrigin) {
    aNewOrigin->x = x;
    aNewOrigin->y = y;
  }
  // Now restore mY
  mY = saveY;
#ifdef NOISY_INCREMENTAL_REFLOW
  if (mReflowState.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,
                                                  PRBool aReflowFloaters)
{
  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)) {
// XXX_perf
      // Before we can place it we have to reflow it
      nsRect combinedArea;
      nsMargin floaterMargins;
      mBlock->ReflowFloater(*this, placeholderFrame, combinedArea,
                            floaterMargins);
      PRBool isLeftFloater;
      nsPoint origin;
      PlaceFloater(placeholderFrame, floaterMargins, &isLeftFloater,
                   &origin);
      // Update the floater combined-area
      combinedArea.x += origin.x;
      combinedArea.y += origin.y;
      CombineRects(combinedArea, mFloaterCombinedArea);
    }
  }
}
/**
 * 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)) {
// XXX_perf
      // Before we can place it we have to reflow it
      nsRect combinedArea;
      nsMargin floaterMargins;
      mBlock->ReflowFloater(*this, placeholderFrame, combinedArea,
                            floaterMargins);
      PRBool isLeftFloater;
      nsPoint origin;
      PlaceFloater(placeholderFrame, floaterMargins, &isLeftFloater, &origin);
      // Update the floater combined-area
      combinedArea.x += origin.x;
      combinedArea.y += origin.y;
      CombineRects(combinedArea, mFloaterCombinedArea);
    }
  }
}
void
nsBlockReflowState::ClearFloaters(nscoord aY, PRUint8 aBreakType)
{
#ifdef NOISY_INCREMENTAL_REFLOW
  if (mReflowState.reason == eReflowReason_Incremental) {
    nsFrame::IndentBy(stdout, gNoiseIndent);
    printf("clear floaters: in: mY=%d aY=%d(%d)\n",
           mY, aY, aY - BorderPadding().top);
  }
#endif
  const nsMargin& bp = BorderPadding();
  nscoord newY = mBand.ClearFloaters(aY - bp.top, aBreakType);
  mY = newY + bp.top;
  GetAvailableSpace();
#ifdef NOISY_INCREMENTAL_REFLOW
  if (mReflowState.reason == eReflowReason_Incremental) {
    nsFrame::IndentBy(stdout, gNoiseIndent);
    printf("clear floaters: out: mY=%d(%d)\n", mY, mY - bp.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;
}
#ifdef NOISY_DAMAGE_REPAIR
static void ComputeCombinedArea(nsLineBox* aLine,
                                nscoord aWidth, nscoord aHeight,
                                nsRect& aResult)
{
  nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
  while (nsnull != aLine) {
    // Compute min and max x/y values for the reflowed frame's
    // combined areas
    nscoord x = aLine->mCombinedArea.x;
    nscoord y = aLine->mCombinedArea.y;
    nscoord xmost = x + aLine->mCombinedArea.width;
    nscoord ymost = y + aLine->mCombinedArea.height;
    if (x < xa) {
      xa = x;
    }
    if (xmost > xb) {
      xb = xmost;
    }
    if (y < ya) {
      ya = y;
    }
    if (ymost > yb) {
      yb = ymost;
    }
    aLine = aLine->mNext;
  }
  aResult.x = xa;
  aResult.y = ya;
  aResult.width = xb - xa;
  aResult.height = yb - ya;
}
#endif
NS_IMETHODIMP
nsBlockFrame::Paint(nsIPresContext&      aPresContext,
                    nsIRenderingContext& aRenderingContext,
                    const nsRect&        aDirtyRect,
                    nsFramePaintLayer    aWhichLayer)
{
#ifdef NOISY_DAMAGE_REPAIR
  if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
    PRInt32 depth = GetDepth();
    nsRect ca;
    ComputeCombinedArea(mLines, mRect.width, mRect.height, ca);
    nsFrame::IndentBy(stdout, depth);
    ListTag(stdout);
    printf(": bounds=%d,%d,%d,%d dirty=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
           mRect.x, mRect.y, mRect.width, mRect.height,
           aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height,
           ca.x, ca.y, ca.width, ca.height);
  }
#endif  
  const nsStyleDisplay* disp = (const nsStyleDisplay*)
    mStyleContext->GetStyleData(eStyleStruct_Display);
  // 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) {
    aRenderingContext.PushState();
    SetClipRect(aRenderingContext);
  }
  // Only paint the border and background if we're visible
  if (disp->mVisible && (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) &&
      (0 != mRect.width) && (0 != mRect.height)) {
    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);
  }
  // Child elements have the opportunity to override the visibility
  // property and display even if the parent is hidden
  if (NS_FRAME_PAINT_LAYER_FLOATERS == aWhichLayer) {
    PaintFloaters(aPresContext, aRenderingContext, aDirtyRect);
  }
  PaintChildren(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
  if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
    PRBool clipState;
    aRenderingContext.PopState(clipState);
  }
  return NS_OK;
}
void
nsBlockFrame::PaintFloaters(nsIPresContext& aPresContext,
                            nsIRenderingContext& aRenderingContext,
                            const nsRect& aDirtyRect)
{
  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->GetOutOfFlowFrame(), NS_FRAME_PAINT_LAYER_BACKGROUND);
      PaintChild(aPresContext, aRenderingContext, aDirtyRect,
                 ph->GetOutOfFlowFrame(), NS_FRAME_PAINT_LAYER_FLOATERS);
      PaintChild(aPresContext, aRenderingContext, aDirtyRect,
                 ph->GetOutOfFlowFrame(), NS_FRAME_PAINT_LAYER_FOREGROUND);
    }
  }
}
void
nsBlockFrame::PaintChildren(nsIPresContext& aPresContext,
                            nsIRenderingContext& aRenderingContext,
                            const nsRect& aDirtyRect,
                            nsFramePaintLayer aWhichLayer)
{
#ifdef NOISY_DAMAGE_REPAIR
  PRInt32 depth = 0;
  if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
    depth = GetDepth();
  }
#endif
  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 ((NS_FRAME_OUTSIDE_CHILDREN & mState) ||
        !((line->mCombinedArea.YMost() <= aDirtyRect.y) ||
          (line->mCombinedArea.y >= aDirtyRect.YMost()))) {
#ifdef NOISY_DAMAGE_REPAIR
      if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
        nsFrame::IndentBy(stdout, depth+1);
        printf("draw line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
               line, line->mBounds.x, line->mBounds.y,
               line->mBounds.width, line->mBounds.height,
               line->mCombinedArea.x, line->mCombinedArea.y,
               line->mCombinedArea.width, line->mCombinedArea.height);
      }
#endif
      nsIFrame* kid = line->mFirstChild;
      PRInt32 n = line->ChildCount();
      while (--n >= 0) {
        PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid,
                   aWhichLayer);
        kid->GetNextSibling(&kid);
      }
#ifdef DEBUG
      if (gShowDirtyLines) {
        if (line->WasDirty()) {
          aRenderingContext.SetColor(NS_RGB(128, 255, 128));
          aRenderingContext.DrawRect(line->mBounds);
        }
      }
#endif
    }
#ifdef NOISY_DAMAGE_REPAIR
    else {
      if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
        nsFrame::IndentBy(stdout, depth+1);
        printf("skip line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
               line, line->mBounds.x, line->mBounds.y,
               line->mBounds.width, line->mBounds.height,
               line->mCombinedArea.x, line->mCombinedArea.y,
               line->mCombinedArea.width, line->mCombinedArea.height);
      }
    }
#endif  
  }
  if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
    if ((nsnull != mBullet) && HaveOutsideBullet()) {
      // Paint outside bullets manually
      PaintChild(aPresContext, aRenderingContext, aDirtyRect, mBullet,
                 aWhichLayer);
    }
  }
}
NS_IMETHODIMP
nsBlockFrame::HandleEvent(nsIPresContext& aPresContext, 
                          nsGUIEvent*     aEvent,
                          nsEventStatus&  aEventStatus)
{
  return NS_OK;
}
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;
    }
  }
  return rv;
}
//////////////////////////////////////////////////////////////////////
// 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::Init(nsIPresContext&  aPresContext,
                   nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIStyleContext* aContext,
                   nsIFrame*        aPrevInFlow)
{
  if (aPrevInFlow) {
    // Copy over the block/area frame flags
    nsBlockFrame*  blockFrame = (nsBlockFrame*)aPrevInFlow;
    SetFlags(blockFrame->mFlags);
  }
  nsresult rv = nsBlockFrameSuper::Init(aPresContext, aContent, aParent,
                                        aContext, aPrevInFlow);
  if (nsBlockReflowContext::IsHTMLParagraph(this)) {
    mState |= NS_BLOCK_IS_HTML_PARAGRAPH;
  }
  return rv;
}
nsIStyleContext*
nsBlockFrame::GetFirstLetterStyle(nsIPresContext* aPresContext)
{
  nsIStyleContext* fls;
  aPresContext->ProbePseudoStyleContextFor(mContent,
                                           nsHTMLAtoms::firstLetterPseudo,
                                           mStyleContext, PR_FALSE, &fls);
  return fls;
}
nsIStyleContext*
nsBlockFrame::GetFirstLineStyle(nsIPresContext* aPresContext)
{
  nsIStyleContext* fls;
  aPresContext->ProbePseudoStyleContextFor(mContent,
                                           nsHTMLAtoms::firstLinePseudo,
                                           mStyleContext, PR_FALSE, &fls);
  return fls;
}
NS_IMETHODIMP
nsBlockFrame::SetInitialChildList(nsIPresContext& aPresContext,
                                  nsIAtom*        aListName,
                                  nsIFrame*       aChildList)
{
  nsresult rv = NS_OK;
  if (nsLayoutAtoms::floaterList == aListName) {
    mFloaters.SetFrames(aChildList);
  }
  else {
    // Lookup up the two pseudo style contexts
    if (nsnull == mPrevInFlow) {
      nsIStyleContext* firstLetterStyle = GetFirstLetterStyle(&aPresContext);
      if (nsnull != firstLetterStyle) {
        mState |= NS_BLOCK_HAS_FIRST_LETTER_STYLE;
#ifdef NOISY_FIRST_LETTER
        ListTag(stdout);
        printf(": first-letter style found\n");
#endif
        NS_RELEASE(firstLetterStyle);
      }
      nsIStyleContext* firstLineStyle = GetFirstLineStyle(&aPresContext);
      if (nsnull != firstLineStyle) {
        mState |= NS_BLOCK_HAS_FIRST_LINE_STYLE;
#ifdef NOISY_FIRST_LINE
        ListTag(stdout);
        printf(": first-line style found\n");
#endif
        NS_RELEASE(firstLineStyle);
      }
    }
    rv = AddFrames(&aPresContext, aChildList, nsnull);
    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;
      aPresContext.ResolvePseudoStyleContextFor(mContent, 
                                             nsHTMLAtoms::mozListBulletPseudo,
                                             mStyleContext, PR_FALSE, &kidSC);
      // Create bullet frame
      mBullet = new nsBulletFrame;
      if (nsnull == mBullet) {
        NS_RELEASE(kidSC);
        return NS_ERROR_OUT_OF_MEMORY;
      }
      mBullet->Init(aPresContext, mContent, this, kidSC, nsnull);
      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) {
        AddFrames(&aPresContext, mBullet, nsnull);
        mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
      }
      else {
        mState |= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
      }
    }
  }
  return NS_OK;
}
void
nsBlockFrame::RenumberLists()
{
  // 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);
  }
  // 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) {
            ordinal =
              listItem->mBullet->SetListItemOrdinal(ordinal);
          }
        }
      }
      frame->GetNextSibling(&frame);
    }
    block = (nsBlockFrame*) block->mNextInFlow;
  }
}
void
nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
                           nsHTMLReflowMetrics& aMetrics)
{
  // Reflow the bullet now
  nsSize availSize;
  availSize.width = NS_UNCONSTRAINEDSIZE;
  availSize.height = NS_UNCONSTRAINEDSIZE;
  nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState,
                                mBullet, availSize);
  reflowState.lineLayout = aState.mLineLayout;
  nsIHTMLReflow* htmlReflow;
  nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
  if (NS_SUCCEEDED(rv)) {
    nsReflowStatus  status;
    htmlReflow->WillReflow(*aState.mPresContext);
    htmlReflow->Reflow(*aState.mPresContext, aMetrics, 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
  const nsMargin& bp = aState.BorderPadding();
  nscoord x = bp.left - reflowState.computedMargin.right - aMetrics.width;
  // Approximate the bullets position; vertical alignment will provide
  // the final vertical location.
  nscoord y = bp.top;
  mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
}
//XXX get rid of this -- its slow
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->GetOutOfFlowFrame();
        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
// XXX Switch to an interface to pass to child frames -or- do the
// grovelling directly ourselves?
nsresult
nsBlockFrame::ComputeTextRuns(nsIPresContext* aPresContext)
{
  // Destroy old run information first
  nsTextRun::DeleteTextRuns(mTextRuns);
  mTextRuns = nsnull;
  nsLineLayout textRunThingy(*aPresContext);
  // Ask each child to find its text runs
  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(textRunThingy);
          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.
          textRunThingy.EndTextRun();
        }
        frame->GetNextSibling(&frame);
      }
    }
    else {
      // A block frame isn't text therefore it will end an open text
      // run.
      textRunThingy.EndTextRun();
    }
    line = line->mNext;
  }
  textRunThingy.EndTextRun();
  // Now take the text-runs away from the line layout engine.
  mTextRuns = textRunThingy.TakeTextRuns();
  return NS_OK;
}
//----------------------------------------------------------------------
nsresult
NS_NewAnonymousBlockFrame(nsIFrame** aNewFrame)
{
  NS_PRECONDITION(aNewFrame, "null OUT ptr");
  if (nsnull == aNewFrame) {
    return NS_ERROR_NULL_POINTER;
  }
  nsAnonymousBlockFrame* 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->GetOutOfFlowFrame());
          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;
      NS_ASSERTION(line->mChildCount < 10000, "bad inline count"); 
      line->MarkDirty();
      firstChild->GetNextSibling(&line->mFirstChild);
    }
    // Break linkage to next child after stolen frame
    firstChild->SetNextSibling(nsnull);
  }
#ifdef DEBUG
  VerifyLines(PR_TRUE);
#endif
}
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;
    }
  }
#ifdef DEBUG
  VerifyLines(PR_TRUE);
#endif
}
#ifdef DEBUG
void
nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
{
  nsLineBox* line = mLines;
  if (!line) {
    return;
  }
  // Add up the counts on each line. Also validate that IsFirstLine is
  // set properly.
  PRInt32 count = 0;
  PRBool seenBlock = PR_FALSE;
  while (nsnull != line) {
    if (aFinalCheckOK) {
      if (line->IsBlock()) {
        seenBlock = PR_TRUE;
      }
      if (line->IsFirstLine() || line->IsBlock()) {
        NS_ASSERTION(1 == line->mChildCount, "bad first line");
      }
      if (NS_BLOCK_HAS_FIRST_LINE_STYLE & mState) {
        if (seenBlock) {
          NS_ASSERTION(!line->IsFirstLine(), "bad first line");
        }
        else {
          NS_ASSERTION(line->IsFirstLine(), "bad first line");
        }
      }
    }
    NS_ASSERTION(line->mChildCount < 10000, "bad line child count");
    count += line->mChildCount;
    line = line->mNext;
  }
  // Then count the frames
  PRInt32 frameCount = 0;
  nsIFrame* frame = mLines->mFirstChild;
  while (nsnull != frame) {
    frameCount++;
    frame->GetNextSibling(&frame);
  }
  NS_ASSERTION(count == frameCount, "bad line list");
  // Next: test that each line has right number of frames on it
  line = mLines;
  nsLineBox* prevLine = nsnull;
  while (nsnull != line) {
    count = line->mChildCount;
    frame = line->mFirstChild;
    while (--count >= 0) {
      frame->GetNextSibling(&frame);
    }
    prevLine = line;
    line = line->mNext;
    if ((nsnull != line) && (0 != line->mChildCount)) {
      NS_ASSERTION(frame == line->mFirstChild, "bad line list");
    }
  }
}
// Its possible that a frame can have some frames on an overflow
// list. But its never possible for multiple frames to have overflow
// lists. Check that this fact is actually true.
void
nsBlockFrame::VerifyOverflowSituation()
{
  PRBool haveOverflow = PR_FALSE;
  nsBlockFrame* flow = (nsBlockFrame*) GetFirstInFlow();
  while (nsnull != flow) {
    if (nsnull != flow->mOverflowLines) {
      NS_ASSERTION(nsnull != flow->mOverflowLines->mFirstChild,
                   "bad overflow list");
      NS_ASSERTION(!haveOverflow, "two frames with overflow lists");
      haveOverflow = PR_TRUE;
    }
    flow = (nsBlockFrame*) flow->mNextInFlow;
  }
}
PRInt32
nsBlockFrame::GetDepth() const
{
  PRInt32 depth = 0;
  nsIFrame* parent = mParent;
  while (nsnull != parent) {
    parent->GetParent(&parent);
    depth++;
  }
  return depth;
}
#endif