diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 59d7407ec5b1..0228ca80d413 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -16,66 +16,37 @@ * Reserved. */ #include "nsBlockFrame.h" -#include "nsSize.h" -#include "nsIAnchoredItems.h" -#include "nsIContent.h" -#include "nsIContentDelegate.h" -#include "nsISpaceManager.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" -#include "nsIPresContext.h" -#include "nsMargin.h" -#include "nsHTMLIIDs.h" -#include "nsCSSLayout.h" -#include "nsCRT.h" -#include "nsIPresShell.h" -#include "nsReflowCommand.h" -#include "nsPlaceholderFrame.h" -#include "nsHTMLAtoms.h" -#include "nsHTMLValue.h" #include "nsIHTMLContent.h" -#include "nsAbsoluteFrame.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIAnchoredItems.h" +#include "nsPlaceholderFrame.h" #include "nsIPtr.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLIIDs.h" +#include "nsHTMLValue.h" -#ifdef NS_DEBUG -#undef NOISY -#undef NOISY_FLOW -#else -#undef NOISY -#undef NOISY_FLOW -#endif +// XXX what do we do with catastrophic errors (rv < 0)? What is the +// state of the reflow world after such an error? -static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); -static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); -static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +#undef NOISY_REFLOW static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID); -static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID); -static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID); -static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID); static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID); -NS_DEF_PTR(nsIStyleContext); +static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); +static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); + NS_DEF_PTR(nsIContent); +NS_DEF_PTR(nsIStyleContext); -struct BlockBandData : public nsBandData { - nsBandTrapezoid data[12]; +//---------------------------------------------------------------------- - // Bounding rect of available space between any left and right floaters - nsRect availSpace; - // Constructor - BlockBandData() {size = 12; trapezoids = data;} - - // Computes the bounding rect of the available space, i.e. space between - // any left and right floaters - // - // Uses the current trapezoid data, see nsISpaceManager::GetBandData(). - // Updates member data "availSpace" - void ComputeAvailSpaceRect(); -}; - -void BlockBandData::ComputeAvailSpaceRect() +void nsBlockBandData::ComputeAvailSpaceRect() { nsBandTrapezoid* trapezoid = data; @@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect() } } -// XXX Bugs -// 1. right to left reflow can generate negative x coordinates. - -// XXX Speedup idea (all containers) - -// If I reflow a child and it gives back not-complete status then -// there is no sense in trying to pullup children. For blocks, it's a -// little more complicated unless the child is a block - if the child -// is a block, then we must be out of room hence we should stop. If -// the child is not a block then our line should be flushed (see #2 -// below) if our line is already empty then we must be out of room. - -// For inline frames and column frames, if we reflow a child and get -// back not-complete status then we should bail immediately because we -// are out of room. - -// XXX Speedup ideas: -// 1. change pullup code to use line information from next in flow -// 2. we can advance to next line immediately after reflowing something -// and noticing that it's not complete. -// 3. pass down last child information in aState so that pullup, etc., -// don't need to recompute it - -// XXX TODO: -// 0. Move justification into line flushing code - -// 1. To get ebina margins I need "auto" information from the style -// system margin's. A bottom/top margin of auto will then be computed like -// ebina computes it [however the heck that is]. - -// 2. kicking out floaters and talking with floater container to adjust -// left and right margins +//---------------------------------------------------------------------- nsBlockReflowState::nsBlockReflowState() { } -void nsBlockReflowState::Init(const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsIStyleContext* aBlockSC, - nsISpaceManager* aSpaceManager) +nsBlockReflowState::~nsBlockReflowState() { - firstLine = PR_TRUE; - allowLeadingWhitespace = PR_FALSE; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - firstChildIsInsideBullet = PR_FALSE; - nextListOrdinal = -1; - column = 0; +} - spaceManager = aSpaceManager; - currentBand = new BlockBandData; +nsresult +nsBlockReflowState::Initialize(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockFrame* aBlock) +{ + nsresult rv = NS_OK; - styleContext = aBlockSC; - styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID); - styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID); - styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID); + mPresContext = aPresContext; + mBlock = aBlock; + mSpaceManager = aSpaceManager; + mBlockIsPseudo = aBlock->IsPseudoFrame(); + mCurrentLine = nsnull; + mPrevKidFrame = nsnull; - justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) && - (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace); - - availSize.width = aMaxSize.width; - availSize.height = aMaxSize.height; - maxElementSize = aMaxElementSize; + mX = 0; + mY = 0; + mAvailSize = aMaxSize; + mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE); + mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE); + mMaxElementSizePointer = aMaxElementSize; if (nsnull != aMaxElementSize) { aMaxElementSize->width = 0; aMaxElementSize->height = 0; } + mKidXMost = 0; - kidXMost = 0; - x = 0; - y = 0; + mPrevMaxPosBottomMargin = 0; + mPrevMaxNegBottomMargin = 0; - isInline = PR_FALSE; - currentLineNumber = 0; - lineStart = nsnull; - lineLength = 0; - ascents = ascentBuf; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; - lineMaxElementSize.width = 0; - lineMaxElementSize.height = 0; - lastContentIsComplete = PR_TRUE; + mNextListOrdinal = -1; + mFirstChildIsInsideBullet = PR_FALSE; - maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]); - needRelativePos = PR_FALSE; - - prevLineLastFrame = nsnull; - prevLineHeight = 0; - topMargin = 0; - prevMaxPosBottomMargin = 0; - prevMaxNegBottomMargin = 0; - prevLineLastContentIsComplete = PR_TRUE; - - unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE); - unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE); - - reflowStatus = nsIFrame::frNotComplete; + return rv; } -nsBlockReflowState::~nsBlockReflowState() -{ - if (ascents != ascentBuf) { - delete ascents; - } - delete currentBand; -} - -void nsBlockReflowState::AddAscent(nscoord aAscent) -{ - NS_PRECONDITION(lineLength <= maxAscents, "bad line length"); - if (lineLength == maxAscents) { - maxAscents *= 2; - nscoord* newAscents = new nscoord[maxAscents]; - if (nsnull != newAscents) { - nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength); - if (ascents != ascentBuf) { - delete ascents; - } - ascents = newAscents; - } else { - // Yikes! Out of memory! - return; - } - } - ascents[lineLength] = aAscent; -} - -void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame, - nscoord aPrevLineHeight) -{ - firstLine = PR_FALSE; - allowLeadingWhitespace = PR_FALSE; - column = 0; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - lineStart = nsnull; - lineLength = 0; - currentLineNumber++; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - needRelativePos = PR_FALSE; - - prevLineLastFrame = aPrevLineLastFrame; - prevLineHeight = aPrevLineHeight; - prevMaxPosBottomMargin = maxPosBottomMargin; - prevMaxNegBottomMargin = maxNegBottomMargin; - - // Remember previous line's lastContentIsComplete - prevLineLastContentIsComplete = lastContentIsComplete; - lastContentIsComplete = PR_TRUE; - - topMargin = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; -} - -#ifdef NS_DEBUG -void nsBlockReflowState::DumpLine() -{ - nsIFrame* f = lineStart; - PRInt32 ll = lineLength; - while (--ll >= 0) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} - -void nsBlockReflowState::DumpList() -{ - nsIFrame* f = lineStart; - while (nsnull != f) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} -#endif - //---------------------------------------------------------------------- -nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, - nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) +nsresult +nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + PRInt32 aIndexInParent, + nsIFrame* aParent) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); if (nsnull == aInstancePtrResult) { @@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, } nsBlockFrame::nsBlockFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) + PRInt32 aIndexInParent, + nsIFrame* aParent) : nsHTMLContainerFrame(aContent, aIndexInParent, aParent) { } nsBlockFrame::~nsBlockFrame() { - if (nsnull != mLines) { - delete mLines; - } + DestroyLines(); } -nsresult +void nsBlockFrame::DestroyLines() +{ +} + +NS_METHOD nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) { NS_PRECONDITION(0 != aInstancePtr, "null ptr"); if (NULL == aInstancePtr) { return NS_ERROR_NULL_POINTER; } - if (aIID.Equals(kIHTMLFrameTypeIID)) { - *aInstancePtr = (void*) ((nsIHTMLFrameType*) this); + if (aIID.Equals(kBlockFrameCID)) { + *aInstancePtr = (void*) (this); return NS_OK; - } else if (aIID.Equals(kIRunaroundIID)) { + } + else if (aIID.Equals(kIRunaroundIID)) { *aInstancePtr = (void*) ((nsIRunaround*) this); return NS_OK; - } else if (aIID.Equals(kIFloaterContainerIID)) { + } + else if (aIID.Equals(kIFloaterContainerIID)) { *aInstancePtr = (void*) ((nsIFloaterContainer*) this); return NS_OK; } return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr); } -// Computes the top margin to use for this child frames based on its display -// type and the display type of the previous child frame. -// -// Adjacent vertical margins between block-level elements are collapsed. -nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC, - PRBool aIsInline) +NS_METHOD +nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const { - if (aIsInline) { - // Just use whatever the previous bottom margin was - return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin; + aIsSplittable = frSplittableNonRectangular; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, + nsIFrame* aParent, + nsIFrame*& aContinuingFrame) +{ + nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); + PrepareContinuingFrame(aCX, aParent, cf); + aContinuingFrame = cf; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::ListTag(FILE* out) const +{ + if ((nsnull != mGeometricParent) && IsPseudoFrame()) { + fprintf(out, "*block<"); + nsIAtom* atom = mContent->GetTag(); + if (nsnull != atom) { + nsAutoString tmp; + atom->ToString(tmp); + fputs(tmp, out); + } + fprintf(out, ">(%d)@%p", mIndexInParent, this); } else { - // Does the frame have a prev-in-flow? - nsIFrame* kidPrevInFlow; + nsHTMLContainerFrame::ListTag(out); + } + return NS_OK; +} - aKidFrame->GetPrevInFlow(kidPrevInFlow); +NS_METHOD +nsBlockFrame::List(FILE* out, PRInt32 aIndent) const +{ + // Indent + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); - if (nsnull == kidPrevInFlow) { + // Output the tag + ListTag(out); + + // Output the first/last content offset + fprintf(out, "[%d,%d,%c] pif=%p nif=%p", + mFirstContentOffset, mLastContentOffset, + (mLastContentIsComplete ? 'T' : 'F'), + mPrevInFlow, mNextInFlow); + + // Output the rect + out << mRect; + + // Output the children, one line at a time + if (nsnull != mLines) { + fputs("<\n", out); + aIndent++; + + nsLineData* line = mLines; + while (nsnull != line) { + line->List(out, aIndent); + line = line->mNextLine; + } + + aIndent--; + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); + fputs(">\n", out); + } else { + fputs("<>\n", out); + } + + return NS_OK; +} + +NS_METHOD +nsBlockFrame::VerifyTree() const +{ + nsresult rv = nsHTMLContainerFrame::VerifyTree(); + if (NS_OK != rv) { + return rv; + } + rv = VerifyLines(PR_TRUE); + return rv; +} + +nsresult +nsBlockFrame::VerifyLines(PRBool aFinalCheck) const +{ + nsresult rv = NS_OK; + + // Make sure that the list of children agrees with our child count. + // If this is not the case then the child list and the line list are + // not properly arranged. + PRInt32 len = LengthOf(mFirstChild); + NS_ASSERTION(mChildCount == len, "bad child list"); + + // Verify that our lines are correctly setup + PRInt32 offset = mFirstContentOffset; + PRInt32 lineChildCount = 0; + nsLineData* line = mLines; + nsLineData* prevLine = nsnull; + while (nsnull != line) { + if (aFinalCheck) { + NS_ASSERTION(((offset == line->mFirstContentOffset) && + (line->mFirstContentOffset <= line->mLastContentOffset)), + "bad line mFirstContentOffset"); + NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset, + "bad line mLastContentOffset"); + offset = line->mLastContentOffset; + if (line->mLastContentIsComplete) { + offset++; + } + } + lineChildCount += line->mChildCount; + rv = line->Verify(aFinalCheck); + if (NS_OK != rv) { + return rv; + } + prevLine = line; + line = line->mNextLine; + } + if (aFinalCheck && (nsnull != prevLine)) { + NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset, + "bad mLastContentOffset"); + NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete, + "bad mLastContentIsComplete"); + } + NS_ASSERTION(lineChildCount == mChildCount, "bad line counts"); + + return rv; +} + +//---------------------------------------------------------------------- + +// Remove a next-in-flow from from this block's list of lines + +// XXX problems here: +// 1. we always have to start from the first line: slow! +// 2. we can avoid this when the child is not the last child in a line + +void +nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow) +{ + // When a reflow indicates completion it's possible that + // next-in-flows were just removed. We have to remove them from any + // nsLineData's that follow the current line. + nsLineData* line = mLines; + while (nsnull != line) { + if (line->mFirstChild == aNextInFlow) { + // Remove child from line. + if (0 == --line->mChildCount) { + line->mFirstChild = nsnull; + } + else { + // Fixup the line + nsIFrame* nextKid; + aNextInFlow->GetNextSibling(nextKid); + line->mFirstChild = nextKid; + nextKid->GetIndexInParent(line->mFirstContentOffset); + } + break; + } + line = line->mNextLine; + } +} + +nsresult +nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize, + aMaxElementSize); + return NS_OK; +} + +nsresult +nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager, + aMaxSize, aDesiredRect, aMaxElementSize); + return NS_OK; +} + +nsLineData* +nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList) +{ + nsLineData* newLine = new nsLineData(); + if (nsnull != newLine) { + nsIFrame* kid = aOverflowList; + newLine->mFirstChild = kid; + kid->GetIndexInParent(newLine->mFirstContentOffset); + newLine->mLastContentOffset = -1; + newLine->mLastContentIsComplete = PRPackedBool(0x255); + PRInt32 kids = 0; + while (nsnull != kid) { + kids++; + kid->GetNextSibling(kid); + } + newLine->mChildCount = kids; + } + return newLine; +} + +void +nsBlockFrame::DrainOverflowList() +{ + nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow; + if (nsnull != prevBlock) { + nsIFrame* overflowList = prevBlock->mOverflowList; + if (nsnull != overflowList) { + NS_ASSERTION(nsnull == mFirstChild, "bad overflow list"); + NS_ASSERTION(nsnull == mLines, "bad overflow list"); + + // Create a line to hold the entire overflow list + nsLineData* newLine = CreateLineForOverflowList(overflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(overflowList); + prevBlock->mOverflowList = nsnull; + + // The new line is the first line + mLines = newLine; + } + } + + if (nsnull != mOverflowList) { + NS_ASSERTION(nsnull != mFirstChild, + "overflow list but no mapped children"); + + // Create a line to hold the overflow list + nsLineData* newLine = CreateLineForOverflowList(mOverflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(mOverflowList, PR_FALSE); + mOverflowList = nsnull; + + // The new line is appended after our other lines + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + while (nsnull != line) { + prevLine = line; + line = line->mNextLine; + } + if (nsnull == prevLine) { + mLines = newLine; + } + else { + prevLine->mNextLine = newLine; + newLine->mPrevLine = prevLine; + } + } +#ifdef NS_DEBUG + VerifyLines(PR_FALSE); +#endif +} + +// XXX add in code here that notices if margin's were not provided by +// the style system and when that is the case to apply the old layout +// engines margin calculations. + +nsresult +nsBlockFrame::PlaceLine(nsBlockReflowState& aState, + nsLineLayout& aLineLayout, + nsLineData* aLine) +{ + nsresult rv = NS_LINE_LAYOUT_COMPLETE; + + nscoord topMargin = 0; + nscoord bottomMargin = 0; + nscoord maxNegBottomMargin = 0; + nscoord maxPosBottomMargin = 0; + + // See if block margins apply to this line or not + PRBool isBlockLine = PR_FALSE; + if (1 == aLine->mChildCount) { + nsIStyleContextPtr kidSC; + nsIFrame* kid = aLine->mFirstChild; + rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef()); + if (NS_OK != rv) return rv; + + nsStyleDisplay* display = (nsStyleDisplay*) + kidSC->GetData(kStyleDisplaySID); + switch (display->mDisplay) { + case NS_STYLE_DISPLAY_BLOCK: + case NS_STYLE_DISPLAY_LIST_ITEM: + isBlockLine = PR_TRUE; + nsStyleSpacing* spacing = (nsStyleSpacing*) + kidSC->GetData(kStyleSpacingSID); + + // Calculate top margin by collapsing with previous bottom margin + // if any. nscoord maxNegTopMargin = 0; nscoord maxPosTopMargin = 0; - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - if (ss->mMargin.top < 0) { - maxNegTopMargin = -ss->mMargin.top; + if (spacing->mMargin.top < 0) { + maxNegTopMargin = -spacing->mMargin.top; } else { - maxPosTopMargin = ss->mMargin.top; + maxPosTopMargin = spacing->mMargin.top; } - - nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin); - nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin); - return maxPos - maxNeg; - } else { - return 0; + nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin); + nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin); + topMargin = maxPos - maxNeg; + + // Save away bottom information for later promotion into aState + if (spacing->mMargin.bottom < 0) { + maxNegBottomMargin = -spacing->mMargin.bottom; + } else { + maxPosBottomMargin = spacing->mMargin.bottom; + } + break; } } + + // Before we move the line, make sure that it will fit in it's new + // location. It always fits if the height isn't constrained or it's + // the first line. + nscoord totalHeight = topMargin + aLine->mBounds.height; + if (!aState.mUnconstrainedHeight && (aLine != mLines)) { + if (aState.mY + totalHeight > aState.mAvailSize.height) { + // The line will not fit + rv = PushLines(aState, aLine); + goto done; + } + } + if (isBlockLine) { + if (0 != topMargin) { + // We have to move the line now that we know the top margin + // for it. + aLine->MoveLineBy(0, topMargin); + aState.mY += topMargin; + } + } + else { + // Apply previous line's bottom margin before the inline-line. + nscoord bottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (0 != bottomMargin) { + aLine->MoveLineBy(0, bottomMargin); + aState.mY += topMargin; + } + } + + // Consume space and advance running values + aState.mY += aLine->mBounds.height; + aState.mPrevMaxNegBottomMargin = maxNegBottomMargin; + aState.mPrevMaxPosBottomMargin = maxPosBottomMargin; + if (nsnull != aState.mMaxElementSizePointer) { + nsSize* maxSize = aState.mMaxElementSizePointer; + if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) { + maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width; + } + if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) { + maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height; + } + } + { + nscoord xmost = aLine->mBounds.XMost(); + if (xmost > aState.mKidXMost) { + aState.mKidXMost = xmost; + } + } + + // Any below current line floaters to place? + if (aState.mPendingFloaters.Count() > 0) { + PlaceBelowCurrentLineFloaters(aState, aState.mY); + // XXX Factor in the height of the floaters as well when considering + // whether the line fits. + // The default policy is that if there isn't room for the floaters then + // both the line and the floaters are pushed to the next-in-flow... + } + + if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) { + // The current y coordinate is now past our available space + // rectangle. Get a new band of space. + GetAvailableSpace(aState, aState.mY); + } + +done: + return rv; } -void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX, - nsBlockReflowState& aState, - nscoord aY) +// aY has borderpadding.top already factored in +// aResult is relative to left,aY +nsresult +nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY) { - NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters"); + nsresult rv = NS_OK; - // XXX Factor this code with PlaceFloater()... - PRInt32 numFloaters = aState.floaterToDo.Count(); - - for (PRInt32 i = 0; i < numFloaters; i++) { - nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i]; - nsRect region; + nsISpaceManager* sm = aState.mSpaceManager; - // Get the band of available space - // XXX This is inefficient to do this inside the loop... - GetAvailableSpaceBand(aState, aY); + // Fill in band data for the specific Y coordinate + sm->Translate(aState.mBorderPadding.left, 0); + sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand); + sm->Translate(-aState.mBorderPadding.left, 0); - // Get the type of floater - nsIStyleContextPtr styleContext; - - floater->GetStyleContext(aCX, styleContext.AssignRef()); - nsStyleDisplay* sd = (nsStyleDisplay*) - styleContext->GetData(kStyleDisplaySID); - - floater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; + // Compute the bounding rect of the available space, i.e. space + // between any left and right floaters + aState.mCurrentBand.ComputeAvailSpaceRect(); - if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; - } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; - } - - // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, - 0); - mCurrentState->spaceManager->AddRectRegion(region, floater); - - // Set the origin of the floater in world coordinates - nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); - floater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); - } - aState.floaterToDo.Clear(); -} - -/** - * Flush a line out. Return true if the line fits in our available - * height. If the line does not fit then return false. When the line - * fits we advance the y coordinate, reset the x coordinate and - * prepare the nsBlockReflowState for the next line. - */ -PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - NS_PRECONDITION(aState.lineLength > 0, "bad line"); - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nscoord y = aState.y + aState.topMargin; - nscoord lineHeight; - - if (aState.isInline) { - // Vertically align the children on this line, returning the height of - // the line upon completion. - lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this, - aState.styleFont, - y, - aState.lineStart, - aState.lineLength, - aState.ascents, - aState.maxAscent); - - // Any below current line floaters to place? - if (aState.floaterToDo.Count() > 0) { - PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight); - // XXX Factor in the height of the floaters as well when considering - // whether the line fits. - // The default policy is that if there isn't room for the floaters then - // both the line and the floaters are pushed to the next-in-flow... - } - } else { - nsSize size; - - aState.lineStart->GetSize(size); - lineHeight = size.height; - } - - // The first line always fits - if (aState.currentLineNumber > 0) { - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if (y + lineHeight > yb) { - // After vertical alignment of the children and factoring in the - // proper margin, the line doesn't fit. - return PR_FALSE; - } - } - - if (aState.isInline) { - // Check if the right-edge of the line exceeds our running x-most - nscoord xMost = aState.borderPadding.left + aState.lineWidth; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - } - - // Advance the y coordinate to the new position where the next - // line or block element will go. - aState.y = y + lineHeight; - aState.x = 0; - - // Now that the vertical alignment is done we can perform horizontal - // alignment and relative positioning. Skip all of these if we are - // doing an unconstrained (in x) reflow. There's no point in doing - // the work if we *know* we are going to reflowed again. - if (!aState.unconstrainedWidth) { - nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText, - aState.lineStart, aState.lineLength, - aState.lineWidth, - aState.availSize.width); - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - if (!aState.justifying) { - nsCSSLayout::RelativePositionChildren(aCX, this, - aState.lineStart, - aState.lineLength); - } - } - - // Record line length - aState.lineLengths.AppendElement((void*)aState.lineLength); - - // Find the last frame in the line - // XXX keep this as running state in the nsBlockReflowState - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - // Update maxElementSize - if (nsnull != aState.maxElementSize) { - nsSize& lineMax = aState.lineMaxElementSize; - nsSize* maxMax = aState.maxElementSize; - if (lineMax.width > maxMax->width) { - maxMax->width = lineMax.width; - } - if (lineMax.height > maxMax->height) { - maxMax->height = lineMax.height; - } - aState.lineMaxElementSize.width = 0; - aState.lineMaxElementSize.height = 0; - } - - // Advance to the next line - aState.AdvanceToNextLine(lastFrame, lineHeight); - - return PR_TRUE; -} - -/** - * Add an inline child to the current line. Advance various running - * values after placement. - */ -void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsReflowMetrics& aKidSize, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) -{ - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; - } - - // Place and size the child - // XXX add in left margin from kid - nsRect r; - r.y = aState.y; - r.width = aKidSize.width; - r.height = aKidSize.height; - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - // Left to right positioning. - r.x = aState.borderPadding.left + aState.x + ss->mMargin.left; - aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right; - } else { - // Right to left positioning - // XXX what should we do when aState.x goes negative??? - r.x = aState.x - aState.borderPadding.right - ss->mMargin.right - - aKidSize.width; - aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left; - } - aKidFrame->SetRect(r); - aState.AddAscent(aKidSize.ascent); - aState.lineWidth += aKidSize.width; - aState.lineLength++; - - // Update maximums for the line - if (aKidSize.ascent > aState.maxAscent) { - aState.maxAscent = aKidSize.ascent; - } - if (aKidSize.descent > aState.maxDescent) { - aState.maxDescent = aKidSize.descent; - } - // Update running margin maximums - if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) { - // XXX temporary code. Since the molecule for the bullet frame - // is the same as the LI frame, we get bad style information. - // ignore it. - } else { - nscoord margin; #if 0 - // XXX CSS2 spec says that top/bottom margin don't affect line height - // calculation. We're waiting for clarification on this issue... - if ((margin = ss->mMargin.top) < 0) { - margin = -margin; - if (margin > aState.maxNegTopMargin) { - aState.maxNegTopMargin = margin; - } - } else { - if (margin > aState.maxPosTopMargin) { - aState.maxPosTopMargin = margin; - } - } + // XXX For now we assume that there are no height restrictions + // (e.g. no "float to bottom of column/page") + nsRect& availSpace = aState.mCurrentBand.availSpace; + aResult.x = availSpace.x; + aResult.y = availSpace.y; + aResult.width = availSpace.width; + aResult.height = aState.mAvailSize.height; +#else + aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left, + aState.mY); #endif - if ((margin = ss->mMargin.bottom) < 0) { - margin = -margin; - if (margin > aState.maxNegBottomMargin) { - aState.maxNegBottomMargin = margin; - } - } else { - if (margin > aState.maxPosBottomMargin) { - aState.maxPosBottomMargin = margin; - } - } - } - // Update line max element size - nsSize& mes = aState.lineMaxElementSize; - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > mes.width) { - mes.width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > mes.height) { - mes.height = aKidMaxElementSize->height; - } - } + return rv; } -// Places and sizes the block-level element, and advances the line. -// The rect is in the local coordinate space of the kid frame. -void nsBlockFrame::AddBlockChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsRect& aKidRect, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) +// Give aLine and any successive lines to the block's next-in-flow; if +// we don't have a next-in-flow then push all the children onto our +// overflow list. +nsresult +nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine) { - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); + PRInt32 i; - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; + // Split our child-list in two; revert our last content offset and + // completion status to the previous line. + nsLineData* prevLine = aLine->mPrevLine; + NS_PRECONDITION(nsnull != prevLine, "pushing first line"); + nsIFrame* prevKidFrame = prevLine->mFirstChild; + for (i = prevLine->mChildCount - 1; --i >= 0; ) { + prevKidFrame->GetNextSibling(prevKidFrame); } - - // Translate from the kid's coordinate space to our coordinate space - aKidRect.x += aState.borderPadding.left + ss->mMargin.left; - aKidRect.y += aState.y + aState.topMargin; - - // Place and size the child - aKidFrame->SetRect(aKidRect); - - aState.AddAscent(aKidRect.height); - aState.lineLength++; - - // Is this the widest child frame? - nscoord xMost = aKidRect.XMost() + ss->mMargin.right; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - - // Update the max element size - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > aState.maxElementSize->width) { - aState.maxElementSize->width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > aState.maxElementSize->height) { - aState.maxElementSize->height = aKidMaxElementSize->height; - } - } - - // and the bottom line margin information which we'll use when placing - // the next child - if (ss->mMargin.bottom < 0) { - aState.maxNegBottomMargin = -ss->mMargin.bottom; - } else { - aState.maxPosBottomMargin = ss->mMargin.bottom; - } - - // Update the running y-offset - aState.y += aKidRect.height + aState.topMargin; - - // Apply relative positioning if necessary - nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1); - - // Advance to the next line - aState.AdvanceToNextLine(aKidFrame, aKidRect.height); -} - -/** - * Compute the available size for reflowing the given child at the - * current x,y position in the state. Note that this may return - * negative or zero width/height's if we are out of room. - */ -void nsBlockFrame::GetAvailSize(nsSize& aResult, - nsBlockReflowState& aState, - nsIStyleContext* aKidSC, - PRBool aIsInline) -{ - // Determine the maximum available reflow height for the child - nscoord yb = aState.borderPadding.top + aState.availSize.height; - aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE : - yb - aState.y - aState.topMargin; - - // Determine the maximum available reflow width for the child - if (aState.unconstrainedWidth) { - aResult.width = NS_UNCONSTRAINEDSIZE; - } else if (aIsInline) { - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - aResult.width = aState.currentBand->availSpace.XMost() - aState.x; - } else { - aResult.width = aState.x - aState.currentBand->availSpace.x; - } - } else { - // It's a block. Don't adjust for the left/right margin here. That happens - // later on once we know the current left/right edge - aResult.width = aState.availSize.width; - } -} - -/** - * Push all of the kids that we have not reflowed, starting at - * aState.lineStart. aPrevKid is the kid previous to aState.lineStart - * and is also our last child. Note that line length is NOT a - * reflection of the number of children we are actually pushing - * (because we don't break the sibling list as we add children to the - * line). - */ -void nsBlockFrame::PushKids(nsBlockReflowState& aState) -{ - nsIFrame* prevFrame = aState.prevLineLastFrame; - NS_PRECONDITION(nsnull != prevFrame, "pushing all kids"); #ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevFrame->GetNextSibling(nextSibling); - NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line"); + nsIFrame* nextFrame; + prevKidFrame->GetNextSibling(nextFrame); + NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list"); #endif + prevKidFrame->SetNextSibling(nsnull); + prevLine->mNextLine = nsnull; + mLastContentOffset = prevLine->mLastContentOffset; + mLastContentIsComplete = prevLine->mLastContentIsComplete; + + // Push children to our next-in-flow if we have, or to our overflow list + nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; + PRInt32 pushCount = 0; + if (nsnull == nextInFlow) { + // Place children on the overflow list + mOverflowList = aLine->mFirstChild; + + // Destroy the lines + nsLineData* line = aLine; + while (nsnull != line) { + pushCount += line->mChildCount; + nsLineData* next = line->mNextLine; + delete line; + line = next; + } + } + else { + aLine->mPrevLine = nsnull; + + // Pass on the children to our next-in-flow + nsLineData* line = aLine; + prevLine = line; + nsIFrame* lastKid = aLine->mFirstChild; + nsIFrame* kid = lastKid; + while (nsnull != line) { + i = line->mChildCount; + pushCount += i; + NS_ASSERTION(kid == line->mFirstChild, "bad line list"); + while (--i >= 0) { + kid->SetGeometricParent(nextInFlow); + nsIFrame* contentParent; + kid->GetContentParent(contentParent); + if (this == contentParent) { + kid->SetContentParent(nextInFlow); + } + lastKid = kid; + kid->GetNextSibling(kid); + } + prevLine = line; + line = line->mNextLine; + } + + // Join the two line lists + nsLineData* nextInFlowLine = nextInFlow->mLines; + if (nsnull != nextInFlowLine) { + lastKid->SetNextSibling(nextInFlowLine->mFirstChild); + nextInFlowLine->mPrevLine = prevLine; + } + nsIFrame* firstKid = aLine->mFirstChild; + prevLine->mNextLine = nextInFlowLine; + nextInFlow->mLines = aLine; + nextInFlow->mFirstChild = firstKid; + nextInFlow->mChildCount += pushCount; + firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset); #ifdef NS_DEBUG - PRInt32 numKids = LengthOf(mFirstChild); - NS_ASSERTION(numKids == mChildCount, "bad child count"); + nextInFlow->VerifyLines(PR_FALSE); +#endif + } + mChildCount -= pushCount; + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return NS_LINE_LAYOUT_NOT_COMPLETE; +} + +nsresult +nsBlockFrame::ReflowMapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != line) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // If we have no children and we have a prev-in-flow then we need to + // pick up where it left off. If we have children, e.g. we're being + // resized, then our content offset will have already been set + // correctly. + nsIFrame* kidPrevInFlow = nsnull; + if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { + nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; + mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary? + if (PR_FALSE == prev->mLastContentIsComplete) { + // Our prev-in-flow's last child is not complete + prev->LastChild(kidPrevInFlow); + } + } + + // Get to the last line where the new content may be added + nsLineData* line = nsnull; + nsLineData* prevLine = nsnull; + if (nsnull != mLines) { + line = mLines; + while (nsnull != line->mNextLine) { + line = line->mNextLine; + } + prevLine = line; + + // If the last line is not complete then kidPrevInFlow should be + // set to the last-line's last child. + if (!prevLine->mLastContentIsComplete) { + kidPrevInFlow = prevLine->GetLastChild(); + } + } + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + // Now reflow the new content until we are out of new content or out + // of vertical space. + PRInt32 kidIndex = NextChildOffset(); + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + lineLayout.mKidPrevInFlow = kidPrevInFlow; + PRInt32 contentChildCount = mContent->ChildCount(); + while (kidIndex < contentChildCount) { + if (nsnull == line) { + if (!MoreToReflow(aState)) { + break; + } + line = new nsLineData(); + if (nsnull == line) { + rv = NS_ERROR_OUT_OF_MEMORY; + goto done; + } + line->mFirstContentOffset = kidIndex; + } + + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mKidPrevInFlow = kidPrevInFlow; + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Add line to the block; do this before placing the line in case + // PushLines is needed. + if (nsnull == prevLine) { + // For the first line, initialize mFirstContentOffset + mFirstContentOffset = line->mFirstContentOffset; + mFirstChild = line->mFirstChild; + mLines = line; + } + else { + prevLine->mNextLine = line; + line->mPrevLine = prevLine; + } + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + kidIndex = lineLayout.mKidIndex; + kidPrevInFlow = lineLayout.mKidPrevInFlow; + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; + if (aState.mBlockIsPseudo) { + PropagateContentOffsets(); + } + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::InitializeState(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockReflowState& aState) +{ + nsresult rv; + rv = aState.Initialize(aPresContext, aSpaceManager, + aMaxSize, aMaxElementSize, this); + + nsStyleSpacing* mySpacing = (nsStyleSpacing*) + mStyleContext->GetData(kStyleSpacingSID); + + // Apply border and padding adjustments for regular frames only + if (!aState.mBlockIsPseudo) { + aState.mY = mySpacing->mBorderPadding.top; + aState.mX = mySpacing->mBorderPadding.left; + aState.mAvailSize.width -= + (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); + aState.mAvailSize.height -= + (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); + aState.mBorderPadding = mySpacing->mBorderPadding; + } + else { + aState.mBorderPadding.SizeTo(0, 0, 0, 0); + } + + // Setup initial list ordinal value + nsIAtom* tag = mContent->GetTag(); + if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || + (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { + nsHTMLValue value; + if (eContentAttr_HasValue == + ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { + if (eHTMLUnit_Integer == value.GetUnit()) { + aState.mNextListOrdinal = value.GetIntValue(); + } + } + } + NS_RELEASE(tag); + + return rv; +} + +#if XXX +NS_METHOD +nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight) +{ + printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth), + NS_TWIPS_TO_POINTS_FLOAT(aHeight)); + return nsHTMLContainerFrame::SizeTo(aWidth, aHeight); +} + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, desiredRect, aStatus); + aDesiredSize.width = desiredRect.width; + aDesiredSize.height = desiredRect.height; + aDesiredSize.ascent = aDesiredSize.height; + aDesiredSize.descent = 0; + } + return rv; +} #endif -#ifdef NOISY - ListTag(stdout); - printf(": push kids (childCount=%d)\n", mChildCount); - DumpFlow(); -#endif - - PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete); - SetLastContentOffset(prevFrame); - - // Set mLastContentIsComplete to the previous lines last content is - // complete now that the previous line's last child is our last - // child. - mLastContentIsComplete = aState.prevLineLastContentIsComplete; - - // Fix up child count - // XXX is there a better way? aState.lineLength doesn't work because - // we might be pushing more than just the pending line. - nsIFrame* kid = mFirstChild; - PRInt32 kids = 0; - while (nsnull != kid) { - kids++; - kid->GetNextSibling(kid); - } - mChildCount = kids; - - // Make sure we have no lingering line data - aState.lineLength = 0; - aState.lineStart = nsnull; - -#ifdef NOISY - ListTag(stdout); - printf(": push kids done (childCount=%d) [%c]\n", mChildCount, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif -} - -/** - * Gets a band of available space starting at the specified y-offset. Assumes - * the local coordinate space is currently set to the upper-left origin of the - * bounding rect - * - * Updates "currentBand" and "x" member data of the block reflow state - */ -void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY) -{ - // Gets a band of available space. - aState.spaceManager->Translate(aState.borderPadding.left, 0); - aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand); - - // Compute the bounding rect of the available space, i.e. space between any - // left and right floaters - aState.currentBand->ComputeAvailSpaceRect(); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - aState.x = aState.currentBand->availSpace.x; -} - -void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear) -{ - // Translate the coordinate space - aState.spaceManager->Translate(aState.borderPadding.left, 0); - -getBand: - nscoord y = aState.y + aState.topMargin; - PRBool isLeftFloater = PR_FALSE; - PRBool isRightFloater = PR_FALSE; - - // Get a band of available space - aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand); - - // Scan the trapezoids looking for left and right floaters - nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids; - for (PRInt32 i = 0; i < aState.currentBand->count; i++) { - // XXX Handle multiply occupied - if (nsBandTrapezoid::smOccupied == trapezoid->state) { - nsStyleDisplay* display; - - trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display); - if (NS_STYLE_FLOAT_LEFT == display->mFloats) { - isLeftFloater = PR_TRUE; - } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) { - isRightFloater = PR_TRUE; - } - } - - trapezoid++; - } - - if (isLeftFloater) { - if ((aClear == NS_STYLE_CLEAR_LEFT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - if (isRightFloater) { - if ((aClear == NS_STYLE_CLEAR_RIGHT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - - aState.spaceManager->Translate(-aState.borderPadding.left, 0); -} - -// Bit's for PlaceAndReflowChild return value -#define PLACE_FIT 0x1 -#define PLACE_FLOWED 0x2 - -PRIntn -nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC) -{ - nsSize kidMaxElementSize; - nsSize* pKidMaxElementSize = - (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; - - // Get line start setup if we are at the start of a new line - if (nsnull == aState.lineStart) { - NS_ASSERTION(0 == aState.lineLength, "bad line length"); - aState.lineStart = aKidFrame; - } - - // Get kid and its style - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - - // Figure out if kid is a block element or not - PRBool isInline = PR_TRUE; - PRIntn display = styleDisplay->mDisplay; - if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) { - // XXX Special hack for properly reflowing bullets that have the - // inside value for list-style-position. - display = NS_STYLE_DISPLAY_INLINE; - } - if ((NS_STYLE_DISPLAY_BLOCK == display) || - (NS_STYLE_DISPLAY_LIST_ITEM == display)) { - // Block elements always end up on the next line (unless they are - // already at the start of the line). - isInline = PR_FALSE; - if (aState.lineLength > 0) { - aState.breakAfterChild = PR_TRUE; - } - } - - // Handle forced break first - if (aState.breakAfterChild) { - NS_ASSERTION(aState.lineStart != aKidFrame, "bad line"); - - // Get the last child in the current line - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - if (!AdvanceToNextLine(aCX, aState)) { - // The previous line didn't fit. - return 0; - } - aState.lineStart = aKidFrame; - - // Get the style for the last child, and see if it wanted to clear - // floaters. This handles the BR tag, which is the only inline - // element for which clear applies - nsIStyleContextPtr lastChildSC; - - lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef()); - nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*) - lastChildSC->GetData(kStyleDisplaySID); - switch (lastChildDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, lastChildDisplay->mBreakType); - break; - } - } - - // Now that we've handled force breaks (and maybe called AdvanceToNextLine() - // which checks), remember whether it's an inline frame - aState.isInline = isInline; - - // If we're at the beginning of a line then compute the top margin that we - // should use - if (aState.lineStart == aKidFrame) { - // Compute the top margin to use for this line - aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC, - aState.isInline); - - // If it's an inline element then get a band of available space - // - // XXX If we have a current band and there's unused space in that band - // then avoid this call to get a band... - if (aState.isInline) { - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - } - } - - // Compute the available space to reflow the child into and then - // reflow it into that space. - nsSize kidAvailSize; - GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - - ReflowStatus status; - - if (aState.isInline) { - nsReflowMetrics kidSize; - - // Inline elements are never passed the space manager - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - NS_ASSERTION(nsnull != aState.lineStart, "bad line start"); - - if (aKidFrame == aState.lineStart) { - // Width always fits when we are at the logical left margin. - // Just check the height. - // - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall - return PLACE_FLOWED; - } - } else { - // Examine state and if the breakBeforeChild is set and we - // aren't already on the new line, do the forcing now. - // XXX Why aren't we doing this check BEFORE we resize reflow the child? - if (aState.breakBeforeChild) { - aState.breakBeforeChild = PR_FALSE; - if (aKidFrame != aState.lineStart) { - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow child now that it has the line to itself - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - } - } - - // When we are not at the logical left margin then we need - // to check the width first. If we are too wide then advance - // to the next line and try reflowing again. - if (kidSize.width > kidAvailSize.width) { - // Too wide. Try next line - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow splittable children - SplittableType isSplittable; - - aKidFrame->IsSplittable(isSplittable); - if (isSplittable != frNotSplittable) { - // Update size info now that we are on the next line. Then - // reflow the child into the new available space. - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - } - - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall on the next line - return PLACE_FLOWED; - } - // It's ok if it's too wide on the next line. - } - } - } - - // Add child to the line - AddInlineChildToLine(aCX, aState, aKidFrame, kidSize, - pKidMaxElementSize, aKidSC); - } else { - nsRect kidRect; - - // Does the block-level element want to clear any floaters that impact - // it? Note that the clear property only applies to block-level elements - // and the BR tag - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - switch (styleDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, styleDisplay->mBreakType); - GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - break; - } - - // Give the block its own local coordinate space.. Note: ReflowChild() - // will adjust for the child's left/right margin after determining the - // current left/right edge - aState.spaceManager->Translate(aState.borderPadding.left, 0); - // Give the block-level element the opportunity to use the space manager - status = ReflowChild(aKidFrame, aCX, aState.spaceManager, - kidAvailSize, kidRect, pKidMaxElementSize); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - // Block elements always fit horizontally (because they are - // always placed at the logical left margin). Check to see if - // the block fits vertically - if (kidRect.YMost() > kidAvailSize.height) { - // Nope - return PLACE_FLOWED; - } - } - - // Add block child - // XXX We need to set lastContentIsComplete here, because AddBlockChild() - // calls AdvaneceToNextLine(). We need to restructure the flow of control, - // and use a state machine... - aState.lastContentIsComplete = PRBool(status == frComplete); - AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC); - } - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - - aState.lastContentIsComplete = PRBool(status == frComplete); - if (aState.isInline && (frNotComplete == status)) { - // Since the inline child didn't complete its reflow we *know* - // that a continuation of it can't possibly fit on the current - // line. Therefore, set a flag in the state that will cause the - // a line break before the next frame is placed. - aState.breakAfterChild = PR_TRUE; - } - - aState.reflowStatus = status; - return PLACE_FLOWED | PLACE_FIT; -} - -/** - * Reflow the existing frames. - * - * @param aCX presentation context to use - * @param aState in out parameter which tracks the state of - * reflow for the block frame. - * @return true if we successfully reflowed all the mapped children and false - * otherwise, e.g. we pushed children to the next in flow - */ PRBool -nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsIFrame* kidFrame; - - for (kidFrame = mFirstChild; nsnull != kidFrame; ) { - - /* we get the kid's style from kidFrame's cached style context */ - nsIStyleContextPtr kidSC; - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - - // Attempt to place and reflow the child - - // XXX if child is not splittable and it fits just place it where - // it is, otherwise advance to the next line and place it there if - // possible - - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - ReflowStatus status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // The child doesn't fit. Push it and any remaining children. - PushKids(aState); - result = PR_FALSE; - goto push_done; - } - - // Is the child complete? - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (frComplete == status) { - // Yes, the child is complete - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); - } else { - // No the child isn't complete - if (nsnull == kidNextInFlow) { - // The child doesn't have a next-in-flow so create a continuing - // frame. This hooks the child into the flow - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); - - // Add the continuing frame to the sibling list - nsIFrame* nextSib; - - kidFrame->GetNextSibling(nextSib); - continuingFrame->SetNextSibling(nextSib); - kidFrame->SetNextSibling(continuingFrame); - mChildCount++; - } - - // Unlike the inline frame code we can't assume that we used - // up all of our space because the child's reflow status is - // frNotComplete. Instead, the child is probably split and - // we need to reflow the continuations as well. - } - - // Get the next child frame - kidFrame->GetNextSibling(kidFrame); - } - - push_done: -#ifdef NS_DEBUG - nsIFrame* lastChild; - PRInt32 lastIndexInParent; - - LastChild(lastChild); - lastChild->GetIndexInParent(lastIndexInParent); - NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset"); - - PRInt32 len = LengthOf(mFirstChild); - NS_POSTCONDITION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -/* XXX: - * In this method, we seem to be getting the style for the next child, - * and checking that child's style for the child's display type. - * The problem is that it's very inefficient to ResolveStyleFor(kid) here - * and then just throw it away, when we end up doing a ResolveStyleFor(kid) - * again when we actually create a frame for the content. - * It would be nice to cache the resolved style, maybe in the reflow state? - */ -PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) +nsBlockFrame::MoreToReflow(nsBlockReflowState& aState) { PRBool rv = PR_FALSE; - if (IsPseudoFrame()) { + if (aState.mBlockIsPseudo) { // Get the next content object that we would like to reflow PRInt32 kidIndex = NextChildOffset(); nsIContentPtr kid = mContent->ChildAt(kidIndex); if (kid.IsNotNull()) { // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); + nsIStyleContextPtr kidSC = + aState.mPresContext->ResolveStyleContextFor(kid, this); nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*) kidSC->GetData(kStyleDisplaySID); switch (kidStyleDisplay->mDisplay) { @@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) return rv; } -/** - * Create new frames for content we haven't yet mapped - * - * @param aCX presentation context to use - * @return frComplete if all content has been mapped and frNotComplete - * if we should be continued - */ -nsIFrame::ReflowStatus -nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) +nsBlockReflowState* +nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext, + nsIFrame* aFrame) { -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif - nsIFrame* kidPrevInFlow = nsnull; - ReflowStatus result = frNotComplete; - - // If we have no children and we have a prev-in-flow then we need to pick - // up where it left off. If we have children, e.g. we're being resized, then - // our content offset should already be set correctly... - if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { - nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; - NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow"); - mFirstContentOffset = prev->NextChildOffset(); - if (PR_FALSE == prev->mLastContentIsComplete) { - // Our prev-in-flow's last child is not complete - prev->LastChild(kidPrevInFlow); - } - } - - // Place our children, one at a time until we are out of children - PRInt32 kidIndex = NextChildOffset(); - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (kid.IsNull()) { - result = frComplete; - break; - } - - // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - nsStylePosition* kidPosition = (nsStylePosition*) - kidSC->GetData(kStylePositionSID); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); - - // Check whether it wants to floated or absolutely positioned - if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) { - AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (nsnull == kidPrevInFlow) { - // Create initial frame for the child - nsIContentDelegate* kidDel; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // If we're being used as a pseudo frame, i.e. we map the same - // content as our parent then we want to indicate we're complete; - // otherwise we'll be continued and go on mapping children... - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - result = frComplete; - goto done; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aCX); - kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow"); - nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); + nsBlockReflowState* state = nsnull; + if (nsnull != aFrame) { + nsIFrame* parent; + aFrame->GetGeometricParent(parent); + while (nsnull != parent) { + nsBlockFrame* block; + nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block); + if (NS_OK == rv) { + nsIPresShell* shell = aPresContext->GetShell(); + state = (nsBlockReflowState*) shell->GetCachedData(block); + NS_RELEASE(shell); break; } - kidFrame->SetStyleContext(aCX,kidSC); - } else { - // Since kid has a prev-in-flow, use that to create the next - // frame. - kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame); + parent->GetGeometricParent(parent); } - - // Link child frame into the list of children. If the frame ends - // up not fitting and getting pushed, the PushKids code will fixup - // the child count for us. - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - mChildCount++; - - // Reflow child frame as many times as necessary until it is - // complete. - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // We ran out of room. - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - goto push_done; - } - - // Did the child complete? - prevKidFrame = kidFrame; - if (frNotComplete == status) { - // Child didn't complete so create a continuing frame - kidPrevInFlow = kidFrame; - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to the sibling list - nsIFrame* kidNextSibling; - - kidFrame->GetNextSibling(kidNextSibling); - continuingFrame->SetNextSibling(kidNextSibling); - kidFrame->SetNextSibling(continuingFrame); - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "huh?"); -#endif - } while (frNotComplete == status); - - // The child that we just reflowed is complete -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); -#endif - kidIndex++; - kidPrevInFlow = nsnull; } - - done: - // To get here we either completely reflowed all our appended - // children OR we are a pseudo-frame and we ran into a block - // element. In either case our last content MUST be complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - - push_done: -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - return result; + return state; } -/** - * Pullup frames from our next in flow and try to place them. Before - * this is called our previously mapped children, if any have been - * reflowed which means that the block reflow state's x and y - * coordinates and other data are ready to go. - * - * Return true if we pulled everything up. - */ -PRBool -nsBlockFrame::PullUpChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - while (nsnull != nextInFlow) { - // Get first available frame from the next-in-flow - nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame); - if (nsnull == kidFrame) { - // We've pulled up all the children from that next-in-flow, so - // move to the next next-in-flow. - nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; - continue; - } - - // Get style information for the pulled up kid - nsIContentPtr kid; - - kidFrame->GetContent(kid.AssignRef()); - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // Push the kids that didn't fit back down to the next-in-flow - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - result = PR_FALSE; - goto push_done; - } - - if (frNotComplete == status) { - // Child is not complete - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (nsnull == kidNextInFlow) { - // Create a continuing frame for the incomplete child - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to our sibling list. - nsIFrame* nextSibling; - - kidFrame->GetNextSibling(nextSibling); - continuingFrame->SetNextSibling(nextSibling); - kidFrame->SetNextSibling(continuingFrame); - prevKidFrame = kidFrame; - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } else { - // The child has a next-in-flow, but it's not one of ours. - // It *must* be in one of our next-in-flows. Collect it - // then. - NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow"); - break; - } - } - } while (frNotComplete == status); - - prevKidFrame = kidFrame; - } - - if (nsnull != prevKidFrame) { - // The only way we can get here is by pulling up every last child - // in our next-in-flows (and reflowing any continunations they - // have). Therefore we KNOW that our last child is complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - } - - push_done:; - - if (result == PR_FALSE) { - // If our next-in-flow is empty OR our next next-in-flow is empty - // then adjust the offsets of all of the empty next-in-flows. - nextInFlow = (nsBlockFrame*) mNextInFlow; - if ((0 == nextInFlow->mChildCount) || - ((nsnull != nextInFlow->mNextInFlow) && - (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) { - // We didn't pullup everything and we need to fixup one of our - // next-in-flows content offsets. - AdjustOffsetOfEmptyNextInFlows(); - } - } - - -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -void nsBlockFrame::SetupState(nsIPresContext* aCX, - nsBlockReflowState& aState, - const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsISpaceManager* aSpaceManager) -{ - // Setup reflow state - aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager); - - nsStyleSpacing* mySpacing = (nsStyleSpacing*) - mStyleContext->GetData(kStyleSpacingSID); - - // Apply border and padding adjustments for regular frames only - if (PR_FALSE == IsPseudoFrame()) { - aState.borderPadding = mySpacing->mBorderPadding; - aState.y = mySpacing->mBorderPadding.top; - aState.availSize.width -= - (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); - aState.availSize.height -= - (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); - } else { - aState.borderPadding.SizeTo(0, 0, 0, 0); - } - - // Setup initial list ordinal value - nsIAtom* tag = mContent->GetTag(); - if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || - (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { - nsHTMLValue value; - if (eContentAttr_HasValue == - ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { - if (eHTMLUnit_Integer == value.GetUnit()) { - aState.nextListOrdinal = value.GetIntValue(); - } - } - } - NS_RELEASE(tag); - - mCurrentState = &aState; -} - -#include "nsUnitConversion.h"/* XXX */ -NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsSize* aMaxElementSize, - ReflowStatus& aStatus) -{ - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager); - return DoResizeReflow(aCX, state, aDesiredRect, aStatus); -} - -nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsRect& aDesiredRect, - ReflowStatus& aStatus) +nsresult +nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + ReflowStatus& aStatus) { #ifdef NS_DEBUG + VerifyLines(PR_TRUE); PreReflowCheck(); #endif -#ifdef NOISY +#ifdef NOISY_REFLOW ListTag(stdout); - printf(": resize reflow %g,%g\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height)); - DumpFlow(); + printf(": Before:\n"); + List(stdout, 1); #endif - // Zap old line data - if (nsnull != mLines) { - delete mLines; - mLines = nsnull; - } - mNumLines = 0; + nsresult rv = NS_OK; - // Check for an overflow list - MoveOverflowToChildList(); - - // Before we start reflowing, cache a pointer to our state structure - // so that inline frames can find it. - nsIPresShell* shell = aCX->GetShell(); + nsIPresShell* shell = aState.mPresContext->GetShell(); shell->PutCachedData(this, &aState); - // First reflow any existing frames - PRBool reflowMappedOK = PR_TRUE; - aStatus = frComplete; - if (nsnull != mFirstChild) { - reflowMappedOK = ReflowMappedChildren(aCX, aState); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } + // Check for an overflow list + DrainOverflowList(); + + if (nsnull != mLines) { + rv = ReflowMapped(aState); } - if (reflowMappedOK) { - // Any space left? - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if ((nsnull != mFirstChild) && (aState.y >= yb)) { - // No space left. Don't try to pull-up children or reflow - // unmapped. We need to return the correct completion status, - // so see if there is more to reflow. - if (MoreToReflow(aCX)) { - aStatus = frNotComplete; - } - } else if (MoreToReflow(aCX)) { - // Try and pull-up some children from a next-in-flow - if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) { - // If we still have unmapped children then create some new frames - if (MoreToReflow(aCX)) { - aStatus = ReflowAppendedChildren(aCX, aState); - } - } else { - // We were unable to pull-up all the existing frames from the - // next in flow - aStatus = frNotComplete; - } + if (NS_OK == rv) { + if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) { + // We are out of space } - } - - // Deal with last line - make sure it gets vertically and - // horizontally aligned. This also updates the state's y coordinate - // which is good because that's how we size ourselves. - if (0 != aState.lineLength) { - if (!AdvanceToNextLine(aCX, aState)) { - // The last line of output doesn't fit. Push all of the kids to - // the next in flow and change our reflow status to not complete - // so that we are continued. -#ifdef NOISY - ListTag(stdout); - printf(": pushing kids since last line doesn't fit\n"); -#endif - - PushKids(aState); - aStatus = frNotComplete; + if (MoreToReflow(aState)) { + rv = ReflowUnmapped(aState); } } - if (frComplete == aStatus) { - // Don't forget to add in the bottom margin from our last child. - // Only add it in if there is room for it. - nscoord margin = aState.prevMaxPosBottomMargin - - aState.prevMaxNegBottomMargin; - nscoord y = aState.y + margin; - if (y <= aState.borderPadding.top + aState.availSize.height) { - aState.y = y; - } - } - - // Now that reflow has finished, remove the cached pointer - shell->RemoveCachedData(this); - NS_RELEASE(shell); - - // Translate state.lineLengths into an integer array - mNumLines = aState.lineLengths.Count(); - if (mNumLines > 0) { - mLines = new PRInt32[mNumLines]; - for (PRInt32 i = 0; i < mNumLines; i++) { - PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i); - mLines[i] = ll; - } - } - - if (!aState.unconstrainedWidth && aState.justifying) { - // Perform justification now that we know how many lines we have. - JustifyLines(aCX, aState); - } - // Return our desired rect and our status aDesiredRect.x = 0; aDesiredRect.y = 0; - aDesiredRect.width = aState.kidXMost + aState.borderPadding.right; - if (!aState.unconstrainedWidth) { + aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right; + if (!aState.mUnconstrainedWidth) { // Make sure we're at least as wide as the max size we were given - nscoord maxWidth = aState.availSize.width + aState.borderPadding.left + - aState.borderPadding.right; - + nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left + + aState.mBorderPadding.right; if (aDesiredRect.width < maxWidth) { aDesiredRect.width = maxWidth; } } - aState.y += aState.borderPadding.bottom; - aDesiredRect.height = aState.y; - -#ifdef NS_DEBUG - PostReflowCheck(aStatus); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height), - ((status == frNotComplete) ? "NOT " : ""), - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F') - ); - DumpFlow(); -#endif - mCurrentState = nsnull; - return NS_OK; -} - -void nsBlockFrame::JustifyLines(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - // XXX we don't justify the last line; what if we are continued, - // should we do it then? - nsIFrame* kid = mFirstChild; - for (PRInt32 i = 0; i < mNumLines; i++) { - nsIFrame* lineStart = kid; - PRInt32 lineLength = mLines[i]; - if (i < mNumLines - 1) { - if (1 == lineLength) { - // For lines with one element on them we can take a shortcut and - // let them do the justification. Note that we still call - // JustifyReflow even if the available space is zero in case the - // child is a hunk of text that ends in whitespace. The whitespace - // will be distributed elsewhere causing a proper flush right look - // for the last word. - nsRect r; - kid->GetRect(r); - nscoord maxWidth = aState.availSize.width; - nscoord availableSpace = maxWidth - r.width; - nscoord maxAvailSpace = nscoord(maxWidth * 0.1f); - if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) { - kid->JustifyReflow(aCX, availableSpace); - kid->SizeTo(r.width + availableSpace, r.height); - } - kid->GetNextSibling(kid); - } else { - // XXX Get justification of multiple elements working - while (--lineLength >= 0) { - kid->GetNextSibling(kid); - } + aState.mY += aState.mBorderPadding.bottom; + nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) { + // It's possible that we don't have room for the last bottom + // margin (the last bottom margin is the margin following a block + // element that we contain; it isn't applied immediately because + // of the margin collapsing logic). This can happen when we are + // reflowed in a limited amount of space because we don't know in + // advance what the last bottom margin will be. + nscoord maxY = aMaxSize.height; + if (aState.mY + lastBottomMargin > maxY) { + lastBottomMargin = maxY - aState.mY; + if (lastBottomMargin < 0) { + lastBottomMargin = 0; } } - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]); + aState.mY += lastBottomMargin; } -} + aDesiredRect.height = aState.mY; -NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const -{ - aIsSplittable = frSplittableNonRectangular; - return NS_OK; -} - -NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, - nsIFrame* aParent, - nsIFrame*& aContinuingFrame) -{ - nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); - PrepareContinuingFrame(aCX, aParent, cf); - aContinuingFrame = cf; - return NS_OK; -} - -NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsReflowCommand& aReflowCommand, - ReflowStatus& aStatus) -{ + // Set return status aStatus = frComplete; - - if (aReflowCommand.GetTarget() == this) { - // XXX for now, just do a complete reflow mapped (it'll kinda - // work, but it's slow) - - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager); - PRBool reflowMappedOK = ReflowMappedChildren(aCX, state); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } - } else { - // XXX not yet implemented - NS_ABORT(); - // XXX work to compute initial state goes *HERE* - aDesiredRect.x = 0; - aDesiredRect.y = 0; - aDesiredRect.width = 0; - aDesiredRect.height = 0; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; } +#ifdef NS_DEBUG + // Verify that the line layout code pulled everything up when it + // indicates a complete reflow. + if (frComplete == aStatus) { + nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow; + while (nsnull != nextBlock) { + NS_ASSERTION((nsnull == nextBlock->mLines) && + (nextBlock->mChildCount == 0) && + (nsnull == nextBlock->mFirstChild), + "bad completion status"); + nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow; + } - mCurrentState = nsnull; - return NS_OK; +#if XXX + // We better not be in the same parent frame as our prev-in-flow. + // If we are it means that we were continued instead of pulling up + // children. + if (nsnull != mPrevInFlow) { + nsIFrame* ourParent = mGeometricParent; + nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent; + NS_ASSERTION(ourParent != prevParent, "bad continuation"); + } +#endif + } +#endif + + // Now that reflow has finished, remove the cached pointer + shell->RemoveCachedData(this); + NS_RELEASE(shell); + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); + PostReflowCheck(aStatus); +#endif +#ifdef NOISY_REFLOW + ListTag(stdout); + printf(": After:\n"); + List(stdout, 1); +#endif + return rv; } -PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ContentAppended(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentInserted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentReplaced(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentDeleted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext, + nsReflowMetrics& aMetrics) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aSpaceManager, aMaxSize, + aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus); + } + return rv; +} + +NS_METHOD +nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsReflowCommand& aReflowCommand, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +PRBool +nsBlockFrame::AddFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + + if (nsnull != state) { + // Get the frame associated with the space manager, and get its + // nsIAnchoredItems interface + nsIFrame* frame = state->mSpaceManager->GetFrame(); + nsIAnchoredItems* anchoredItems = nsnull; + + frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); + NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); + if (nsnull != anchoredItems) { + anchoredItems->AddAnchoredItem(aFloater, + nsIAnchoredItems::anHTMLFloater, + this); + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + return PR_TRUE; + } + } + + return PR_FALSE; +} + +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + if (nsnull != state) { + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + } +} + +PRBool +nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) { do { nsIFrame* parent; - aFrame->GetGeometricParent(parent); - // See if there are any non-zero sized child frames that precede aFrame - // in the child list + // See if there are any non-zero sized child frames that precede + // aFrame in the child list nsIFrame* child; - parent->FirstChild(child); while ((nsnull != child) && (aFrame != child)) { nsSize size; @@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // We found a non-zero sized child frame that precedes aFrame return PR_FALSE; } - child->GetNextSibling(child); } @@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // Walk up one level and check that its parent is left-most as well aFrame = parent; } while (aFrame != this); - return PR_TRUE; } -PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) -{ - // Get the frame associated with the space manager, and get its nsIAnchoredItems - // interface - nsIFrame* frame = mCurrentState->spaceManager->GetFrame(); - nsIAnchoredItems* anchoredItems = nsnull; - - frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); - NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); - - if (nsnull != anchoredItems) { - anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this); - PlaceFloater(aCX, aFloater, aPlaceholder); - return PR_TRUE; - } - - return PR_FALSE; -} - -// XXX The size of the floater needs to be taken into consideration if we're -// computing a maximum element size -void nsBlockFrame::PlaceFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder, + nsBlockReflowState& aState) { // If the floater is the left-most non-zero size child frame then insert // it before the current line; otherwise add it to the below-current-line // todo list and we'll handle it when we flush out the line if (IsLeftMostChild(aPlaceholder)) { - // Get the type of floater - nsIStyleContextPtr styleContext; + nsISpaceManager* sm = aState.mSpaceManager; - aFloater->GetStyleContext(aCX, styleContext.AssignRef()); + // Get the type of floater + nsIStyleContextPtr styleContext; + aFloater->GetStyleContext(aPresContext, styleContext.AssignRef()); nsStyleDisplay* floaterDisplay = (nsStyleDisplay*) styleContext->GetData(kStyleDisplaySID); - if (!mCurrentState->isInline) { - // Get the current band for this line - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); - } - - // Commit some space in the space manager - nsRect region; - + // Commit some space in the space manager and adjust our current + // band of available space. + nsRect region; aFloater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; - + region.y = aState.mY; if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; + region.x = aState.mX; } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, + "bad float type"); + region.x = aState.mCurrentBand.availSpace.XMost() - region.width; } // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0); - mCurrentState->spaceManager->AddRectRegion(region, aFloater); + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, aFloater); // Set the origin of the floater in world coordinates nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); + sm->GetTranslation(worldX, worldY); aFloater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); + sm->Translate(-aState.mBorderPadding.left, 0); - // Update the band of available space to reflect space taken up by the floater - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); + // Update the band of available space to reflect space taken up by + // the floater + GetAvailableSpace(aState, aState.mY); + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); + } } else { // Add the floater to our to-do list - mCurrentState->floaterToDo.AppendElement(aFloater); + aState.mPendingFloaters.AppendElement(aFloater); } } -NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell, - nsIPresContext* aPresContext, - nsIContent* aContainer) +void +nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState, + nscoord aY) { - // Get the last-in-flow - nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow(); - PRInt32 kidIndex = flow->NextChildOffset(); - PRInt32 startIndex = kidIndex; + NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters"); -#if 0 - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - flow->LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (nsnull == kid) { - break; - } + nsISpaceManager* sm = aState.mSpaceManager; + nsBlockBandData* bd = &aState.mCurrentBand; - // Resolve style for the kid - nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); + // XXX Factor this code with PlaceFloater()... + PRInt32 numFloaters = aState.mPendingFloaters.Count(); + for (PRInt32 i = 0; i < numFloaters; i++) { + nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i]; + nsRect region; - // Is it a floater? - if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); + // Get the band of available space + // XXX This is inefficient to do this inside the loop... + GetAvailableSpace(aState, aY); + + // Get the type of floater + nsIStyleContextPtr styleContext; + floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef()); + nsStyleDisplay* sd = (nsStyleDisplay*) + styleContext->GetData(kStyleDisplaySID); + + floater->GetRect(region); + region.y = bd->availSpace.y; + if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { + region.x = bd->availSpace.x; } else { - // Create initial frame for the child - nsIContentDelegate* kidDel; - nsresult fr; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // Update last content offset now that we are done drawing - // children from our parent. - SetLastContentOffset(prevKidFrame); - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - return NS_OK; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aPresContext); - kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); - break; - } + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); + region.x = bd->availSpace.XMost() - region.width; } - kidFrame->SetStyleContext(aCX,kidSC); - // Link child frame into the list of children - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; + // XXX Don't forget the floater's margins... + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, floater); - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - kidIndex++; - mChildCount++; + // Set the origin of the floater in world coordinates + nscoord worldX, worldY; + sm->GetTranslation(worldX, worldY); + floater->MoveTo(region.x + worldX, region.y + worldY); + sm->Translate(-aState.mBorderPadding.left, 0); } - SetLastContentOffset(prevKidFrame); -#endif - // If this is a pseudo-frame then our parent will generate the - // reflow command. Otherwise, if the container is us then we should - // generate the reflow command because we were directly called. - if (!IsPseudoFrame() && (aContainer == mContent)) { - nsReflowCommand* rc = - new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended, - startIndex); - aShell->AppendReflowCommand(rc); + aState.mPendingFloaters.Clear(); + + // Pass on updated available space to the current line + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); } - return NS_OK; } -PRIntn nsBlockFrame::GetSkipSides() const +//---------------------------------------------------------------------- + +nsLineData* +nsBlockFrame::GetFirstLine() +{ + return mLines; +} + +PRIntn +nsBlockFrame::GetSkipSides() const { PRIntn skip = 0; if (nsnull != mPrevInFlow) { @@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const } return skip; } - -nsHTMLFrameType nsBlockFrame::GetFrameType() const -{ - return eHTMLFrame_Block; -} - -NS_METHOD nsBlockFrame::ListTag(FILE* out) const -{ - if ((nsnull != mGeometricParent) && IsPseudoFrame()) { - fprintf(out, "*block<"); - nsIAtom* atom = mContent->GetTag(); - if (nsnull != atom) { - nsAutoString tmp; - atom->ToString(tmp); - fputs(tmp, out); - } - fprintf(out, ">(%d)@%p", mIndexInParent, this); - } else { - nsHTMLContainerFrame::ListTag(out); - } - return NS_OK; -} - -#ifdef NS_DEBUG -void nsBlockFrame::DumpFlow() const -{ -#ifdef NOISY_FLOW - nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow; - while (flow != 0) { - printf(" %p: [%d,%d,%c]\n", - flow, flow->mFirstContentOffset, flow->mLastContentOffset, - (flow->mLastContentIsComplete ? 'T' : 'F')); - flow = (nsBlockFrame*) flow->mNextInFlow; - } -#endif -} -#endif diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h index f11bba52ca10..f3fa810539b5 100644 --- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -20,158 +20,101 @@ #include "nsHTMLContainerFrame.h" #include "nsIFloaterContainer.h" -#include "nsIHTMLFrameType.h" #include "nsIRunaround.h" +#include "nsISpaceManager.h" +#include "nsLineLayout.h" #include "nsVoidArray.h" -struct BlockBandData; + struct nsMargin; struct nsStyleDisplay; struct nsStyleFont; struct nsStyleText; +class nsBlockFrame; +struct nsBandData; + +struct nsBlockBandData : public nsBandData { + // Trapezoid's used during band processing + nsBandTrapezoid data[12]; + + // Bounding rect of available space between any left and right floaters + nsRect availSpace; + + nsBlockBandData() { + size = 12; + trapezoids = data; + } + + /** + * Computes the bounding rect of the available space, i.e. space + * between any left and right floaters Uses the current trapezoid + * data, see nsISpaceManager::GetBandData(). Also updates member + * data "availSpace". + */ + void ComputeAvailSpaceRect(); +}; -/** - * Block frames have some state which needs to be made - * available to child frames for proper reflow. This structure - * describes that state. - */ struct nsBlockReflowState { - // True if this is the first line of text in a block container - PRPackedBool firstLine; - - // True if leading whitespace is allowed to show - PRPackedBool allowLeadingWhitespace; - - // This is set when some child frame needs a break after it's placed - PRPackedBool breakAfterChild; - - // This is set when a child needs a break before it's placed. Note - // that this value is set by a child AFTER we have called it's - // ResizeReflow method. - PRPackedBool breakBeforeChild; - - // This is set when the first child should be treated specially - // because it's an inside list item bullet. - // XXX this can go away once we have a way for the bullet's style - // molecule to *not* be the same as it's parent's - PRPackedBool firstChildIsInsideBullet; - - // For pre-formatted text, this is our current column - PRIntn column; - - // The next list ordinal value - PRInt32 nextListOrdinal; - - //---------------------------------------------------------------------- - // State from here on down is not to be used by block child frames! - - // Space manager to use - nsISpaceManager* spaceManager; - - // Block's style context - nsIStyleContext* styleContext; - nsStyleText* styleText; - nsStyleFont* styleFont; - nsStyleDisplay* styleDisplay; - - // Block's available size (computed from the block's parent) - nsSize availSize; - - // Current band of available space. Used for doing runaround - BlockBandData* currentBand; - - // Pointer to a max-element-size (nsnull if none required) - nsSize* maxElementSize; - - // The maximum x-most of our lines and block-level elements. This is used to - // compute our desired size, and includes our left border/padding. For block- - // level elements this also includes the block's right margin. - nscoord kidXMost; - - // Current reflow position - nscoord y; - - // Current line state - nscoord x; // inline elements only. not include border/padding - PRBool isInline; // whether the current is inline or block - nsVoidArray lineLengths; // line length temporary storage - PRIntn currentLineNumber; // index into mLines - nsIFrame* lineStart; // frame starting the line - PRInt32 lineLength; // length of line - nscoord* ascents; // ascent information for each child - nscoord maxAscent; // max ascent for this line - nscoord maxDescent; // max descent for this line - nscoord lineWidth; // current width of line -#if 0 - nscoord maxPosTopMargin; // maximum positive top margin - nscoord maxNegTopMargin; // maximum negative top margin -#else - nscoord topMargin; // current top margin -#endif - nscoord maxPosBottomMargin; // maximum positive bottom margin - nscoord maxNegBottomMargin; // maximum negative bottom margin - nsSize lineMaxElementSize; // max element size for current line - PRBool lastContentIsComplete; // reflow status of last child on line - nsVoidArray floaterToDo; // list of floaters to place below current line - - PRInt32 maxAscents; // size of ascent buffer - nscoord ascentBuf[20]; - - PRPackedBool needRelativePos; // some kid in line needs relative pos - - // Previous line state that we carry forward to the next line - nsIFrame* prevLineLastFrame; - nscoord prevLineHeight; // height of the previous line - nscoord prevMaxPosBottomMargin; // maximum posative bottom margin - nscoord prevMaxNegBottomMargin; // maximum negative bottom margin - PRBool prevLineLastContentIsComplete; - - // Sanitized version of mol->borderPadding; if this block frame is a - // pseudo-frame then the margin will be zero'd. - nsMargin borderPadding; - - PRPackedBool justifying; // we are justifying - - // Status from last PlaceAndReflowChild - nsIFrame::ReflowStatus reflowStatus; - - // Flags for whether the max size is unconstrained - PRBool unconstrainedWidth; - PRBool unconstrainedHeight; - nsBlockReflowState(); - ~nsBlockReflowState(); - void Init(const nsSize& aMaxSize, nsSize* aMaxElementSize, - nsIStyleContext* aStyleContext, nsISpaceManager* aSpaceManager); + nsresult Initialize(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockFrame* aBlock); - void AddAscent(nscoord aAscent); - void AdvanceToNextLine(nsIFrame* aPrevLineLastFrame, nscoord aPrevLineHeight); + nsIPresContext* mPresContext; -#ifdef NS_DEBUG - void DumpLine(); - void DumpList(); -#endif + nsBlockFrame* mBlock; + PRBool mBlockIsPseudo; + + // Current line being reflowed + nsLineLayout* mCurrentLine; + + // Previous line's last child frame + nsIFrame* mPrevKidFrame; + + // Layout position information + nscoord mX; + nscoord mY; + nsSize mAvailSize; + PRPackedBool mUnconstrainedWidth; + PRPackedBool mUnconstrainedHeight; + nsSize* mMaxElementSizePointer; + nscoord mKidXMost; + + // Bottom margin information from the previous line + nscoord mPrevMaxNegBottomMargin; + nscoord mPrevMaxPosBottomMargin; + + // Block frame border+padding information + nsMargin mBorderPadding; + + // Space manager and current band information + nsISpaceManager* mSpaceManager; + nsBlockBandData mCurrentBand; + + // Array of floaters to place below current line + nsVoidArray mPendingFloaters; + + PRInt32 mNextListOrdinal; + PRPackedBool mFirstChildIsInsideBullet; }; //---------------------------------------------------------------------- +/* 94e8e410-de21-11d1-89bf-006008911b81 */ +#define NS_BLOCKFRAME_CID \ + {0x94e8e410, 0xde21, 0x11d1, {0x89, 0xbf, 0x00, 0x60, 0x08, 0x91, 0x1b, 0x81}} + /** *

Block Reflow

* * The block frame reflow machinery performs "2D" layout. Inline * elements are flowed into logical lines (left to right or right to - * left) and the lines are stacked vertically. Block elements are - * flowed onto their own line after flushing out any preceeding line.

- * - * After a line is ready to be flushed out, vertical alignment is - * performed. Vertical alignment may require the line to consume more - * vertical space than is available thus causing the entire line to - * be pushed.

- * - * After vertical alignment is done, horizontal alignment (including - * justification) is performed. Finally, relative positioning is done - * on any elements that require it.

+ * left) and the lines are stacked vertically. nsLineLayout is used + * for this part of the process. Block elements are flowed directly by + * the block reflow logic after flushing out any preceeding line.

* * During reflow, the block frame will make available to child frames * it's reflow state using the presentation shell's cached data @@ -186,144 +129,155 @@ struct nsBlockReflowState { * *

Assertions

* mLastContentIsComplete always reflects the state of the last - * child frame on our chlid list. + * child frame on our chlid list. */ -class nsBlockFrame : public nsHTMLContainerFrame, public nsIHTMLFrameType, - public nsIRunaround, public nsIFloaterContainer + +// XXX we don't use nsContainerFrame mOverFlowList!!! wasted memory + +class nsBlockFrame : public nsHTMLContainerFrame, + public nsIRunaround, + public nsIFloaterContainer { public: /** * Create a new block frame that maps the given piece of content. */ - static nsresult NewFrame(nsIFrame** aInstancePtrResult, + static nsresult NewFrame(nsIFrame** aInstancePtrResult, nsIContent* aContent, PRInt32 aIndexInParent, nsIFrame* aParent); + // nsISupports NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); - NS_IMETHOD ResizeReflow(nsIPresContext* aPresContext, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsSize* aMaxElementSize, - ReflowStatus& aStatus); - - NS_IMETHOD IncrementalReflow(nsIPresContext* aPresContext, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsReflowCommand& aReflowCommand, - ReflowStatus& aStatus); - - NS_IMETHOD ContentAppended(nsIPresShell* aShell, + // nsIFrame + NS_IMETHOD ContentAppended(nsIPresShell* aShell, nsIPresContext* aPresContext, - nsIContent* aContainer); - + nsIContent* aContainer); + NS_IMETHOD ContentInserted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent); + NS_IMETHOD ContentReplaced(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInParent); + NS_IMETHOD ContentDeleted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent); + NS_IMETHOD GetReflowMetrics(nsIPresContext* aPresContext, + nsReflowMetrics& aMetrics); NS_IMETHOD IsSplittable(SplittableType& aIsSplittable) const; - NS_IMETHOD CreateContinuingFrame(nsIPresContext* aPresContext, - nsIFrame* aParent, - nsIFrame*& aContinuingFrame); + nsIFrame* aParent, + nsIFrame*& aContinuingFrame); + NS_IMETHOD List(FILE* out = stdout, PRInt32 aIndent = 0) const; + NS_IMETHOD ListTag(FILE* out) const; + NS_IMETHOD VerifyTree() const; - virtual PRBool AddFloater(nsIPresContext* aCX, - nsIFrame* aFloater, + // nsIRunaround + NS_IMETHOD ResizeReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + nsIFrame::ReflowStatus& aStatus); + NS_IMETHOD IncrementalReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsReflowCommand& aReflowCommand, + nsIFrame::ReflowStatus& aStatus); + + // nsIFloaterContainer + virtual PRBool AddFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, PlaceholderFrame* aPlaceholder); - virtual void PlaceFloater(nsIPresContext* aCX, - nsIFrame* aFloater, + virtual void PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, PlaceholderFrame* aPlaceholder); - NS_IMETHOD ListTag(FILE* out = stdout) const; + // nsBlockFrame + nsresult ReflowInlineChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus); - virtual nsHTMLFrameType GetFrameType() const; + nsresult ReflowBlockChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + ReflowStatus& aStatus); + + nsLineData* GetFirstLine(); + + static nsBlockReflowState* FindBlockReflowState(nsIPresContext* aPresContext, + nsIFrame* aFrame); protected: nsBlockFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent); + PRInt32 aIndexInParent, + nsIFrame* aParent); virtual ~nsBlockFrame(); virtual PRIntn GetSkipSides() const; - PRBool MoreToReflow(nsIPresContext* aCX); + virtual void WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow); - nscoord GetTopMarginFor(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC, - PRBool aIsInline); - - PRBool AdvanceToNextLine(nsIPresContext* aPresContext, + nsresult InitializeState(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, nsBlockReflowState& aState); - void AddInlineChildToLine(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsReflowMetrics& aKidSize, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC); + nsresult DoResizeReflow(nsBlockReflowState& aState, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + ReflowStatus& aStatus); - void AddBlockChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsRect& aKidRect, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC); + void DestroyLines(); - void GetAvailSize(nsSize& aResult, - nsBlockReflowState& aState, - nsIStyleContext* aKidSC, - PRBool aIsInline); + void DrainOverflowList(); - PRIntn PlaceAndReflowChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* kidFrame, - nsIStyleContext* aKidSC); + nsLineData* CreateLineForOverflowList(nsIFrame* aOverflowList); - void PushKids(nsBlockReflowState& aState); + nsresult VerifyLines(PRBool aFinalCheck) const; - void SetupState(nsIPresContext* aCX, nsBlockReflowState& aState, - const nsSize& aMaxSize, nsSize* aMaxElementSize, - nsISpaceManager* aSpaceManager); - - nsresult DoResizeReflow(nsIPresContext* aPresContext, - nsBlockReflowState& aState, - nsRect& aDesiredRect, - ReflowStatus& aStatus); - - PRBool ReflowMappedChildren(nsIPresContext* aPresContext, - nsBlockReflowState& aState); - - PRBool PullUpChildren(nsIPresContext* aCX, - nsBlockReflowState& aState); - - ReflowStatus ReflowAppendedChildren(nsIPresContext* aPresContext, - nsBlockReflowState& aState); - - void JustifyLines(nsIPresContext* aPresContext, nsBlockReflowState& aState); + nsresult PlaceLine(nsBlockReflowState& aState, + nsLineLayout& aLineLayout, + nsLineData* aLine); PRBool IsLeftMostChild(nsIFrame* aFrame); - void GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY); - - void PlaceBelowCurrentLineFloaters(nsIPresContext* aCX, - nsBlockReflowState& aState, + void PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder, + nsBlockReflowState& aState); + void PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState, nscoord aY); - void ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear); + nsresult GetAvailableSpace(nsBlockReflowState& aState, nscoord aY); -#ifdef NS_DEBUG - void DumpFlow() const; -#endif + PRBool MoreToReflow(nsBlockReflowState& aState); - /** - * Array of lines lengths. For each logical line of children, this array - * contains a count of the number of children on the line. - */ - PRInt32* mLines; - PRInt32 mNumLines; - nsBlockReflowState* mCurrentState; + nsresult PushLines(nsBlockReflowState& aState, + nsLineData* aLine); + + nsresult ReflowMapped(nsBlockReflowState& aState); + + nsresult ReflowUnmapped(nsBlockReflowState& aState); + + nsLineData* mLines; }; #endif /* nsBlockFrame_h___ */ diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp index 59d7407ec5b1..0228ca80d413 100644 --- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -16,66 +16,37 @@ * Reserved. */ #include "nsBlockFrame.h" -#include "nsSize.h" -#include "nsIAnchoredItems.h" -#include "nsIContent.h" -#include "nsIContentDelegate.h" -#include "nsISpaceManager.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" -#include "nsIPresContext.h" -#include "nsMargin.h" -#include "nsHTMLIIDs.h" -#include "nsCSSLayout.h" -#include "nsCRT.h" -#include "nsIPresShell.h" -#include "nsReflowCommand.h" -#include "nsPlaceholderFrame.h" -#include "nsHTMLAtoms.h" -#include "nsHTMLValue.h" #include "nsIHTMLContent.h" -#include "nsAbsoluteFrame.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIAnchoredItems.h" +#include "nsPlaceholderFrame.h" #include "nsIPtr.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLIIDs.h" +#include "nsHTMLValue.h" -#ifdef NS_DEBUG -#undef NOISY -#undef NOISY_FLOW -#else -#undef NOISY -#undef NOISY_FLOW -#endif +// XXX what do we do with catastrophic errors (rv < 0)? What is the +// state of the reflow world after such an error? -static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); -static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); -static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +#undef NOISY_REFLOW static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID); -static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID); -static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID); -static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID); static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID); -NS_DEF_PTR(nsIStyleContext); +static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); +static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); + NS_DEF_PTR(nsIContent); +NS_DEF_PTR(nsIStyleContext); -struct BlockBandData : public nsBandData { - nsBandTrapezoid data[12]; +//---------------------------------------------------------------------- - // Bounding rect of available space between any left and right floaters - nsRect availSpace; - // Constructor - BlockBandData() {size = 12; trapezoids = data;} - - // Computes the bounding rect of the available space, i.e. space between - // any left and right floaters - // - // Uses the current trapezoid data, see nsISpaceManager::GetBandData(). - // Updates member data "availSpace" - void ComputeAvailSpaceRect(); -}; - -void BlockBandData::ComputeAvailSpaceRect() +void nsBlockBandData::ComputeAvailSpaceRect() { nsBandTrapezoid* trapezoid = data; @@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect() } } -// XXX Bugs -// 1. right to left reflow can generate negative x coordinates. - -// XXX Speedup idea (all containers) - -// If I reflow a child and it gives back not-complete status then -// there is no sense in trying to pullup children. For blocks, it's a -// little more complicated unless the child is a block - if the child -// is a block, then we must be out of room hence we should stop. If -// the child is not a block then our line should be flushed (see #2 -// below) if our line is already empty then we must be out of room. - -// For inline frames and column frames, if we reflow a child and get -// back not-complete status then we should bail immediately because we -// are out of room. - -// XXX Speedup ideas: -// 1. change pullup code to use line information from next in flow -// 2. we can advance to next line immediately after reflowing something -// and noticing that it's not complete. -// 3. pass down last child information in aState so that pullup, etc., -// don't need to recompute it - -// XXX TODO: -// 0. Move justification into line flushing code - -// 1. To get ebina margins I need "auto" information from the style -// system margin's. A bottom/top margin of auto will then be computed like -// ebina computes it [however the heck that is]. - -// 2. kicking out floaters and talking with floater container to adjust -// left and right margins +//---------------------------------------------------------------------- nsBlockReflowState::nsBlockReflowState() { } -void nsBlockReflowState::Init(const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsIStyleContext* aBlockSC, - nsISpaceManager* aSpaceManager) +nsBlockReflowState::~nsBlockReflowState() { - firstLine = PR_TRUE; - allowLeadingWhitespace = PR_FALSE; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - firstChildIsInsideBullet = PR_FALSE; - nextListOrdinal = -1; - column = 0; +} - spaceManager = aSpaceManager; - currentBand = new BlockBandData; +nsresult +nsBlockReflowState::Initialize(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockFrame* aBlock) +{ + nsresult rv = NS_OK; - styleContext = aBlockSC; - styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID); - styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID); - styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID); + mPresContext = aPresContext; + mBlock = aBlock; + mSpaceManager = aSpaceManager; + mBlockIsPseudo = aBlock->IsPseudoFrame(); + mCurrentLine = nsnull; + mPrevKidFrame = nsnull; - justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) && - (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace); - - availSize.width = aMaxSize.width; - availSize.height = aMaxSize.height; - maxElementSize = aMaxElementSize; + mX = 0; + mY = 0; + mAvailSize = aMaxSize; + mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE); + mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE); + mMaxElementSizePointer = aMaxElementSize; if (nsnull != aMaxElementSize) { aMaxElementSize->width = 0; aMaxElementSize->height = 0; } + mKidXMost = 0; - kidXMost = 0; - x = 0; - y = 0; + mPrevMaxPosBottomMargin = 0; + mPrevMaxNegBottomMargin = 0; - isInline = PR_FALSE; - currentLineNumber = 0; - lineStart = nsnull; - lineLength = 0; - ascents = ascentBuf; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; - lineMaxElementSize.width = 0; - lineMaxElementSize.height = 0; - lastContentIsComplete = PR_TRUE; + mNextListOrdinal = -1; + mFirstChildIsInsideBullet = PR_FALSE; - maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]); - needRelativePos = PR_FALSE; - - prevLineLastFrame = nsnull; - prevLineHeight = 0; - topMargin = 0; - prevMaxPosBottomMargin = 0; - prevMaxNegBottomMargin = 0; - prevLineLastContentIsComplete = PR_TRUE; - - unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE); - unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE); - - reflowStatus = nsIFrame::frNotComplete; + return rv; } -nsBlockReflowState::~nsBlockReflowState() -{ - if (ascents != ascentBuf) { - delete ascents; - } - delete currentBand; -} - -void nsBlockReflowState::AddAscent(nscoord aAscent) -{ - NS_PRECONDITION(lineLength <= maxAscents, "bad line length"); - if (lineLength == maxAscents) { - maxAscents *= 2; - nscoord* newAscents = new nscoord[maxAscents]; - if (nsnull != newAscents) { - nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength); - if (ascents != ascentBuf) { - delete ascents; - } - ascents = newAscents; - } else { - // Yikes! Out of memory! - return; - } - } - ascents[lineLength] = aAscent; -} - -void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame, - nscoord aPrevLineHeight) -{ - firstLine = PR_FALSE; - allowLeadingWhitespace = PR_FALSE; - column = 0; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - lineStart = nsnull; - lineLength = 0; - currentLineNumber++; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - needRelativePos = PR_FALSE; - - prevLineLastFrame = aPrevLineLastFrame; - prevLineHeight = aPrevLineHeight; - prevMaxPosBottomMargin = maxPosBottomMargin; - prevMaxNegBottomMargin = maxNegBottomMargin; - - // Remember previous line's lastContentIsComplete - prevLineLastContentIsComplete = lastContentIsComplete; - lastContentIsComplete = PR_TRUE; - - topMargin = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; -} - -#ifdef NS_DEBUG -void nsBlockReflowState::DumpLine() -{ - nsIFrame* f = lineStart; - PRInt32 ll = lineLength; - while (--ll >= 0) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} - -void nsBlockReflowState::DumpList() -{ - nsIFrame* f = lineStart; - while (nsnull != f) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} -#endif - //---------------------------------------------------------------------- -nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, - nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) +nsresult +nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + PRInt32 aIndexInParent, + nsIFrame* aParent) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); if (nsnull == aInstancePtrResult) { @@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, } nsBlockFrame::nsBlockFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) + PRInt32 aIndexInParent, + nsIFrame* aParent) : nsHTMLContainerFrame(aContent, aIndexInParent, aParent) { } nsBlockFrame::~nsBlockFrame() { - if (nsnull != mLines) { - delete mLines; - } + DestroyLines(); } -nsresult +void nsBlockFrame::DestroyLines() +{ +} + +NS_METHOD nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) { NS_PRECONDITION(0 != aInstancePtr, "null ptr"); if (NULL == aInstancePtr) { return NS_ERROR_NULL_POINTER; } - if (aIID.Equals(kIHTMLFrameTypeIID)) { - *aInstancePtr = (void*) ((nsIHTMLFrameType*) this); + if (aIID.Equals(kBlockFrameCID)) { + *aInstancePtr = (void*) (this); return NS_OK; - } else if (aIID.Equals(kIRunaroundIID)) { + } + else if (aIID.Equals(kIRunaroundIID)) { *aInstancePtr = (void*) ((nsIRunaround*) this); return NS_OK; - } else if (aIID.Equals(kIFloaterContainerIID)) { + } + else if (aIID.Equals(kIFloaterContainerIID)) { *aInstancePtr = (void*) ((nsIFloaterContainer*) this); return NS_OK; } return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr); } -// Computes the top margin to use for this child frames based on its display -// type and the display type of the previous child frame. -// -// Adjacent vertical margins between block-level elements are collapsed. -nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC, - PRBool aIsInline) +NS_METHOD +nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const { - if (aIsInline) { - // Just use whatever the previous bottom margin was - return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin; + aIsSplittable = frSplittableNonRectangular; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, + nsIFrame* aParent, + nsIFrame*& aContinuingFrame) +{ + nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); + PrepareContinuingFrame(aCX, aParent, cf); + aContinuingFrame = cf; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::ListTag(FILE* out) const +{ + if ((nsnull != mGeometricParent) && IsPseudoFrame()) { + fprintf(out, "*block<"); + nsIAtom* atom = mContent->GetTag(); + if (nsnull != atom) { + nsAutoString tmp; + atom->ToString(tmp); + fputs(tmp, out); + } + fprintf(out, ">(%d)@%p", mIndexInParent, this); } else { - // Does the frame have a prev-in-flow? - nsIFrame* kidPrevInFlow; + nsHTMLContainerFrame::ListTag(out); + } + return NS_OK; +} - aKidFrame->GetPrevInFlow(kidPrevInFlow); +NS_METHOD +nsBlockFrame::List(FILE* out, PRInt32 aIndent) const +{ + // Indent + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); - if (nsnull == kidPrevInFlow) { + // Output the tag + ListTag(out); + + // Output the first/last content offset + fprintf(out, "[%d,%d,%c] pif=%p nif=%p", + mFirstContentOffset, mLastContentOffset, + (mLastContentIsComplete ? 'T' : 'F'), + mPrevInFlow, mNextInFlow); + + // Output the rect + out << mRect; + + // Output the children, one line at a time + if (nsnull != mLines) { + fputs("<\n", out); + aIndent++; + + nsLineData* line = mLines; + while (nsnull != line) { + line->List(out, aIndent); + line = line->mNextLine; + } + + aIndent--; + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); + fputs(">\n", out); + } else { + fputs("<>\n", out); + } + + return NS_OK; +} + +NS_METHOD +nsBlockFrame::VerifyTree() const +{ + nsresult rv = nsHTMLContainerFrame::VerifyTree(); + if (NS_OK != rv) { + return rv; + } + rv = VerifyLines(PR_TRUE); + return rv; +} + +nsresult +nsBlockFrame::VerifyLines(PRBool aFinalCheck) const +{ + nsresult rv = NS_OK; + + // Make sure that the list of children agrees with our child count. + // If this is not the case then the child list and the line list are + // not properly arranged. + PRInt32 len = LengthOf(mFirstChild); + NS_ASSERTION(mChildCount == len, "bad child list"); + + // Verify that our lines are correctly setup + PRInt32 offset = mFirstContentOffset; + PRInt32 lineChildCount = 0; + nsLineData* line = mLines; + nsLineData* prevLine = nsnull; + while (nsnull != line) { + if (aFinalCheck) { + NS_ASSERTION(((offset == line->mFirstContentOffset) && + (line->mFirstContentOffset <= line->mLastContentOffset)), + "bad line mFirstContentOffset"); + NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset, + "bad line mLastContentOffset"); + offset = line->mLastContentOffset; + if (line->mLastContentIsComplete) { + offset++; + } + } + lineChildCount += line->mChildCount; + rv = line->Verify(aFinalCheck); + if (NS_OK != rv) { + return rv; + } + prevLine = line; + line = line->mNextLine; + } + if (aFinalCheck && (nsnull != prevLine)) { + NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset, + "bad mLastContentOffset"); + NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete, + "bad mLastContentIsComplete"); + } + NS_ASSERTION(lineChildCount == mChildCount, "bad line counts"); + + return rv; +} + +//---------------------------------------------------------------------- + +// Remove a next-in-flow from from this block's list of lines + +// XXX problems here: +// 1. we always have to start from the first line: slow! +// 2. we can avoid this when the child is not the last child in a line + +void +nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow) +{ + // When a reflow indicates completion it's possible that + // next-in-flows were just removed. We have to remove them from any + // nsLineData's that follow the current line. + nsLineData* line = mLines; + while (nsnull != line) { + if (line->mFirstChild == aNextInFlow) { + // Remove child from line. + if (0 == --line->mChildCount) { + line->mFirstChild = nsnull; + } + else { + // Fixup the line + nsIFrame* nextKid; + aNextInFlow->GetNextSibling(nextKid); + line->mFirstChild = nextKid; + nextKid->GetIndexInParent(line->mFirstContentOffset); + } + break; + } + line = line->mNextLine; + } +} + +nsresult +nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize, + aMaxElementSize); + return NS_OK; +} + +nsresult +nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager, + aMaxSize, aDesiredRect, aMaxElementSize); + return NS_OK; +} + +nsLineData* +nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList) +{ + nsLineData* newLine = new nsLineData(); + if (nsnull != newLine) { + nsIFrame* kid = aOverflowList; + newLine->mFirstChild = kid; + kid->GetIndexInParent(newLine->mFirstContentOffset); + newLine->mLastContentOffset = -1; + newLine->mLastContentIsComplete = PRPackedBool(0x255); + PRInt32 kids = 0; + while (nsnull != kid) { + kids++; + kid->GetNextSibling(kid); + } + newLine->mChildCount = kids; + } + return newLine; +} + +void +nsBlockFrame::DrainOverflowList() +{ + nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow; + if (nsnull != prevBlock) { + nsIFrame* overflowList = prevBlock->mOverflowList; + if (nsnull != overflowList) { + NS_ASSERTION(nsnull == mFirstChild, "bad overflow list"); + NS_ASSERTION(nsnull == mLines, "bad overflow list"); + + // Create a line to hold the entire overflow list + nsLineData* newLine = CreateLineForOverflowList(overflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(overflowList); + prevBlock->mOverflowList = nsnull; + + // The new line is the first line + mLines = newLine; + } + } + + if (nsnull != mOverflowList) { + NS_ASSERTION(nsnull != mFirstChild, + "overflow list but no mapped children"); + + // Create a line to hold the overflow list + nsLineData* newLine = CreateLineForOverflowList(mOverflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(mOverflowList, PR_FALSE); + mOverflowList = nsnull; + + // The new line is appended after our other lines + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + while (nsnull != line) { + prevLine = line; + line = line->mNextLine; + } + if (nsnull == prevLine) { + mLines = newLine; + } + else { + prevLine->mNextLine = newLine; + newLine->mPrevLine = prevLine; + } + } +#ifdef NS_DEBUG + VerifyLines(PR_FALSE); +#endif +} + +// XXX add in code here that notices if margin's were not provided by +// the style system and when that is the case to apply the old layout +// engines margin calculations. + +nsresult +nsBlockFrame::PlaceLine(nsBlockReflowState& aState, + nsLineLayout& aLineLayout, + nsLineData* aLine) +{ + nsresult rv = NS_LINE_LAYOUT_COMPLETE; + + nscoord topMargin = 0; + nscoord bottomMargin = 0; + nscoord maxNegBottomMargin = 0; + nscoord maxPosBottomMargin = 0; + + // See if block margins apply to this line or not + PRBool isBlockLine = PR_FALSE; + if (1 == aLine->mChildCount) { + nsIStyleContextPtr kidSC; + nsIFrame* kid = aLine->mFirstChild; + rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef()); + if (NS_OK != rv) return rv; + + nsStyleDisplay* display = (nsStyleDisplay*) + kidSC->GetData(kStyleDisplaySID); + switch (display->mDisplay) { + case NS_STYLE_DISPLAY_BLOCK: + case NS_STYLE_DISPLAY_LIST_ITEM: + isBlockLine = PR_TRUE; + nsStyleSpacing* spacing = (nsStyleSpacing*) + kidSC->GetData(kStyleSpacingSID); + + // Calculate top margin by collapsing with previous bottom margin + // if any. nscoord maxNegTopMargin = 0; nscoord maxPosTopMargin = 0; - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - if (ss->mMargin.top < 0) { - maxNegTopMargin = -ss->mMargin.top; + if (spacing->mMargin.top < 0) { + maxNegTopMargin = -spacing->mMargin.top; } else { - maxPosTopMargin = ss->mMargin.top; + maxPosTopMargin = spacing->mMargin.top; } - - nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin); - nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin); - return maxPos - maxNeg; - } else { - return 0; + nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin); + nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin); + topMargin = maxPos - maxNeg; + + // Save away bottom information for later promotion into aState + if (spacing->mMargin.bottom < 0) { + maxNegBottomMargin = -spacing->mMargin.bottom; + } else { + maxPosBottomMargin = spacing->mMargin.bottom; + } + break; } } + + // Before we move the line, make sure that it will fit in it's new + // location. It always fits if the height isn't constrained or it's + // the first line. + nscoord totalHeight = topMargin + aLine->mBounds.height; + if (!aState.mUnconstrainedHeight && (aLine != mLines)) { + if (aState.mY + totalHeight > aState.mAvailSize.height) { + // The line will not fit + rv = PushLines(aState, aLine); + goto done; + } + } + if (isBlockLine) { + if (0 != topMargin) { + // We have to move the line now that we know the top margin + // for it. + aLine->MoveLineBy(0, topMargin); + aState.mY += topMargin; + } + } + else { + // Apply previous line's bottom margin before the inline-line. + nscoord bottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (0 != bottomMargin) { + aLine->MoveLineBy(0, bottomMargin); + aState.mY += topMargin; + } + } + + // Consume space and advance running values + aState.mY += aLine->mBounds.height; + aState.mPrevMaxNegBottomMargin = maxNegBottomMargin; + aState.mPrevMaxPosBottomMargin = maxPosBottomMargin; + if (nsnull != aState.mMaxElementSizePointer) { + nsSize* maxSize = aState.mMaxElementSizePointer; + if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) { + maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width; + } + if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) { + maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height; + } + } + { + nscoord xmost = aLine->mBounds.XMost(); + if (xmost > aState.mKidXMost) { + aState.mKidXMost = xmost; + } + } + + // Any below current line floaters to place? + if (aState.mPendingFloaters.Count() > 0) { + PlaceBelowCurrentLineFloaters(aState, aState.mY); + // XXX Factor in the height of the floaters as well when considering + // whether the line fits. + // The default policy is that if there isn't room for the floaters then + // both the line and the floaters are pushed to the next-in-flow... + } + + if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) { + // The current y coordinate is now past our available space + // rectangle. Get a new band of space. + GetAvailableSpace(aState, aState.mY); + } + +done: + return rv; } -void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX, - nsBlockReflowState& aState, - nscoord aY) +// aY has borderpadding.top already factored in +// aResult is relative to left,aY +nsresult +nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY) { - NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters"); + nsresult rv = NS_OK; - // XXX Factor this code with PlaceFloater()... - PRInt32 numFloaters = aState.floaterToDo.Count(); - - for (PRInt32 i = 0; i < numFloaters; i++) { - nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i]; - nsRect region; + nsISpaceManager* sm = aState.mSpaceManager; - // Get the band of available space - // XXX This is inefficient to do this inside the loop... - GetAvailableSpaceBand(aState, aY); + // Fill in band data for the specific Y coordinate + sm->Translate(aState.mBorderPadding.left, 0); + sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand); + sm->Translate(-aState.mBorderPadding.left, 0); - // Get the type of floater - nsIStyleContextPtr styleContext; - - floater->GetStyleContext(aCX, styleContext.AssignRef()); - nsStyleDisplay* sd = (nsStyleDisplay*) - styleContext->GetData(kStyleDisplaySID); - - floater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; + // Compute the bounding rect of the available space, i.e. space + // between any left and right floaters + aState.mCurrentBand.ComputeAvailSpaceRect(); - if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; - } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; - } - - // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, - 0); - mCurrentState->spaceManager->AddRectRegion(region, floater); - - // Set the origin of the floater in world coordinates - nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); - floater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); - } - aState.floaterToDo.Clear(); -} - -/** - * Flush a line out. Return true if the line fits in our available - * height. If the line does not fit then return false. When the line - * fits we advance the y coordinate, reset the x coordinate and - * prepare the nsBlockReflowState for the next line. - */ -PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - NS_PRECONDITION(aState.lineLength > 0, "bad line"); - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nscoord y = aState.y + aState.topMargin; - nscoord lineHeight; - - if (aState.isInline) { - // Vertically align the children on this line, returning the height of - // the line upon completion. - lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this, - aState.styleFont, - y, - aState.lineStart, - aState.lineLength, - aState.ascents, - aState.maxAscent); - - // Any below current line floaters to place? - if (aState.floaterToDo.Count() > 0) { - PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight); - // XXX Factor in the height of the floaters as well when considering - // whether the line fits. - // The default policy is that if there isn't room for the floaters then - // both the line and the floaters are pushed to the next-in-flow... - } - } else { - nsSize size; - - aState.lineStart->GetSize(size); - lineHeight = size.height; - } - - // The first line always fits - if (aState.currentLineNumber > 0) { - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if (y + lineHeight > yb) { - // After vertical alignment of the children and factoring in the - // proper margin, the line doesn't fit. - return PR_FALSE; - } - } - - if (aState.isInline) { - // Check if the right-edge of the line exceeds our running x-most - nscoord xMost = aState.borderPadding.left + aState.lineWidth; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - } - - // Advance the y coordinate to the new position where the next - // line or block element will go. - aState.y = y + lineHeight; - aState.x = 0; - - // Now that the vertical alignment is done we can perform horizontal - // alignment and relative positioning. Skip all of these if we are - // doing an unconstrained (in x) reflow. There's no point in doing - // the work if we *know* we are going to reflowed again. - if (!aState.unconstrainedWidth) { - nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText, - aState.lineStart, aState.lineLength, - aState.lineWidth, - aState.availSize.width); - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - if (!aState.justifying) { - nsCSSLayout::RelativePositionChildren(aCX, this, - aState.lineStart, - aState.lineLength); - } - } - - // Record line length - aState.lineLengths.AppendElement((void*)aState.lineLength); - - // Find the last frame in the line - // XXX keep this as running state in the nsBlockReflowState - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - // Update maxElementSize - if (nsnull != aState.maxElementSize) { - nsSize& lineMax = aState.lineMaxElementSize; - nsSize* maxMax = aState.maxElementSize; - if (lineMax.width > maxMax->width) { - maxMax->width = lineMax.width; - } - if (lineMax.height > maxMax->height) { - maxMax->height = lineMax.height; - } - aState.lineMaxElementSize.width = 0; - aState.lineMaxElementSize.height = 0; - } - - // Advance to the next line - aState.AdvanceToNextLine(lastFrame, lineHeight); - - return PR_TRUE; -} - -/** - * Add an inline child to the current line. Advance various running - * values after placement. - */ -void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsReflowMetrics& aKidSize, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) -{ - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; - } - - // Place and size the child - // XXX add in left margin from kid - nsRect r; - r.y = aState.y; - r.width = aKidSize.width; - r.height = aKidSize.height; - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - // Left to right positioning. - r.x = aState.borderPadding.left + aState.x + ss->mMargin.left; - aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right; - } else { - // Right to left positioning - // XXX what should we do when aState.x goes negative??? - r.x = aState.x - aState.borderPadding.right - ss->mMargin.right - - aKidSize.width; - aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left; - } - aKidFrame->SetRect(r); - aState.AddAscent(aKidSize.ascent); - aState.lineWidth += aKidSize.width; - aState.lineLength++; - - // Update maximums for the line - if (aKidSize.ascent > aState.maxAscent) { - aState.maxAscent = aKidSize.ascent; - } - if (aKidSize.descent > aState.maxDescent) { - aState.maxDescent = aKidSize.descent; - } - // Update running margin maximums - if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) { - // XXX temporary code. Since the molecule for the bullet frame - // is the same as the LI frame, we get bad style information. - // ignore it. - } else { - nscoord margin; #if 0 - // XXX CSS2 spec says that top/bottom margin don't affect line height - // calculation. We're waiting for clarification on this issue... - if ((margin = ss->mMargin.top) < 0) { - margin = -margin; - if (margin > aState.maxNegTopMargin) { - aState.maxNegTopMargin = margin; - } - } else { - if (margin > aState.maxPosTopMargin) { - aState.maxPosTopMargin = margin; - } - } + // XXX For now we assume that there are no height restrictions + // (e.g. no "float to bottom of column/page") + nsRect& availSpace = aState.mCurrentBand.availSpace; + aResult.x = availSpace.x; + aResult.y = availSpace.y; + aResult.width = availSpace.width; + aResult.height = aState.mAvailSize.height; +#else + aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left, + aState.mY); #endif - if ((margin = ss->mMargin.bottom) < 0) { - margin = -margin; - if (margin > aState.maxNegBottomMargin) { - aState.maxNegBottomMargin = margin; - } - } else { - if (margin > aState.maxPosBottomMargin) { - aState.maxPosBottomMargin = margin; - } - } - } - // Update line max element size - nsSize& mes = aState.lineMaxElementSize; - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > mes.width) { - mes.width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > mes.height) { - mes.height = aKidMaxElementSize->height; - } - } + return rv; } -// Places and sizes the block-level element, and advances the line. -// The rect is in the local coordinate space of the kid frame. -void nsBlockFrame::AddBlockChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsRect& aKidRect, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) +// Give aLine and any successive lines to the block's next-in-flow; if +// we don't have a next-in-flow then push all the children onto our +// overflow list. +nsresult +nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine) { - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); + PRInt32 i; - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; + // Split our child-list in two; revert our last content offset and + // completion status to the previous line. + nsLineData* prevLine = aLine->mPrevLine; + NS_PRECONDITION(nsnull != prevLine, "pushing first line"); + nsIFrame* prevKidFrame = prevLine->mFirstChild; + for (i = prevLine->mChildCount - 1; --i >= 0; ) { + prevKidFrame->GetNextSibling(prevKidFrame); } - - // Translate from the kid's coordinate space to our coordinate space - aKidRect.x += aState.borderPadding.left + ss->mMargin.left; - aKidRect.y += aState.y + aState.topMargin; - - // Place and size the child - aKidFrame->SetRect(aKidRect); - - aState.AddAscent(aKidRect.height); - aState.lineLength++; - - // Is this the widest child frame? - nscoord xMost = aKidRect.XMost() + ss->mMargin.right; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - - // Update the max element size - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > aState.maxElementSize->width) { - aState.maxElementSize->width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > aState.maxElementSize->height) { - aState.maxElementSize->height = aKidMaxElementSize->height; - } - } - - // and the bottom line margin information which we'll use when placing - // the next child - if (ss->mMargin.bottom < 0) { - aState.maxNegBottomMargin = -ss->mMargin.bottom; - } else { - aState.maxPosBottomMargin = ss->mMargin.bottom; - } - - // Update the running y-offset - aState.y += aKidRect.height + aState.topMargin; - - // Apply relative positioning if necessary - nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1); - - // Advance to the next line - aState.AdvanceToNextLine(aKidFrame, aKidRect.height); -} - -/** - * Compute the available size for reflowing the given child at the - * current x,y position in the state. Note that this may return - * negative or zero width/height's if we are out of room. - */ -void nsBlockFrame::GetAvailSize(nsSize& aResult, - nsBlockReflowState& aState, - nsIStyleContext* aKidSC, - PRBool aIsInline) -{ - // Determine the maximum available reflow height for the child - nscoord yb = aState.borderPadding.top + aState.availSize.height; - aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE : - yb - aState.y - aState.topMargin; - - // Determine the maximum available reflow width for the child - if (aState.unconstrainedWidth) { - aResult.width = NS_UNCONSTRAINEDSIZE; - } else if (aIsInline) { - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - aResult.width = aState.currentBand->availSpace.XMost() - aState.x; - } else { - aResult.width = aState.x - aState.currentBand->availSpace.x; - } - } else { - // It's a block. Don't adjust for the left/right margin here. That happens - // later on once we know the current left/right edge - aResult.width = aState.availSize.width; - } -} - -/** - * Push all of the kids that we have not reflowed, starting at - * aState.lineStart. aPrevKid is the kid previous to aState.lineStart - * and is also our last child. Note that line length is NOT a - * reflection of the number of children we are actually pushing - * (because we don't break the sibling list as we add children to the - * line). - */ -void nsBlockFrame::PushKids(nsBlockReflowState& aState) -{ - nsIFrame* prevFrame = aState.prevLineLastFrame; - NS_PRECONDITION(nsnull != prevFrame, "pushing all kids"); #ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevFrame->GetNextSibling(nextSibling); - NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line"); + nsIFrame* nextFrame; + prevKidFrame->GetNextSibling(nextFrame); + NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list"); #endif + prevKidFrame->SetNextSibling(nsnull); + prevLine->mNextLine = nsnull; + mLastContentOffset = prevLine->mLastContentOffset; + mLastContentIsComplete = prevLine->mLastContentIsComplete; + + // Push children to our next-in-flow if we have, or to our overflow list + nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; + PRInt32 pushCount = 0; + if (nsnull == nextInFlow) { + // Place children on the overflow list + mOverflowList = aLine->mFirstChild; + + // Destroy the lines + nsLineData* line = aLine; + while (nsnull != line) { + pushCount += line->mChildCount; + nsLineData* next = line->mNextLine; + delete line; + line = next; + } + } + else { + aLine->mPrevLine = nsnull; + + // Pass on the children to our next-in-flow + nsLineData* line = aLine; + prevLine = line; + nsIFrame* lastKid = aLine->mFirstChild; + nsIFrame* kid = lastKid; + while (nsnull != line) { + i = line->mChildCount; + pushCount += i; + NS_ASSERTION(kid == line->mFirstChild, "bad line list"); + while (--i >= 0) { + kid->SetGeometricParent(nextInFlow); + nsIFrame* contentParent; + kid->GetContentParent(contentParent); + if (this == contentParent) { + kid->SetContentParent(nextInFlow); + } + lastKid = kid; + kid->GetNextSibling(kid); + } + prevLine = line; + line = line->mNextLine; + } + + // Join the two line lists + nsLineData* nextInFlowLine = nextInFlow->mLines; + if (nsnull != nextInFlowLine) { + lastKid->SetNextSibling(nextInFlowLine->mFirstChild); + nextInFlowLine->mPrevLine = prevLine; + } + nsIFrame* firstKid = aLine->mFirstChild; + prevLine->mNextLine = nextInFlowLine; + nextInFlow->mLines = aLine; + nextInFlow->mFirstChild = firstKid; + nextInFlow->mChildCount += pushCount; + firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset); #ifdef NS_DEBUG - PRInt32 numKids = LengthOf(mFirstChild); - NS_ASSERTION(numKids == mChildCount, "bad child count"); + nextInFlow->VerifyLines(PR_FALSE); +#endif + } + mChildCount -= pushCount; + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return NS_LINE_LAYOUT_NOT_COMPLETE; +} + +nsresult +nsBlockFrame::ReflowMapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != line) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // If we have no children and we have a prev-in-flow then we need to + // pick up where it left off. If we have children, e.g. we're being + // resized, then our content offset will have already been set + // correctly. + nsIFrame* kidPrevInFlow = nsnull; + if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { + nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; + mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary? + if (PR_FALSE == prev->mLastContentIsComplete) { + // Our prev-in-flow's last child is not complete + prev->LastChild(kidPrevInFlow); + } + } + + // Get to the last line where the new content may be added + nsLineData* line = nsnull; + nsLineData* prevLine = nsnull; + if (nsnull != mLines) { + line = mLines; + while (nsnull != line->mNextLine) { + line = line->mNextLine; + } + prevLine = line; + + // If the last line is not complete then kidPrevInFlow should be + // set to the last-line's last child. + if (!prevLine->mLastContentIsComplete) { + kidPrevInFlow = prevLine->GetLastChild(); + } + } + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + // Now reflow the new content until we are out of new content or out + // of vertical space. + PRInt32 kidIndex = NextChildOffset(); + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + lineLayout.mKidPrevInFlow = kidPrevInFlow; + PRInt32 contentChildCount = mContent->ChildCount(); + while (kidIndex < contentChildCount) { + if (nsnull == line) { + if (!MoreToReflow(aState)) { + break; + } + line = new nsLineData(); + if (nsnull == line) { + rv = NS_ERROR_OUT_OF_MEMORY; + goto done; + } + line->mFirstContentOffset = kidIndex; + } + + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mKidPrevInFlow = kidPrevInFlow; + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Add line to the block; do this before placing the line in case + // PushLines is needed. + if (nsnull == prevLine) { + // For the first line, initialize mFirstContentOffset + mFirstContentOffset = line->mFirstContentOffset; + mFirstChild = line->mFirstChild; + mLines = line; + } + else { + prevLine->mNextLine = line; + line->mPrevLine = prevLine; + } + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + kidIndex = lineLayout.mKidIndex; + kidPrevInFlow = lineLayout.mKidPrevInFlow; + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; + if (aState.mBlockIsPseudo) { + PropagateContentOffsets(); + } + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::InitializeState(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockReflowState& aState) +{ + nsresult rv; + rv = aState.Initialize(aPresContext, aSpaceManager, + aMaxSize, aMaxElementSize, this); + + nsStyleSpacing* mySpacing = (nsStyleSpacing*) + mStyleContext->GetData(kStyleSpacingSID); + + // Apply border and padding adjustments for regular frames only + if (!aState.mBlockIsPseudo) { + aState.mY = mySpacing->mBorderPadding.top; + aState.mX = mySpacing->mBorderPadding.left; + aState.mAvailSize.width -= + (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); + aState.mAvailSize.height -= + (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); + aState.mBorderPadding = mySpacing->mBorderPadding; + } + else { + aState.mBorderPadding.SizeTo(0, 0, 0, 0); + } + + // Setup initial list ordinal value + nsIAtom* tag = mContent->GetTag(); + if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || + (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { + nsHTMLValue value; + if (eContentAttr_HasValue == + ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { + if (eHTMLUnit_Integer == value.GetUnit()) { + aState.mNextListOrdinal = value.GetIntValue(); + } + } + } + NS_RELEASE(tag); + + return rv; +} + +#if XXX +NS_METHOD +nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight) +{ + printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth), + NS_TWIPS_TO_POINTS_FLOAT(aHeight)); + return nsHTMLContainerFrame::SizeTo(aWidth, aHeight); +} + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, desiredRect, aStatus); + aDesiredSize.width = desiredRect.width; + aDesiredSize.height = desiredRect.height; + aDesiredSize.ascent = aDesiredSize.height; + aDesiredSize.descent = 0; + } + return rv; +} #endif -#ifdef NOISY - ListTag(stdout); - printf(": push kids (childCount=%d)\n", mChildCount); - DumpFlow(); -#endif - - PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete); - SetLastContentOffset(prevFrame); - - // Set mLastContentIsComplete to the previous lines last content is - // complete now that the previous line's last child is our last - // child. - mLastContentIsComplete = aState.prevLineLastContentIsComplete; - - // Fix up child count - // XXX is there a better way? aState.lineLength doesn't work because - // we might be pushing more than just the pending line. - nsIFrame* kid = mFirstChild; - PRInt32 kids = 0; - while (nsnull != kid) { - kids++; - kid->GetNextSibling(kid); - } - mChildCount = kids; - - // Make sure we have no lingering line data - aState.lineLength = 0; - aState.lineStart = nsnull; - -#ifdef NOISY - ListTag(stdout); - printf(": push kids done (childCount=%d) [%c]\n", mChildCount, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif -} - -/** - * Gets a band of available space starting at the specified y-offset. Assumes - * the local coordinate space is currently set to the upper-left origin of the - * bounding rect - * - * Updates "currentBand" and "x" member data of the block reflow state - */ -void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY) -{ - // Gets a band of available space. - aState.spaceManager->Translate(aState.borderPadding.left, 0); - aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand); - - // Compute the bounding rect of the available space, i.e. space between any - // left and right floaters - aState.currentBand->ComputeAvailSpaceRect(); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - aState.x = aState.currentBand->availSpace.x; -} - -void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear) -{ - // Translate the coordinate space - aState.spaceManager->Translate(aState.borderPadding.left, 0); - -getBand: - nscoord y = aState.y + aState.topMargin; - PRBool isLeftFloater = PR_FALSE; - PRBool isRightFloater = PR_FALSE; - - // Get a band of available space - aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand); - - // Scan the trapezoids looking for left and right floaters - nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids; - for (PRInt32 i = 0; i < aState.currentBand->count; i++) { - // XXX Handle multiply occupied - if (nsBandTrapezoid::smOccupied == trapezoid->state) { - nsStyleDisplay* display; - - trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display); - if (NS_STYLE_FLOAT_LEFT == display->mFloats) { - isLeftFloater = PR_TRUE; - } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) { - isRightFloater = PR_TRUE; - } - } - - trapezoid++; - } - - if (isLeftFloater) { - if ((aClear == NS_STYLE_CLEAR_LEFT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - if (isRightFloater) { - if ((aClear == NS_STYLE_CLEAR_RIGHT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - - aState.spaceManager->Translate(-aState.borderPadding.left, 0); -} - -// Bit's for PlaceAndReflowChild return value -#define PLACE_FIT 0x1 -#define PLACE_FLOWED 0x2 - -PRIntn -nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC) -{ - nsSize kidMaxElementSize; - nsSize* pKidMaxElementSize = - (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; - - // Get line start setup if we are at the start of a new line - if (nsnull == aState.lineStart) { - NS_ASSERTION(0 == aState.lineLength, "bad line length"); - aState.lineStart = aKidFrame; - } - - // Get kid and its style - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - - // Figure out if kid is a block element or not - PRBool isInline = PR_TRUE; - PRIntn display = styleDisplay->mDisplay; - if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) { - // XXX Special hack for properly reflowing bullets that have the - // inside value for list-style-position. - display = NS_STYLE_DISPLAY_INLINE; - } - if ((NS_STYLE_DISPLAY_BLOCK == display) || - (NS_STYLE_DISPLAY_LIST_ITEM == display)) { - // Block elements always end up on the next line (unless they are - // already at the start of the line). - isInline = PR_FALSE; - if (aState.lineLength > 0) { - aState.breakAfterChild = PR_TRUE; - } - } - - // Handle forced break first - if (aState.breakAfterChild) { - NS_ASSERTION(aState.lineStart != aKidFrame, "bad line"); - - // Get the last child in the current line - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - if (!AdvanceToNextLine(aCX, aState)) { - // The previous line didn't fit. - return 0; - } - aState.lineStart = aKidFrame; - - // Get the style for the last child, and see if it wanted to clear - // floaters. This handles the BR tag, which is the only inline - // element for which clear applies - nsIStyleContextPtr lastChildSC; - - lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef()); - nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*) - lastChildSC->GetData(kStyleDisplaySID); - switch (lastChildDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, lastChildDisplay->mBreakType); - break; - } - } - - // Now that we've handled force breaks (and maybe called AdvanceToNextLine() - // which checks), remember whether it's an inline frame - aState.isInline = isInline; - - // If we're at the beginning of a line then compute the top margin that we - // should use - if (aState.lineStart == aKidFrame) { - // Compute the top margin to use for this line - aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC, - aState.isInline); - - // If it's an inline element then get a band of available space - // - // XXX If we have a current band and there's unused space in that band - // then avoid this call to get a band... - if (aState.isInline) { - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - } - } - - // Compute the available space to reflow the child into and then - // reflow it into that space. - nsSize kidAvailSize; - GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - - ReflowStatus status; - - if (aState.isInline) { - nsReflowMetrics kidSize; - - // Inline elements are never passed the space manager - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - NS_ASSERTION(nsnull != aState.lineStart, "bad line start"); - - if (aKidFrame == aState.lineStart) { - // Width always fits when we are at the logical left margin. - // Just check the height. - // - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall - return PLACE_FLOWED; - } - } else { - // Examine state and if the breakBeforeChild is set and we - // aren't already on the new line, do the forcing now. - // XXX Why aren't we doing this check BEFORE we resize reflow the child? - if (aState.breakBeforeChild) { - aState.breakBeforeChild = PR_FALSE; - if (aKidFrame != aState.lineStart) { - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow child now that it has the line to itself - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - } - } - - // When we are not at the logical left margin then we need - // to check the width first. If we are too wide then advance - // to the next line and try reflowing again. - if (kidSize.width > kidAvailSize.width) { - // Too wide. Try next line - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow splittable children - SplittableType isSplittable; - - aKidFrame->IsSplittable(isSplittable); - if (isSplittable != frNotSplittable) { - // Update size info now that we are on the next line. Then - // reflow the child into the new available space. - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - } - - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall on the next line - return PLACE_FLOWED; - } - // It's ok if it's too wide on the next line. - } - } - } - - // Add child to the line - AddInlineChildToLine(aCX, aState, aKidFrame, kidSize, - pKidMaxElementSize, aKidSC); - } else { - nsRect kidRect; - - // Does the block-level element want to clear any floaters that impact - // it? Note that the clear property only applies to block-level elements - // and the BR tag - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - switch (styleDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, styleDisplay->mBreakType); - GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - break; - } - - // Give the block its own local coordinate space.. Note: ReflowChild() - // will adjust for the child's left/right margin after determining the - // current left/right edge - aState.spaceManager->Translate(aState.borderPadding.left, 0); - // Give the block-level element the opportunity to use the space manager - status = ReflowChild(aKidFrame, aCX, aState.spaceManager, - kidAvailSize, kidRect, pKidMaxElementSize); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - // Block elements always fit horizontally (because they are - // always placed at the logical left margin). Check to see if - // the block fits vertically - if (kidRect.YMost() > kidAvailSize.height) { - // Nope - return PLACE_FLOWED; - } - } - - // Add block child - // XXX We need to set lastContentIsComplete here, because AddBlockChild() - // calls AdvaneceToNextLine(). We need to restructure the flow of control, - // and use a state machine... - aState.lastContentIsComplete = PRBool(status == frComplete); - AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC); - } - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - - aState.lastContentIsComplete = PRBool(status == frComplete); - if (aState.isInline && (frNotComplete == status)) { - // Since the inline child didn't complete its reflow we *know* - // that a continuation of it can't possibly fit on the current - // line. Therefore, set a flag in the state that will cause the - // a line break before the next frame is placed. - aState.breakAfterChild = PR_TRUE; - } - - aState.reflowStatus = status; - return PLACE_FLOWED | PLACE_FIT; -} - -/** - * Reflow the existing frames. - * - * @param aCX presentation context to use - * @param aState in out parameter which tracks the state of - * reflow for the block frame. - * @return true if we successfully reflowed all the mapped children and false - * otherwise, e.g. we pushed children to the next in flow - */ PRBool -nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsIFrame* kidFrame; - - for (kidFrame = mFirstChild; nsnull != kidFrame; ) { - - /* we get the kid's style from kidFrame's cached style context */ - nsIStyleContextPtr kidSC; - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - - // Attempt to place and reflow the child - - // XXX if child is not splittable and it fits just place it where - // it is, otherwise advance to the next line and place it there if - // possible - - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - ReflowStatus status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // The child doesn't fit. Push it and any remaining children. - PushKids(aState); - result = PR_FALSE; - goto push_done; - } - - // Is the child complete? - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (frComplete == status) { - // Yes, the child is complete - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); - } else { - // No the child isn't complete - if (nsnull == kidNextInFlow) { - // The child doesn't have a next-in-flow so create a continuing - // frame. This hooks the child into the flow - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); - - // Add the continuing frame to the sibling list - nsIFrame* nextSib; - - kidFrame->GetNextSibling(nextSib); - continuingFrame->SetNextSibling(nextSib); - kidFrame->SetNextSibling(continuingFrame); - mChildCount++; - } - - // Unlike the inline frame code we can't assume that we used - // up all of our space because the child's reflow status is - // frNotComplete. Instead, the child is probably split and - // we need to reflow the continuations as well. - } - - // Get the next child frame - kidFrame->GetNextSibling(kidFrame); - } - - push_done: -#ifdef NS_DEBUG - nsIFrame* lastChild; - PRInt32 lastIndexInParent; - - LastChild(lastChild); - lastChild->GetIndexInParent(lastIndexInParent); - NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset"); - - PRInt32 len = LengthOf(mFirstChild); - NS_POSTCONDITION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -/* XXX: - * In this method, we seem to be getting the style for the next child, - * and checking that child's style for the child's display type. - * The problem is that it's very inefficient to ResolveStyleFor(kid) here - * and then just throw it away, when we end up doing a ResolveStyleFor(kid) - * again when we actually create a frame for the content. - * It would be nice to cache the resolved style, maybe in the reflow state? - */ -PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) +nsBlockFrame::MoreToReflow(nsBlockReflowState& aState) { PRBool rv = PR_FALSE; - if (IsPseudoFrame()) { + if (aState.mBlockIsPseudo) { // Get the next content object that we would like to reflow PRInt32 kidIndex = NextChildOffset(); nsIContentPtr kid = mContent->ChildAt(kidIndex); if (kid.IsNotNull()) { // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); + nsIStyleContextPtr kidSC = + aState.mPresContext->ResolveStyleContextFor(kid, this); nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*) kidSC->GetData(kStyleDisplaySID); switch (kidStyleDisplay->mDisplay) { @@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) return rv; } -/** - * Create new frames for content we haven't yet mapped - * - * @param aCX presentation context to use - * @return frComplete if all content has been mapped and frNotComplete - * if we should be continued - */ -nsIFrame::ReflowStatus -nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) +nsBlockReflowState* +nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext, + nsIFrame* aFrame) { -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif - nsIFrame* kidPrevInFlow = nsnull; - ReflowStatus result = frNotComplete; - - // If we have no children and we have a prev-in-flow then we need to pick - // up where it left off. If we have children, e.g. we're being resized, then - // our content offset should already be set correctly... - if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { - nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; - NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow"); - mFirstContentOffset = prev->NextChildOffset(); - if (PR_FALSE == prev->mLastContentIsComplete) { - // Our prev-in-flow's last child is not complete - prev->LastChild(kidPrevInFlow); - } - } - - // Place our children, one at a time until we are out of children - PRInt32 kidIndex = NextChildOffset(); - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (kid.IsNull()) { - result = frComplete; - break; - } - - // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - nsStylePosition* kidPosition = (nsStylePosition*) - kidSC->GetData(kStylePositionSID); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); - - // Check whether it wants to floated or absolutely positioned - if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) { - AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (nsnull == kidPrevInFlow) { - // Create initial frame for the child - nsIContentDelegate* kidDel; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // If we're being used as a pseudo frame, i.e. we map the same - // content as our parent then we want to indicate we're complete; - // otherwise we'll be continued and go on mapping children... - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - result = frComplete; - goto done; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aCX); - kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow"); - nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); + nsBlockReflowState* state = nsnull; + if (nsnull != aFrame) { + nsIFrame* parent; + aFrame->GetGeometricParent(parent); + while (nsnull != parent) { + nsBlockFrame* block; + nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block); + if (NS_OK == rv) { + nsIPresShell* shell = aPresContext->GetShell(); + state = (nsBlockReflowState*) shell->GetCachedData(block); + NS_RELEASE(shell); break; } - kidFrame->SetStyleContext(aCX,kidSC); - } else { - // Since kid has a prev-in-flow, use that to create the next - // frame. - kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame); + parent->GetGeometricParent(parent); } - - // Link child frame into the list of children. If the frame ends - // up not fitting and getting pushed, the PushKids code will fixup - // the child count for us. - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - mChildCount++; - - // Reflow child frame as many times as necessary until it is - // complete. - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // We ran out of room. - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - goto push_done; - } - - // Did the child complete? - prevKidFrame = kidFrame; - if (frNotComplete == status) { - // Child didn't complete so create a continuing frame - kidPrevInFlow = kidFrame; - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to the sibling list - nsIFrame* kidNextSibling; - - kidFrame->GetNextSibling(kidNextSibling); - continuingFrame->SetNextSibling(kidNextSibling); - kidFrame->SetNextSibling(continuingFrame); - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "huh?"); -#endif - } while (frNotComplete == status); - - // The child that we just reflowed is complete -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); -#endif - kidIndex++; - kidPrevInFlow = nsnull; } - - done: - // To get here we either completely reflowed all our appended - // children OR we are a pseudo-frame and we ran into a block - // element. In either case our last content MUST be complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - - push_done: -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - return result; + return state; } -/** - * Pullup frames from our next in flow and try to place them. Before - * this is called our previously mapped children, if any have been - * reflowed which means that the block reflow state's x and y - * coordinates and other data are ready to go. - * - * Return true if we pulled everything up. - */ -PRBool -nsBlockFrame::PullUpChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - while (nsnull != nextInFlow) { - // Get first available frame from the next-in-flow - nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame); - if (nsnull == kidFrame) { - // We've pulled up all the children from that next-in-flow, so - // move to the next next-in-flow. - nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; - continue; - } - - // Get style information for the pulled up kid - nsIContentPtr kid; - - kidFrame->GetContent(kid.AssignRef()); - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // Push the kids that didn't fit back down to the next-in-flow - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - result = PR_FALSE; - goto push_done; - } - - if (frNotComplete == status) { - // Child is not complete - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (nsnull == kidNextInFlow) { - // Create a continuing frame for the incomplete child - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to our sibling list. - nsIFrame* nextSibling; - - kidFrame->GetNextSibling(nextSibling); - continuingFrame->SetNextSibling(nextSibling); - kidFrame->SetNextSibling(continuingFrame); - prevKidFrame = kidFrame; - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } else { - // The child has a next-in-flow, but it's not one of ours. - // It *must* be in one of our next-in-flows. Collect it - // then. - NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow"); - break; - } - } - } while (frNotComplete == status); - - prevKidFrame = kidFrame; - } - - if (nsnull != prevKidFrame) { - // The only way we can get here is by pulling up every last child - // in our next-in-flows (and reflowing any continunations they - // have). Therefore we KNOW that our last child is complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - } - - push_done:; - - if (result == PR_FALSE) { - // If our next-in-flow is empty OR our next next-in-flow is empty - // then adjust the offsets of all of the empty next-in-flows. - nextInFlow = (nsBlockFrame*) mNextInFlow; - if ((0 == nextInFlow->mChildCount) || - ((nsnull != nextInFlow->mNextInFlow) && - (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) { - // We didn't pullup everything and we need to fixup one of our - // next-in-flows content offsets. - AdjustOffsetOfEmptyNextInFlows(); - } - } - - -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -void nsBlockFrame::SetupState(nsIPresContext* aCX, - nsBlockReflowState& aState, - const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsISpaceManager* aSpaceManager) -{ - // Setup reflow state - aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager); - - nsStyleSpacing* mySpacing = (nsStyleSpacing*) - mStyleContext->GetData(kStyleSpacingSID); - - // Apply border and padding adjustments for regular frames only - if (PR_FALSE == IsPseudoFrame()) { - aState.borderPadding = mySpacing->mBorderPadding; - aState.y = mySpacing->mBorderPadding.top; - aState.availSize.width -= - (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); - aState.availSize.height -= - (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); - } else { - aState.borderPadding.SizeTo(0, 0, 0, 0); - } - - // Setup initial list ordinal value - nsIAtom* tag = mContent->GetTag(); - if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || - (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { - nsHTMLValue value; - if (eContentAttr_HasValue == - ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { - if (eHTMLUnit_Integer == value.GetUnit()) { - aState.nextListOrdinal = value.GetIntValue(); - } - } - } - NS_RELEASE(tag); - - mCurrentState = &aState; -} - -#include "nsUnitConversion.h"/* XXX */ -NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsSize* aMaxElementSize, - ReflowStatus& aStatus) -{ - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager); - return DoResizeReflow(aCX, state, aDesiredRect, aStatus); -} - -nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsRect& aDesiredRect, - ReflowStatus& aStatus) +nsresult +nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + ReflowStatus& aStatus) { #ifdef NS_DEBUG + VerifyLines(PR_TRUE); PreReflowCheck(); #endif -#ifdef NOISY +#ifdef NOISY_REFLOW ListTag(stdout); - printf(": resize reflow %g,%g\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height)); - DumpFlow(); + printf(": Before:\n"); + List(stdout, 1); #endif - // Zap old line data - if (nsnull != mLines) { - delete mLines; - mLines = nsnull; - } - mNumLines = 0; + nsresult rv = NS_OK; - // Check for an overflow list - MoveOverflowToChildList(); - - // Before we start reflowing, cache a pointer to our state structure - // so that inline frames can find it. - nsIPresShell* shell = aCX->GetShell(); + nsIPresShell* shell = aState.mPresContext->GetShell(); shell->PutCachedData(this, &aState); - // First reflow any existing frames - PRBool reflowMappedOK = PR_TRUE; - aStatus = frComplete; - if (nsnull != mFirstChild) { - reflowMappedOK = ReflowMappedChildren(aCX, aState); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } + // Check for an overflow list + DrainOverflowList(); + + if (nsnull != mLines) { + rv = ReflowMapped(aState); } - if (reflowMappedOK) { - // Any space left? - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if ((nsnull != mFirstChild) && (aState.y >= yb)) { - // No space left. Don't try to pull-up children or reflow - // unmapped. We need to return the correct completion status, - // so see if there is more to reflow. - if (MoreToReflow(aCX)) { - aStatus = frNotComplete; - } - } else if (MoreToReflow(aCX)) { - // Try and pull-up some children from a next-in-flow - if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) { - // If we still have unmapped children then create some new frames - if (MoreToReflow(aCX)) { - aStatus = ReflowAppendedChildren(aCX, aState); - } - } else { - // We were unable to pull-up all the existing frames from the - // next in flow - aStatus = frNotComplete; - } + if (NS_OK == rv) { + if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) { + // We are out of space } - } - - // Deal with last line - make sure it gets vertically and - // horizontally aligned. This also updates the state's y coordinate - // which is good because that's how we size ourselves. - if (0 != aState.lineLength) { - if (!AdvanceToNextLine(aCX, aState)) { - // The last line of output doesn't fit. Push all of the kids to - // the next in flow and change our reflow status to not complete - // so that we are continued. -#ifdef NOISY - ListTag(stdout); - printf(": pushing kids since last line doesn't fit\n"); -#endif - - PushKids(aState); - aStatus = frNotComplete; + if (MoreToReflow(aState)) { + rv = ReflowUnmapped(aState); } } - if (frComplete == aStatus) { - // Don't forget to add in the bottom margin from our last child. - // Only add it in if there is room for it. - nscoord margin = aState.prevMaxPosBottomMargin - - aState.prevMaxNegBottomMargin; - nscoord y = aState.y + margin; - if (y <= aState.borderPadding.top + aState.availSize.height) { - aState.y = y; - } - } - - // Now that reflow has finished, remove the cached pointer - shell->RemoveCachedData(this); - NS_RELEASE(shell); - - // Translate state.lineLengths into an integer array - mNumLines = aState.lineLengths.Count(); - if (mNumLines > 0) { - mLines = new PRInt32[mNumLines]; - for (PRInt32 i = 0; i < mNumLines; i++) { - PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i); - mLines[i] = ll; - } - } - - if (!aState.unconstrainedWidth && aState.justifying) { - // Perform justification now that we know how many lines we have. - JustifyLines(aCX, aState); - } - // Return our desired rect and our status aDesiredRect.x = 0; aDesiredRect.y = 0; - aDesiredRect.width = aState.kidXMost + aState.borderPadding.right; - if (!aState.unconstrainedWidth) { + aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right; + if (!aState.mUnconstrainedWidth) { // Make sure we're at least as wide as the max size we were given - nscoord maxWidth = aState.availSize.width + aState.borderPadding.left + - aState.borderPadding.right; - + nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left + + aState.mBorderPadding.right; if (aDesiredRect.width < maxWidth) { aDesiredRect.width = maxWidth; } } - aState.y += aState.borderPadding.bottom; - aDesiredRect.height = aState.y; - -#ifdef NS_DEBUG - PostReflowCheck(aStatus); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height), - ((status == frNotComplete) ? "NOT " : ""), - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F') - ); - DumpFlow(); -#endif - mCurrentState = nsnull; - return NS_OK; -} - -void nsBlockFrame::JustifyLines(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - // XXX we don't justify the last line; what if we are continued, - // should we do it then? - nsIFrame* kid = mFirstChild; - for (PRInt32 i = 0; i < mNumLines; i++) { - nsIFrame* lineStart = kid; - PRInt32 lineLength = mLines[i]; - if (i < mNumLines - 1) { - if (1 == lineLength) { - // For lines with one element on them we can take a shortcut and - // let them do the justification. Note that we still call - // JustifyReflow even if the available space is zero in case the - // child is a hunk of text that ends in whitespace. The whitespace - // will be distributed elsewhere causing a proper flush right look - // for the last word. - nsRect r; - kid->GetRect(r); - nscoord maxWidth = aState.availSize.width; - nscoord availableSpace = maxWidth - r.width; - nscoord maxAvailSpace = nscoord(maxWidth * 0.1f); - if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) { - kid->JustifyReflow(aCX, availableSpace); - kid->SizeTo(r.width + availableSpace, r.height); - } - kid->GetNextSibling(kid); - } else { - // XXX Get justification of multiple elements working - while (--lineLength >= 0) { - kid->GetNextSibling(kid); - } + aState.mY += aState.mBorderPadding.bottom; + nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) { + // It's possible that we don't have room for the last bottom + // margin (the last bottom margin is the margin following a block + // element that we contain; it isn't applied immediately because + // of the margin collapsing logic). This can happen when we are + // reflowed in a limited amount of space because we don't know in + // advance what the last bottom margin will be. + nscoord maxY = aMaxSize.height; + if (aState.mY + lastBottomMargin > maxY) { + lastBottomMargin = maxY - aState.mY; + if (lastBottomMargin < 0) { + lastBottomMargin = 0; } } - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]); + aState.mY += lastBottomMargin; } -} + aDesiredRect.height = aState.mY; -NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const -{ - aIsSplittable = frSplittableNonRectangular; - return NS_OK; -} - -NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, - nsIFrame* aParent, - nsIFrame*& aContinuingFrame) -{ - nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); - PrepareContinuingFrame(aCX, aParent, cf); - aContinuingFrame = cf; - return NS_OK; -} - -NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsReflowCommand& aReflowCommand, - ReflowStatus& aStatus) -{ + // Set return status aStatus = frComplete; - - if (aReflowCommand.GetTarget() == this) { - // XXX for now, just do a complete reflow mapped (it'll kinda - // work, but it's slow) - - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager); - PRBool reflowMappedOK = ReflowMappedChildren(aCX, state); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } - } else { - // XXX not yet implemented - NS_ABORT(); - // XXX work to compute initial state goes *HERE* - aDesiredRect.x = 0; - aDesiredRect.y = 0; - aDesiredRect.width = 0; - aDesiredRect.height = 0; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; } +#ifdef NS_DEBUG + // Verify that the line layout code pulled everything up when it + // indicates a complete reflow. + if (frComplete == aStatus) { + nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow; + while (nsnull != nextBlock) { + NS_ASSERTION((nsnull == nextBlock->mLines) && + (nextBlock->mChildCount == 0) && + (nsnull == nextBlock->mFirstChild), + "bad completion status"); + nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow; + } - mCurrentState = nsnull; - return NS_OK; +#if XXX + // We better not be in the same parent frame as our prev-in-flow. + // If we are it means that we were continued instead of pulling up + // children. + if (nsnull != mPrevInFlow) { + nsIFrame* ourParent = mGeometricParent; + nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent; + NS_ASSERTION(ourParent != prevParent, "bad continuation"); + } +#endif + } +#endif + + // Now that reflow has finished, remove the cached pointer + shell->RemoveCachedData(this); + NS_RELEASE(shell); + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); + PostReflowCheck(aStatus); +#endif +#ifdef NOISY_REFLOW + ListTag(stdout); + printf(": After:\n"); + List(stdout, 1); +#endif + return rv; } -PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ContentAppended(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentInserted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentReplaced(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentDeleted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext, + nsReflowMetrics& aMetrics) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aSpaceManager, aMaxSize, + aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus); + } + return rv; +} + +NS_METHOD +nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsReflowCommand& aReflowCommand, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +PRBool +nsBlockFrame::AddFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + + if (nsnull != state) { + // Get the frame associated with the space manager, and get its + // nsIAnchoredItems interface + nsIFrame* frame = state->mSpaceManager->GetFrame(); + nsIAnchoredItems* anchoredItems = nsnull; + + frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); + NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); + if (nsnull != anchoredItems) { + anchoredItems->AddAnchoredItem(aFloater, + nsIAnchoredItems::anHTMLFloater, + this); + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + return PR_TRUE; + } + } + + return PR_FALSE; +} + +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + if (nsnull != state) { + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + } +} + +PRBool +nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) { do { nsIFrame* parent; - aFrame->GetGeometricParent(parent); - // See if there are any non-zero sized child frames that precede aFrame - // in the child list + // See if there are any non-zero sized child frames that precede + // aFrame in the child list nsIFrame* child; - parent->FirstChild(child); while ((nsnull != child) && (aFrame != child)) { nsSize size; @@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // We found a non-zero sized child frame that precedes aFrame return PR_FALSE; } - child->GetNextSibling(child); } @@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // Walk up one level and check that its parent is left-most as well aFrame = parent; } while (aFrame != this); - return PR_TRUE; } -PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) -{ - // Get the frame associated with the space manager, and get its nsIAnchoredItems - // interface - nsIFrame* frame = mCurrentState->spaceManager->GetFrame(); - nsIAnchoredItems* anchoredItems = nsnull; - - frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); - NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); - - if (nsnull != anchoredItems) { - anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this); - PlaceFloater(aCX, aFloater, aPlaceholder); - return PR_TRUE; - } - - return PR_FALSE; -} - -// XXX The size of the floater needs to be taken into consideration if we're -// computing a maximum element size -void nsBlockFrame::PlaceFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder, + nsBlockReflowState& aState) { // If the floater is the left-most non-zero size child frame then insert // it before the current line; otherwise add it to the below-current-line // todo list and we'll handle it when we flush out the line if (IsLeftMostChild(aPlaceholder)) { - // Get the type of floater - nsIStyleContextPtr styleContext; + nsISpaceManager* sm = aState.mSpaceManager; - aFloater->GetStyleContext(aCX, styleContext.AssignRef()); + // Get the type of floater + nsIStyleContextPtr styleContext; + aFloater->GetStyleContext(aPresContext, styleContext.AssignRef()); nsStyleDisplay* floaterDisplay = (nsStyleDisplay*) styleContext->GetData(kStyleDisplaySID); - if (!mCurrentState->isInline) { - // Get the current band for this line - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); - } - - // Commit some space in the space manager - nsRect region; - + // Commit some space in the space manager and adjust our current + // band of available space. + nsRect region; aFloater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; - + region.y = aState.mY; if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; + region.x = aState.mX; } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, + "bad float type"); + region.x = aState.mCurrentBand.availSpace.XMost() - region.width; } // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0); - mCurrentState->spaceManager->AddRectRegion(region, aFloater); + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, aFloater); // Set the origin of the floater in world coordinates nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); + sm->GetTranslation(worldX, worldY); aFloater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); + sm->Translate(-aState.mBorderPadding.left, 0); - // Update the band of available space to reflect space taken up by the floater - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); + // Update the band of available space to reflect space taken up by + // the floater + GetAvailableSpace(aState, aState.mY); + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); + } } else { // Add the floater to our to-do list - mCurrentState->floaterToDo.AppendElement(aFloater); + aState.mPendingFloaters.AppendElement(aFloater); } } -NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell, - nsIPresContext* aPresContext, - nsIContent* aContainer) +void +nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState, + nscoord aY) { - // Get the last-in-flow - nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow(); - PRInt32 kidIndex = flow->NextChildOffset(); - PRInt32 startIndex = kidIndex; + NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters"); -#if 0 - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - flow->LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (nsnull == kid) { - break; - } + nsISpaceManager* sm = aState.mSpaceManager; + nsBlockBandData* bd = &aState.mCurrentBand; - // Resolve style for the kid - nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); + // XXX Factor this code with PlaceFloater()... + PRInt32 numFloaters = aState.mPendingFloaters.Count(); + for (PRInt32 i = 0; i < numFloaters; i++) { + nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i]; + nsRect region; - // Is it a floater? - if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); + // Get the band of available space + // XXX This is inefficient to do this inside the loop... + GetAvailableSpace(aState, aY); + + // Get the type of floater + nsIStyleContextPtr styleContext; + floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef()); + nsStyleDisplay* sd = (nsStyleDisplay*) + styleContext->GetData(kStyleDisplaySID); + + floater->GetRect(region); + region.y = bd->availSpace.y; + if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { + region.x = bd->availSpace.x; } else { - // Create initial frame for the child - nsIContentDelegate* kidDel; - nsresult fr; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // Update last content offset now that we are done drawing - // children from our parent. - SetLastContentOffset(prevKidFrame); - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - return NS_OK; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aPresContext); - kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); - break; - } + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); + region.x = bd->availSpace.XMost() - region.width; } - kidFrame->SetStyleContext(aCX,kidSC); - // Link child frame into the list of children - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; + // XXX Don't forget the floater's margins... + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, floater); - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - kidIndex++; - mChildCount++; + // Set the origin of the floater in world coordinates + nscoord worldX, worldY; + sm->GetTranslation(worldX, worldY); + floater->MoveTo(region.x + worldX, region.y + worldY); + sm->Translate(-aState.mBorderPadding.left, 0); } - SetLastContentOffset(prevKidFrame); -#endif - // If this is a pseudo-frame then our parent will generate the - // reflow command. Otherwise, if the container is us then we should - // generate the reflow command because we were directly called. - if (!IsPseudoFrame() && (aContainer == mContent)) { - nsReflowCommand* rc = - new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended, - startIndex); - aShell->AppendReflowCommand(rc); + aState.mPendingFloaters.Clear(); + + // Pass on updated available space to the current line + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); } - return NS_OK; } -PRIntn nsBlockFrame::GetSkipSides() const +//---------------------------------------------------------------------- + +nsLineData* +nsBlockFrame::GetFirstLine() +{ + return mLines; +} + +PRIntn +nsBlockFrame::GetSkipSides() const { PRIntn skip = 0; if (nsnull != mPrevInFlow) { @@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const } return skip; } - -nsHTMLFrameType nsBlockFrame::GetFrameType() const -{ - return eHTMLFrame_Block; -} - -NS_METHOD nsBlockFrame::ListTag(FILE* out) const -{ - if ((nsnull != mGeometricParent) && IsPseudoFrame()) { - fprintf(out, "*block<"); - nsIAtom* atom = mContent->GetTag(); - if (nsnull != atom) { - nsAutoString tmp; - atom->ToString(tmp); - fputs(tmp, out); - } - fprintf(out, ">(%d)@%p", mIndexInParent, this); - } else { - nsHTMLContainerFrame::ListTag(out); - } - return NS_OK; -} - -#ifdef NS_DEBUG -void nsBlockFrame::DumpFlow() const -{ -#ifdef NOISY_FLOW - nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow; - while (flow != 0) { - printf(" %p: [%d,%d,%c]\n", - flow, flow->mFirstContentOffset, flow->mLastContentOffset, - (flow->mLastContentIsComplete ? 'T' : 'F')); - flow = (nsBlockFrame*) flow->mNextInFlow; - } -#endif -} -#endif diff --git a/layout/generic/nsBlockReflowState.h b/layout/generic/nsBlockReflowState.h index 59d7407ec5b1..0228ca80d413 100644 --- a/layout/generic/nsBlockReflowState.h +++ b/layout/generic/nsBlockReflowState.h @@ -16,66 +16,37 @@ * Reserved. */ #include "nsBlockFrame.h" -#include "nsSize.h" -#include "nsIAnchoredItems.h" -#include "nsIContent.h" -#include "nsIContentDelegate.h" -#include "nsISpaceManager.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" -#include "nsIPresContext.h" -#include "nsMargin.h" -#include "nsHTMLIIDs.h" -#include "nsCSSLayout.h" -#include "nsCRT.h" -#include "nsIPresShell.h" -#include "nsReflowCommand.h" -#include "nsPlaceholderFrame.h" -#include "nsHTMLAtoms.h" -#include "nsHTMLValue.h" #include "nsIHTMLContent.h" -#include "nsAbsoluteFrame.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIAnchoredItems.h" +#include "nsPlaceholderFrame.h" #include "nsIPtr.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLIIDs.h" +#include "nsHTMLValue.h" -#ifdef NS_DEBUG -#undef NOISY -#undef NOISY_FLOW -#else -#undef NOISY -#undef NOISY_FLOW -#endif +// XXX what do we do with catastrophic errors (rv < 0)? What is the +// state of the reflow world after such an error? -static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); -static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); -static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +#undef NOISY_REFLOW static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID); -static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID); -static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID); -static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID); static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID); -NS_DEF_PTR(nsIStyleContext); +static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); +static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); + NS_DEF_PTR(nsIContent); +NS_DEF_PTR(nsIStyleContext); -struct BlockBandData : public nsBandData { - nsBandTrapezoid data[12]; +//---------------------------------------------------------------------- - // Bounding rect of available space between any left and right floaters - nsRect availSpace; - // Constructor - BlockBandData() {size = 12; trapezoids = data;} - - // Computes the bounding rect of the available space, i.e. space between - // any left and right floaters - // - // Uses the current trapezoid data, see nsISpaceManager::GetBandData(). - // Updates member data "availSpace" - void ComputeAvailSpaceRect(); -}; - -void BlockBandData::ComputeAvailSpaceRect() +void nsBlockBandData::ComputeAvailSpaceRect() { nsBandTrapezoid* trapezoid = data; @@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect() } } -// XXX Bugs -// 1. right to left reflow can generate negative x coordinates. - -// XXX Speedup idea (all containers) - -// If I reflow a child and it gives back not-complete status then -// there is no sense in trying to pullup children. For blocks, it's a -// little more complicated unless the child is a block - if the child -// is a block, then we must be out of room hence we should stop. If -// the child is not a block then our line should be flushed (see #2 -// below) if our line is already empty then we must be out of room. - -// For inline frames and column frames, if we reflow a child and get -// back not-complete status then we should bail immediately because we -// are out of room. - -// XXX Speedup ideas: -// 1. change pullup code to use line information from next in flow -// 2. we can advance to next line immediately after reflowing something -// and noticing that it's not complete. -// 3. pass down last child information in aState so that pullup, etc., -// don't need to recompute it - -// XXX TODO: -// 0. Move justification into line flushing code - -// 1. To get ebina margins I need "auto" information from the style -// system margin's. A bottom/top margin of auto will then be computed like -// ebina computes it [however the heck that is]. - -// 2. kicking out floaters and talking with floater container to adjust -// left and right margins +//---------------------------------------------------------------------- nsBlockReflowState::nsBlockReflowState() { } -void nsBlockReflowState::Init(const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsIStyleContext* aBlockSC, - nsISpaceManager* aSpaceManager) +nsBlockReflowState::~nsBlockReflowState() { - firstLine = PR_TRUE; - allowLeadingWhitespace = PR_FALSE; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - firstChildIsInsideBullet = PR_FALSE; - nextListOrdinal = -1; - column = 0; +} - spaceManager = aSpaceManager; - currentBand = new BlockBandData; +nsresult +nsBlockReflowState::Initialize(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockFrame* aBlock) +{ + nsresult rv = NS_OK; - styleContext = aBlockSC; - styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID); - styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID); - styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID); + mPresContext = aPresContext; + mBlock = aBlock; + mSpaceManager = aSpaceManager; + mBlockIsPseudo = aBlock->IsPseudoFrame(); + mCurrentLine = nsnull; + mPrevKidFrame = nsnull; - justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) && - (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace); - - availSize.width = aMaxSize.width; - availSize.height = aMaxSize.height; - maxElementSize = aMaxElementSize; + mX = 0; + mY = 0; + mAvailSize = aMaxSize; + mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE); + mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE); + mMaxElementSizePointer = aMaxElementSize; if (nsnull != aMaxElementSize) { aMaxElementSize->width = 0; aMaxElementSize->height = 0; } + mKidXMost = 0; - kidXMost = 0; - x = 0; - y = 0; + mPrevMaxPosBottomMargin = 0; + mPrevMaxNegBottomMargin = 0; - isInline = PR_FALSE; - currentLineNumber = 0; - lineStart = nsnull; - lineLength = 0; - ascents = ascentBuf; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; - lineMaxElementSize.width = 0; - lineMaxElementSize.height = 0; - lastContentIsComplete = PR_TRUE; + mNextListOrdinal = -1; + mFirstChildIsInsideBullet = PR_FALSE; - maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]); - needRelativePos = PR_FALSE; - - prevLineLastFrame = nsnull; - prevLineHeight = 0; - topMargin = 0; - prevMaxPosBottomMargin = 0; - prevMaxNegBottomMargin = 0; - prevLineLastContentIsComplete = PR_TRUE; - - unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE); - unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE); - - reflowStatus = nsIFrame::frNotComplete; + return rv; } -nsBlockReflowState::~nsBlockReflowState() -{ - if (ascents != ascentBuf) { - delete ascents; - } - delete currentBand; -} - -void nsBlockReflowState::AddAscent(nscoord aAscent) -{ - NS_PRECONDITION(lineLength <= maxAscents, "bad line length"); - if (lineLength == maxAscents) { - maxAscents *= 2; - nscoord* newAscents = new nscoord[maxAscents]; - if (nsnull != newAscents) { - nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength); - if (ascents != ascentBuf) { - delete ascents; - } - ascents = newAscents; - } else { - // Yikes! Out of memory! - return; - } - } - ascents[lineLength] = aAscent; -} - -void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame, - nscoord aPrevLineHeight) -{ - firstLine = PR_FALSE; - allowLeadingWhitespace = PR_FALSE; - column = 0; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - lineStart = nsnull; - lineLength = 0; - currentLineNumber++; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - needRelativePos = PR_FALSE; - - prevLineLastFrame = aPrevLineLastFrame; - prevLineHeight = aPrevLineHeight; - prevMaxPosBottomMargin = maxPosBottomMargin; - prevMaxNegBottomMargin = maxNegBottomMargin; - - // Remember previous line's lastContentIsComplete - prevLineLastContentIsComplete = lastContentIsComplete; - lastContentIsComplete = PR_TRUE; - - topMargin = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; -} - -#ifdef NS_DEBUG -void nsBlockReflowState::DumpLine() -{ - nsIFrame* f = lineStart; - PRInt32 ll = lineLength; - while (--ll >= 0) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} - -void nsBlockReflowState::DumpList() -{ - nsIFrame* f = lineStart; - while (nsnull != f) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} -#endif - //---------------------------------------------------------------------- -nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, - nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) +nsresult +nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + PRInt32 aIndexInParent, + nsIFrame* aParent) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); if (nsnull == aInstancePtrResult) { @@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, } nsBlockFrame::nsBlockFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) + PRInt32 aIndexInParent, + nsIFrame* aParent) : nsHTMLContainerFrame(aContent, aIndexInParent, aParent) { } nsBlockFrame::~nsBlockFrame() { - if (nsnull != mLines) { - delete mLines; - } + DestroyLines(); } -nsresult +void nsBlockFrame::DestroyLines() +{ +} + +NS_METHOD nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) { NS_PRECONDITION(0 != aInstancePtr, "null ptr"); if (NULL == aInstancePtr) { return NS_ERROR_NULL_POINTER; } - if (aIID.Equals(kIHTMLFrameTypeIID)) { - *aInstancePtr = (void*) ((nsIHTMLFrameType*) this); + if (aIID.Equals(kBlockFrameCID)) { + *aInstancePtr = (void*) (this); return NS_OK; - } else if (aIID.Equals(kIRunaroundIID)) { + } + else if (aIID.Equals(kIRunaroundIID)) { *aInstancePtr = (void*) ((nsIRunaround*) this); return NS_OK; - } else if (aIID.Equals(kIFloaterContainerIID)) { + } + else if (aIID.Equals(kIFloaterContainerIID)) { *aInstancePtr = (void*) ((nsIFloaterContainer*) this); return NS_OK; } return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr); } -// Computes the top margin to use for this child frames based on its display -// type and the display type of the previous child frame. -// -// Adjacent vertical margins between block-level elements are collapsed. -nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC, - PRBool aIsInline) +NS_METHOD +nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const { - if (aIsInline) { - // Just use whatever the previous bottom margin was - return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin; + aIsSplittable = frSplittableNonRectangular; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, + nsIFrame* aParent, + nsIFrame*& aContinuingFrame) +{ + nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); + PrepareContinuingFrame(aCX, aParent, cf); + aContinuingFrame = cf; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::ListTag(FILE* out) const +{ + if ((nsnull != mGeometricParent) && IsPseudoFrame()) { + fprintf(out, "*block<"); + nsIAtom* atom = mContent->GetTag(); + if (nsnull != atom) { + nsAutoString tmp; + atom->ToString(tmp); + fputs(tmp, out); + } + fprintf(out, ">(%d)@%p", mIndexInParent, this); } else { - // Does the frame have a prev-in-flow? - nsIFrame* kidPrevInFlow; + nsHTMLContainerFrame::ListTag(out); + } + return NS_OK; +} - aKidFrame->GetPrevInFlow(kidPrevInFlow); +NS_METHOD +nsBlockFrame::List(FILE* out, PRInt32 aIndent) const +{ + // Indent + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); - if (nsnull == kidPrevInFlow) { + // Output the tag + ListTag(out); + + // Output the first/last content offset + fprintf(out, "[%d,%d,%c] pif=%p nif=%p", + mFirstContentOffset, mLastContentOffset, + (mLastContentIsComplete ? 'T' : 'F'), + mPrevInFlow, mNextInFlow); + + // Output the rect + out << mRect; + + // Output the children, one line at a time + if (nsnull != mLines) { + fputs("<\n", out); + aIndent++; + + nsLineData* line = mLines; + while (nsnull != line) { + line->List(out, aIndent); + line = line->mNextLine; + } + + aIndent--; + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); + fputs(">\n", out); + } else { + fputs("<>\n", out); + } + + return NS_OK; +} + +NS_METHOD +nsBlockFrame::VerifyTree() const +{ + nsresult rv = nsHTMLContainerFrame::VerifyTree(); + if (NS_OK != rv) { + return rv; + } + rv = VerifyLines(PR_TRUE); + return rv; +} + +nsresult +nsBlockFrame::VerifyLines(PRBool aFinalCheck) const +{ + nsresult rv = NS_OK; + + // Make sure that the list of children agrees with our child count. + // If this is not the case then the child list and the line list are + // not properly arranged. + PRInt32 len = LengthOf(mFirstChild); + NS_ASSERTION(mChildCount == len, "bad child list"); + + // Verify that our lines are correctly setup + PRInt32 offset = mFirstContentOffset; + PRInt32 lineChildCount = 0; + nsLineData* line = mLines; + nsLineData* prevLine = nsnull; + while (nsnull != line) { + if (aFinalCheck) { + NS_ASSERTION(((offset == line->mFirstContentOffset) && + (line->mFirstContentOffset <= line->mLastContentOffset)), + "bad line mFirstContentOffset"); + NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset, + "bad line mLastContentOffset"); + offset = line->mLastContentOffset; + if (line->mLastContentIsComplete) { + offset++; + } + } + lineChildCount += line->mChildCount; + rv = line->Verify(aFinalCheck); + if (NS_OK != rv) { + return rv; + } + prevLine = line; + line = line->mNextLine; + } + if (aFinalCheck && (nsnull != prevLine)) { + NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset, + "bad mLastContentOffset"); + NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete, + "bad mLastContentIsComplete"); + } + NS_ASSERTION(lineChildCount == mChildCount, "bad line counts"); + + return rv; +} + +//---------------------------------------------------------------------- + +// Remove a next-in-flow from from this block's list of lines + +// XXX problems here: +// 1. we always have to start from the first line: slow! +// 2. we can avoid this when the child is not the last child in a line + +void +nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow) +{ + // When a reflow indicates completion it's possible that + // next-in-flows were just removed. We have to remove them from any + // nsLineData's that follow the current line. + nsLineData* line = mLines; + while (nsnull != line) { + if (line->mFirstChild == aNextInFlow) { + // Remove child from line. + if (0 == --line->mChildCount) { + line->mFirstChild = nsnull; + } + else { + // Fixup the line + nsIFrame* nextKid; + aNextInFlow->GetNextSibling(nextKid); + line->mFirstChild = nextKid; + nextKid->GetIndexInParent(line->mFirstContentOffset); + } + break; + } + line = line->mNextLine; + } +} + +nsresult +nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize, + aMaxElementSize); + return NS_OK; +} + +nsresult +nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager, + aMaxSize, aDesiredRect, aMaxElementSize); + return NS_OK; +} + +nsLineData* +nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList) +{ + nsLineData* newLine = new nsLineData(); + if (nsnull != newLine) { + nsIFrame* kid = aOverflowList; + newLine->mFirstChild = kid; + kid->GetIndexInParent(newLine->mFirstContentOffset); + newLine->mLastContentOffset = -1; + newLine->mLastContentIsComplete = PRPackedBool(0x255); + PRInt32 kids = 0; + while (nsnull != kid) { + kids++; + kid->GetNextSibling(kid); + } + newLine->mChildCount = kids; + } + return newLine; +} + +void +nsBlockFrame::DrainOverflowList() +{ + nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow; + if (nsnull != prevBlock) { + nsIFrame* overflowList = prevBlock->mOverflowList; + if (nsnull != overflowList) { + NS_ASSERTION(nsnull == mFirstChild, "bad overflow list"); + NS_ASSERTION(nsnull == mLines, "bad overflow list"); + + // Create a line to hold the entire overflow list + nsLineData* newLine = CreateLineForOverflowList(overflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(overflowList); + prevBlock->mOverflowList = nsnull; + + // The new line is the first line + mLines = newLine; + } + } + + if (nsnull != mOverflowList) { + NS_ASSERTION(nsnull != mFirstChild, + "overflow list but no mapped children"); + + // Create a line to hold the overflow list + nsLineData* newLine = CreateLineForOverflowList(mOverflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(mOverflowList, PR_FALSE); + mOverflowList = nsnull; + + // The new line is appended after our other lines + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + while (nsnull != line) { + prevLine = line; + line = line->mNextLine; + } + if (nsnull == prevLine) { + mLines = newLine; + } + else { + prevLine->mNextLine = newLine; + newLine->mPrevLine = prevLine; + } + } +#ifdef NS_DEBUG + VerifyLines(PR_FALSE); +#endif +} + +// XXX add in code here that notices if margin's were not provided by +// the style system and when that is the case to apply the old layout +// engines margin calculations. + +nsresult +nsBlockFrame::PlaceLine(nsBlockReflowState& aState, + nsLineLayout& aLineLayout, + nsLineData* aLine) +{ + nsresult rv = NS_LINE_LAYOUT_COMPLETE; + + nscoord topMargin = 0; + nscoord bottomMargin = 0; + nscoord maxNegBottomMargin = 0; + nscoord maxPosBottomMargin = 0; + + // See if block margins apply to this line or not + PRBool isBlockLine = PR_FALSE; + if (1 == aLine->mChildCount) { + nsIStyleContextPtr kidSC; + nsIFrame* kid = aLine->mFirstChild; + rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef()); + if (NS_OK != rv) return rv; + + nsStyleDisplay* display = (nsStyleDisplay*) + kidSC->GetData(kStyleDisplaySID); + switch (display->mDisplay) { + case NS_STYLE_DISPLAY_BLOCK: + case NS_STYLE_DISPLAY_LIST_ITEM: + isBlockLine = PR_TRUE; + nsStyleSpacing* spacing = (nsStyleSpacing*) + kidSC->GetData(kStyleSpacingSID); + + // Calculate top margin by collapsing with previous bottom margin + // if any. nscoord maxNegTopMargin = 0; nscoord maxPosTopMargin = 0; - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - if (ss->mMargin.top < 0) { - maxNegTopMargin = -ss->mMargin.top; + if (spacing->mMargin.top < 0) { + maxNegTopMargin = -spacing->mMargin.top; } else { - maxPosTopMargin = ss->mMargin.top; + maxPosTopMargin = spacing->mMargin.top; } - - nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin); - nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin); - return maxPos - maxNeg; - } else { - return 0; + nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin); + nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin); + topMargin = maxPos - maxNeg; + + // Save away bottom information for later promotion into aState + if (spacing->mMargin.bottom < 0) { + maxNegBottomMargin = -spacing->mMargin.bottom; + } else { + maxPosBottomMargin = spacing->mMargin.bottom; + } + break; } } + + // Before we move the line, make sure that it will fit in it's new + // location. It always fits if the height isn't constrained or it's + // the first line. + nscoord totalHeight = topMargin + aLine->mBounds.height; + if (!aState.mUnconstrainedHeight && (aLine != mLines)) { + if (aState.mY + totalHeight > aState.mAvailSize.height) { + // The line will not fit + rv = PushLines(aState, aLine); + goto done; + } + } + if (isBlockLine) { + if (0 != topMargin) { + // We have to move the line now that we know the top margin + // for it. + aLine->MoveLineBy(0, topMargin); + aState.mY += topMargin; + } + } + else { + // Apply previous line's bottom margin before the inline-line. + nscoord bottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (0 != bottomMargin) { + aLine->MoveLineBy(0, bottomMargin); + aState.mY += topMargin; + } + } + + // Consume space and advance running values + aState.mY += aLine->mBounds.height; + aState.mPrevMaxNegBottomMargin = maxNegBottomMargin; + aState.mPrevMaxPosBottomMargin = maxPosBottomMargin; + if (nsnull != aState.mMaxElementSizePointer) { + nsSize* maxSize = aState.mMaxElementSizePointer; + if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) { + maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width; + } + if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) { + maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height; + } + } + { + nscoord xmost = aLine->mBounds.XMost(); + if (xmost > aState.mKidXMost) { + aState.mKidXMost = xmost; + } + } + + // Any below current line floaters to place? + if (aState.mPendingFloaters.Count() > 0) { + PlaceBelowCurrentLineFloaters(aState, aState.mY); + // XXX Factor in the height of the floaters as well when considering + // whether the line fits. + // The default policy is that if there isn't room for the floaters then + // both the line and the floaters are pushed to the next-in-flow... + } + + if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) { + // The current y coordinate is now past our available space + // rectangle. Get a new band of space. + GetAvailableSpace(aState, aState.mY); + } + +done: + return rv; } -void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX, - nsBlockReflowState& aState, - nscoord aY) +// aY has borderpadding.top already factored in +// aResult is relative to left,aY +nsresult +nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY) { - NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters"); + nsresult rv = NS_OK; - // XXX Factor this code with PlaceFloater()... - PRInt32 numFloaters = aState.floaterToDo.Count(); - - for (PRInt32 i = 0; i < numFloaters; i++) { - nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i]; - nsRect region; + nsISpaceManager* sm = aState.mSpaceManager; - // Get the band of available space - // XXX This is inefficient to do this inside the loop... - GetAvailableSpaceBand(aState, aY); + // Fill in band data for the specific Y coordinate + sm->Translate(aState.mBorderPadding.left, 0); + sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand); + sm->Translate(-aState.mBorderPadding.left, 0); - // Get the type of floater - nsIStyleContextPtr styleContext; - - floater->GetStyleContext(aCX, styleContext.AssignRef()); - nsStyleDisplay* sd = (nsStyleDisplay*) - styleContext->GetData(kStyleDisplaySID); - - floater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; + // Compute the bounding rect of the available space, i.e. space + // between any left and right floaters + aState.mCurrentBand.ComputeAvailSpaceRect(); - if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; - } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; - } - - // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, - 0); - mCurrentState->spaceManager->AddRectRegion(region, floater); - - // Set the origin of the floater in world coordinates - nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); - floater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); - } - aState.floaterToDo.Clear(); -} - -/** - * Flush a line out. Return true if the line fits in our available - * height. If the line does not fit then return false. When the line - * fits we advance the y coordinate, reset the x coordinate and - * prepare the nsBlockReflowState for the next line. - */ -PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - NS_PRECONDITION(aState.lineLength > 0, "bad line"); - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nscoord y = aState.y + aState.topMargin; - nscoord lineHeight; - - if (aState.isInline) { - // Vertically align the children on this line, returning the height of - // the line upon completion. - lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this, - aState.styleFont, - y, - aState.lineStart, - aState.lineLength, - aState.ascents, - aState.maxAscent); - - // Any below current line floaters to place? - if (aState.floaterToDo.Count() > 0) { - PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight); - // XXX Factor in the height of the floaters as well when considering - // whether the line fits. - // The default policy is that if there isn't room for the floaters then - // both the line and the floaters are pushed to the next-in-flow... - } - } else { - nsSize size; - - aState.lineStart->GetSize(size); - lineHeight = size.height; - } - - // The first line always fits - if (aState.currentLineNumber > 0) { - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if (y + lineHeight > yb) { - // After vertical alignment of the children and factoring in the - // proper margin, the line doesn't fit. - return PR_FALSE; - } - } - - if (aState.isInline) { - // Check if the right-edge of the line exceeds our running x-most - nscoord xMost = aState.borderPadding.left + aState.lineWidth; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - } - - // Advance the y coordinate to the new position where the next - // line or block element will go. - aState.y = y + lineHeight; - aState.x = 0; - - // Now that the vertical alignment is done we can perform horizontal - // alignment and relative positioning. Skip all of these if we are - // doing an unconstrained (in x) reflow. There's no point in doing - // the work if we *know* we are going to reflowed again. - if (!aState.unconstrainedWidth) { - nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText, - aState.lineStart, aState.lineLength, - aState.lineWidth, - aState.availSize.width); - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - if (!aState.justifying) { - nsCSSLayout::RelativePositionChildren(aCX, this, - aState.lineStart, - aState.lineLength); - } - } - - // Record line length - aState.lineLengths.AppendElement((void*)aState.lineLength); - - // Find the last frame in the line - // XXX keep this as running state in the nsBlockReflowState - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - // Update maxElementSize - if (nsnull != aState.maxElementSize) { - nsSize& lineMax = aState.lineMaxElementSize; - nsSize* maxMax = aState.maxElementSize; - if (lineMax.width > maxMax->width) { - maxMax->width = lineMax.width; - } - if (lineMax.height > maxMax->height) { - maxMax->height = lineMax.height; - } - aState.lineMaxElementSize.width = 0; - aState.lineMaxElementSize.height = 0; - } - - // Advance to the next line - aState.AdvanceToNextLine(lastFrame, lineHeight); - - return PR_TRUE; -} - -/** - * Add an inline child to the current line. Advance various running - * values after placement. - */ -void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsReflowMetrics& aKidSize, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) -{ - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; - } - - // Place and size the child - // XXX add in left margin from kid - nsRect r; - r.y = aState.y; - r.width = aKidSize.width; - r.height = aKidSize.height; - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - // Left to right positioning. - r.x = aState.borderPadding.left + aState.x + ss->mMargin.left; - aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right; - } else { - // Right to left positioning - // XXX what should we do when aState.x goes negative??? - r.x = aState.x - aState.borderPadding.right - ss->mMargin.right - - aKidSize.width; - aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left; - } - aKidFrame->SetRect(r); - aState.AddAscent(aKidSize.ascent); - aState.lineWidth += aKidSize.width; - aState.lineLength++; - - // Update maximums for the line - if (aKidSize.ascent > aState.maxAscent) { - aState.maxAscent = aKidSize.ascent; - } - if (aKidSize.descent > aState.maxDescent) { - aState.maxDescent = aKidSize.descent; - } - // Update running margin maximums - if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) { - // XXX temporary code. Since the molecule for the bullet frame - // is the same as the LI frame, we get bad style information. - // ignore it. - } else { - nscoord margin; #if 0 - // XXX CSS2 spec says that top/bottom margin don't affect line height - // calculation. We're waiting for clarification on this issue... - if ((margin = ss->mMargin.top) < 0) { - margin = -margin; - if (margin > aState.maxNegTopMargin) { - aState.maxNegTopMargin = margin; - } - } else { - if (margin > aState.maxPosTopMargin) { - aState.maxPosTopMargin = margin; - } - } + // XXX For now we assume that there are no height restrictions + // (e.g. no "float to bottom of column/page") + nsRect& availSpace = aState.mCurrentBand.availSpace; + aResult.x = availSpace.x; + aResult.y = availSpace.y; + aResult.width = availSpace.width; + aResult.height = aState.mAvailSize.height; +#else + aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left, + aState.mY); #endif - if ((margin = ss->mMargin.bottom) < 0) { - margin = -margin; - if (margin > aState.maxNegBottomMargin) { - aState.maxNegBottomMargin = margin; - } - } else { - if (margin > aState.maxPosBottomMargin) { - aState.maxPosBottomMargin = margin; - } - } - } - // Update line max element size - nsSize& mes = aState.lineMaxElementSize; - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > mes.width) { - mes.width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > mes.height) { - mes.height = aKidMaxElementSize->height; - } - } + return rv; } -// Places and sizes the block-level element, and advances the line. -// The rect is in the local coordinate space of the kid frame. -void nsBlockFrame::AddBlockChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsRect& aKidRect, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) +// Give aLine and any successive lines to the block's next-in-flow; if +// we don't have a next-in-flow then push all the children onto our +// overflow list. +nsresult +nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine) { - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); + PRInt32 i; - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; + // Split our child-list in two; revert our last content offset and + // completion status to the previous line. + nsLineData* prevLine = aLine->mPrevLine; + NS_PRECONDITION(nsnull != prevLine, "pushing first line"); + nsIFrame* prevKidFrame = prevLine->mFirstChild; + for (i = prevLine->mChildCount - 1; --i >= 0; ) { + prevKidFrame->GetNextSibling(prevKidFrame); } - - // Translate from the kid's coordinate space to our coordinate space - aKidRect.x += aState.borderPadding.left + ss->mMargin.left; - aKidRect.y += aState.y + aState.topMargin; - - // Place and size the child - aKidFrame->SetRect(aKidRect); - - aState.AddAscent(aKidRect.height); - aState.lineLength++; - - // Is this the widest child frame? - nscoord xMost = aKidRect.XMost() + ss->mMargin.right; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - - // Update the max element size - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > aState.maxElementSize->width) { - aState.maxElementSize->width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > aState.maxElementSize->height) { - aState.maxElementSize->height = aKidMaxElementSize->height; - } - } - - // and the bottom line margin information which we'll use when placing - // the next child - if (ss->mMargin.bottom < 0) { - aState.maxNegBottomMargin = -ss->mMargin.bottom; - } else { - aState.maxPosBottomMargin = ss->mMargin.bottom; - } - - // Update the running y-offset - aState.y += aKidRect.height + aState.topMargin; - - // Apply relative positioning if necessary - nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1); - - // Advance to the next line - aState.AdvanceToNextLine(aKidFrame, aKidRect.height); -} - -/** - * Compute the available size for reflowing the given child at the - * current x,y position in the state. Note that this may return - * negative or zero width/height's if we are out of room. - */ -void nsBlockFrame::GetAvailSize(nsSize& aResult, - nsBlockReflowState& aState, - nsIStyleContext* aKidSC, - PRBool aIsInline) -{ - // Determine the maximum available reflow height for the child - nscoord yb = aState.borderPadding.top + aState.availSize.height; - aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE : - yb - aState.y - aState.topMargin; - - // Determine the maximum available reflow width for the child - if (aState.unconstrainedWidth) { - aResult.width = NS_UNCONSTRAINEDSIZE; - } else if (aIsInline) { - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - aResult.width = aState.currentBand->availSpace.XMost() - aState.x; - } else { - aResult.width = aState.x - aState.currentBand->availSpace.x; - } - } else { - // It's a block. Don't adjust for the left/right margin here. That happens - // later on once we know the current left/right edge - aResult.width = aState.availSize.width; - } -} - -/** - * Push all of the kids that we have not reflowed, starting at - * aState.lineStart. aPrevKid is the kid previous to aState.lineStart - * and is also our last child. Note that line length is NOT a - * reflection of the number of children we are actually pushing - * (because we don't break the sibling list as we add children to the - * line). - */ -void nsBlockFrame::PushKids(nsBlockReflowState& aState) -{ - nsIFrame* prevFrame = aState.prevLineLastFrame; - NS_PRECONDITION(nsnull != prevFrame, "pushing all kids"); #ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevFrame->GetNextSibling(nextSibling); - NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line"); + nsIFrame* nextFrame; + prevKidFrame->GetNextSibling(nextFrame); + NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list"); #endif + prevKidFrame->SetNextSibling(nsnull); + prevLine->mNextLine = nsnull; + mLastContentOffset = prevLine->mLastContentOffset; + mLastContentIsComplete = prevLine->mLastContentIsComplete; + + // Push children to our next-in-flow if we have, or to our overflow list + nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; + PRInt32 pushCount = 0; + if (nsnull == nextInFlow) { + // Place children on the overflow list + mOverflowList = aLine->mFirstChild; + + // Destroy the lines + nsLineData* line = aLine; + while (nsnull != line) { + pushCount += line->mChildCount; + nsLineData* next = line->mNextLine; + delete line; + line = next; + } + } + else { + aLine->mPrevLine = nsnull; + + // Pass on the children to our next-in-flow + nsLineData* line = aLine; + prevLine = line; + nsIFrame* lastKid = aLine->mFirstChild; + nsIFrame* kid = lastKid; + while (nsnull != line) { + i = line->mChildCount; + pushCount += i; + NS_ASSERTION(kid == line->mFirstChild, "bad line list"); + while (--i >= 0) { + kid->SetGeometricParent(nextInFlow); + nsIFrame* contentParent; + kid->GetContentParent(contentParent); + if (this == contentParent) { + kid->SetContentParent(nextInFlow); + } + lastKid = kid; + kid->GetNextSibling(kid); + } + prevLine = line; + line = line->mNextLine; + } + + // Join the two line lists + nsLineData* nextInFlowLine = nextInFlow->mLines; + if (nsnull != nextInFlowLine) { + lastKid->SetNextSibling(nextInFlowLine->mFirstChild); + nextInFlowLine->mPrevLine = prevLine; + } + nsIFrame* firstKid = aLine->mFirstChild; + prevLine->mNextLine = nextInFlowLine; + nextInFlow->mLines = aLine; + nextInFlow->mFirstChild = firstKid; + nextInFlow->mChildCount += pushCount; + firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset); #ifdef NS_DEBUG - PRInt32 numKids = LengthOf(mFirstChild); - NS_ASSERTION(numKids == mChildCount, "bad child count"); + nextInFlow->VerifyLines(PR_FALSE); +#endif + } + mChildCount -= pushCount; + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return NS_LINE_LAYOUT_NOT_COMPLETE; +} + +nsresult +nsBlockFrame::ReflowMapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != line) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // If we have no children and we have a prev-in-flow then we need to + // pick up where it left off. If we have children, e.g. we're being + // resized, then our content offset will have already been set + // correctly. + nsIFrame* kidPrevInFlow = nsnull; + if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { + nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; + mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary? + if (PR_FALSE == prev->mLastContentIsComplete) { + // Our prev-in-flow's last child is not complete + prev->LastChild(kidPrevInFlow); + } + } + + // Get to the last line where the new content may be added + nsLineData* line = nsnull; + nsLineData* prevLine = nsnull; + if (nsnull != mLines) { + line = mLines; + while (nsnull != line->mNextLine) { + line = line->mNextLine; + } + prevLine = line; + + // If the last line is not complete then kidPrevInFlow should be + // set to the last-line's last child. + if (!prevLine->mLastContentIsComplete) { + kidPrevInFlow = prevLine->GetLastChild(); + } + } + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + // Now reflow the new content until we are out of new content or out + // of vertical space. + PRInt32 kidIndex = NextChildOffset(); + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + lineLayout.mKidPrevInFlow = kidPrevInFlow; + PRInt32 contentChildCount = mContent->ChildCount(); + while (kidIndex < contentChildCount) { + if (nsnull == line) { + if (!MoreToReflow(aState)) { + break; + } + line = new nsLineData(); + if (nsnull == line) { + rv = NS_ERROR_OUT_OF_MEMORY; + goto done; + } + line->mFirstContentOffset = kidIndex; + } + + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mKidPrevInFlow = kidPrevInFlow; + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Add line to the block; do this before placing the line in case + // PushLines is needed. + if (nsnull == prevLine) { + // For the first line, initialize mFirstContentOffset + mFirstContentOffset = line->mFirstContentOffset; + mFirstChild = line->mFirstChild; + mLines = line; + } + else { + prevLine->mNextLine = line; + line->mPrevLine = prevLine; + } + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + kidIndex = lineLayout.mKidIndex; + kidPrevInFlow = lineLayout.mKidPrevInFlow; + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; + if (aState.mBlockIsPseudo) { + PropagateContentOffsets(); + } + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::InitializeState(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockReflowState& aState) +{ + nsresult rv; + rv = aState.Initialize(aPresContext, aSpaceManager, + aMaxSize, aMaxElementSize, this); + + nsStyleSpacing* mySpacing = (nsStyleSpacing*) + mStyleContext->GetData(kStyleSpacingSID); + + // Apply border and padding adjustments for regular frames only + if (!aState.mBlockIsPseudo) { + aState.mY = mySpacing->mBorderPadding.top; + aState.mX = mySpacing->mBorderPadding.left; + aState.mAvailSize.width -= + (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); + aState.mAvailSize.height -= + (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); + aState.mBorderPadding = mySpacing->mBorderPadding; + } + else { + aState.mBorderPadding.SizeTo(0, 0, 0, 0); + } + + // Setup initial list ordinal value + nsIAtom* tag = mContent->GetTag(); + if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || + (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { + nsHTMLValue value; + if (eContentAttr_HasValue == + ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { + if (eHTMLUnit_Integer == value.GetUnit()) { + aState.mNextListOrdinal = value.GetIntValue(); + } + } + } + NS_RELEASE(tag); + + return rv; +} + +#if XXX +NS_METHOD +nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight) +{ + printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth), + NS_TWIPS_TO_POINTS_FLOAT(aHeight)); + return nsHTMLContainerFrame::SizeTo(aWidth, aHeight); +} + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, desiredRect, aStatus); + aDesiredSize.width = desiredRect.width; + aDesiredSize.height = desiredRect.height; + aDesiredSize.ascent = aDesiredSize.height; + aDesiredSize.descent = 0; + } + return rv; +} #endif -#ifdef NOISY - ListTag(stdout); - printf(": push kids (childCount=%d)\n", mChildCount); - DumpFlow(); -#endif - - PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete); - SetLastContentOffset(prevFrame); - - // Set mLastContentIsComplete to the previous lines last content is - // complete now that the previous line's last child is our last - // child. - mLastContentIsComplete = aState.prevLineLastContentIsComplete; - - // Fix up child count - // XXX is there a better way? aState.lineLength doesn't work because - // we might be pushing more than just the pending line. - nsIFrame* kid = mFirstChild; - PRInt32 kids = 0; - while (nsnull != kid) { - kids++; - kid->GetNextSibling(kid); - } - mChildCount = kids; - - // Make sure we have no lingering line data - aState.lineLength = 0; - aState.lineStart = nsnull; - -#ifdef NOISY - ListTag(stdout); - printf(": push kids done (childCount=%d) [%c]\n", mChildCount, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif -} - -/** - * Gets a band of available space starting at the specified y-offset. Assumes - * the local coordinate space is currently set to the upper-left origin of the - * bounding rect - * - * Updates "currentBand" and "x" member data of the block reflow state - */ -void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY) -{ - // Gets a band of available space. - aState.spaceManager->Translate(aState.borderPadding.left, 0); - aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand); - - // Compute the bounding rect of the available space, i.e. space between any - // left and right floaters - aState.currentBand->ComputeAvailSpaceRect(); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - aState.x = aState.currentBand->availSpace.x; -} - -void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear) -{ - // Translate the coordinate space - aState.spaceManager->Translate(aState.borderPadding.left, 0); - -getBand: - nscoord y = aState.y + aState.topMargin; - PRBool isLeftFloater = PR_FALSE; - PRBool isRightFloater = PR_FALSE; - - // Get a band of available space - aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand); - - // Scan the trapezoids looking for left and right floaters - nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids; - for (PRInt32 i = 0; i < aState.currentBand->count; i++) { - // XXX Handle multiply occupied - if (nsBandTrapezoid::smOccupied == trapezoid->state) { - nsStyleDisplay* display; - - trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display); - if (NS_STYLE_FLOAT_LEFT == display->mFloats) { - isLeftFloater = PR_TRUE; - } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) { - isRightFloater = PR_TRUE; - } - } - - trapezoid++; - } - - if (isLeftFloater) { - if ((aClear == NS_STYLE_CLEAR_LEFT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - if (isRightFloater) { - if ((aClear == NS_STYLE_CLEAR_RIGHT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - - aState.spaceManager->Translate(-aState.borderPadding.left, 0); -} - -// Bit's for PlaceAndReflowChild return value -#define PLACE_FIT 0x1 -#define PLACE_FLOWED 0x2 - -PRIntn -nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC) -{ - nsSize kidMaxElementSize; - nsSize* pKidMaxElementSize = - (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; - - // Get line start setup if we are at the start of a new line - if (nsnull == aState.lineStart) { - NS_ASSERTION(0 == aState.lineLength, "bad line length"); - aState.lineStart = aKidFrame; - } - - // Get kid and its style - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - - // Figure out if kid is a block element or not - PRBool isInline = PR_TRUE; - PRIntn display = styleDisplay->mDisplay; - if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) { - // XXX Special hack for properly reflowing bullets that have the - // inside value for list-style-position. - display = NS_STYLE_DISPLAY_INLINE; - } - if ((NS_STYLE_DISPLAY_BLOCK == display) || - (NS_STYLE_DISPLAY_LIST_ITEM == display)) { - // Block elements always end up on the next line (unless they are - // already at the start of the line). - isInline = PR_FALSE; - if (aState.lineLength > 0) { - aState.breakAfterChild = PR_TRUE; - } - } - - // Handle forced break first - if (aState.breakAfterChild) { - NS_ASSERTION(aState.lineStart != aKidFrame, "bad line"); - - // Get the last child in the current line - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - if (!AdvanceToNextLine(aCX, aState)) { - // The previous line didn't fit. - return 0; - } - aState.lineStart = aKidFrame; - - // Get the style for the last child, and see if it wanted to clear - // floaters. This handles the BR tag, which is the only inline - // element for which clear applies - nsIStyleContextPtr lastChildSC; - - lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef()); - nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*) - lastChildSC->GetData(kStyleDisplaySID); - switch (lastChildDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, lastChildDisplay->mBreakType); - break; - } - } - - // Now that we've handled force breaks (and maybe called AdvanceToNextLine() - // which checks), remember whether it's an inline frame - aState.isInline = isInline; - - // If we're at the beginning of a line then compute the top margin that we - // should use - if (aState.lineStart == aKidFrame) { - // Compute the top margin to use for this line - aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC, - aState.isInline); - - // If it's an inline element then get a band of available space - // - // XXX If we have a current band and there's unused space in that band - // then avoid this call to get a band... - if (aState.isInline) { - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - } - } - - // Compute the available space to reflow the child into and then - // reflow it into that space. - nsSize kidAvailSize; - GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - - ReflowStatus status; - - if (aState.isInline) { - nsReflowMetrics kidSize; - - // Inline elements are never passed the space manager - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - NS_ASSERTION(nsnull != aState.lineStart, "bad line start"); - - if (aKidFrame == aState.lineStart) { - // Width always fits when we are at the logical left margin. - // Just check the height. - // - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall - return PLACE_FLOWED; - } - } else { - // Examine state and if the breakBeforeChild is set and we - // aren't already on the new line, do the forcing now. - // XXX Why aren't we doing this check BEFORE we resize reflow the child? - if (aState.breakBeforeChild) { - aState.breakBeforeChild = PR_FALSE; - if (aKidFrame != aState.lineStart) { - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow child now that it has the line to itself - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - } - } - - // When we are not at the logical left margin then we need - // to check the width first. If we are too wide then advance - // to the next line and try reflowing again. - if (kidSize.width > kidAvailSize.width) { - // Too wide. Try next line - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow splittable children - SplittableType isSplittable; - - aKidFrame->IsSplittable(isSplittable); - if (isSplittable != frNotSplittable) { - // Update size info now that we are on the next line. Then - // reflow the child into the new available space. - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - } - - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall on the next line - return PLACE_FLOWED; - } - // It's ok if it's too wide on the next line. - } - } - } - - // Add child to the line - AddInlineChildToLine(aCX, aState, aKidFrame, kidSize, - pKidMaxElementSize, aKidSC); - } else { - nsRect kidRect; - - // Does the block-level element want to clear any floaters that impact - // it? Note that the clear property only applies to block-level elements - // and the BR tag - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - switch (styleDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, styleDisplay->mBreakType); - GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - break; - } - - // Give the block its own local coordinate space.. Note: ReflowChild() - // will adjust for the child's left/right margin after determining the - // current left/right edge - aState.spaceManager->Translate(aState.borderPadding.left, 0); - // Give the block-level element the opportunity to use the space manager - status = ReflowChild(aKidFrame, aCX, aState.spaceManager, - kidAvailSize, kidRect, pKidMaxElementSize); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - // Block elements always fit horizontally (because they are - // always placed at the logical left margin). Check to see if - // the block fits vertically - if (kidRect.YMost() > kidAvailSize.height) { - // Nope - return PLACE_FLOWED; - } - } - - // Add block child - // XXX We need to set lastContentIsComplete here, because AddBlockChild() - // calls AdvaneceToNextLine(). We need to restructure the flow of control, - // and use a state machine... - aState.lastContentIsComplete = PRBool(status == frComplete); - AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC); - } - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - - aState.lastContentIsComplete = PRBool(status == frComplete); - if (aState.isInline && (frNotComplete == status)) { - // Since the inline child didn't complete its reflow we *know* - // that a continuation of it can't possibly fit on the current - // line. Therefore, set a flag in the state that will cause the - // a line break before the next frame is placed. - aState.breakAfterChild = PR_TRUE; - } - - aState.reflowStatus = status; - return PLACE_FLOWED | PLACE_FIT; -} - -/** - * Reflow the existing frames. - * - * @param aCX presentation context to use - * @param aState in out parameter which tracks the state of - * reflow for the block frame. - * @return true if we successfully reflowed all the mapped children and false - * otherwise, e.g. we pushed children to the next in flow - */ PRBool -nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsIFrame* kidFrame; - - for (kidFrame = mFirstChild; nsnull != kidFrame; ) { - - /* we get the kid's style from kidFrame's cached style context */ - nsIStyleContextPtr kidSC; - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - - // Attempt to place and reflow the child - - // XXX if child is not splittable and it fits just place it where - // it is, otherwise advance to the next line and place it there if - // possible - - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - ReflowStatus status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // The child doesn't fit. Push it and any remaining children. - PushKids(aState); - result = PR_FALSE; - goto push_done; - } - - // Is the child complete? - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (frComplete == status) { - // Yes, the child is complete - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); - } else { - // No the child isn't complete - if (nsnull == kidNextInFlow) { - // The child doesn't have a next-in-flow so create a continuing - // frame. This hooks the child into the flow - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); - - // Add the continuing frame to the sibling list - nsIFrame* nextSib; - - kidFrame->GetNextSibling(nextSib); - continuingFrame->SetNextSibling(nextSib); - kidFrame->SetNextSibling(continuingFrame); - mChildCount++; - } - - // Unlike the inline frame code we can't assume that we used - // up all of our space because the child's reflow status is - // frNotComplete. Instead, the child is probably split and - // we need to reflow the continuations as well. - } - - // Get the next child frame - kidFrame->GetNextSibling(kidFrame); - } - - push_done: -#ifdef NS_DEBUG - nsIFrame* lastChild; - PRInt32 lastIndexInParent; - - LastChild(lastChild); - lastChild->GetIndexInParent(lastIndexInParent); - NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset"); - - PRInt32 len = LengthOf(mFirstChild); - NS_POSTCONDITION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -/* XXX: - * In this method, we seem to be getting the style for the next child, - * and checking that child's style for the child's display type. - * The problem is that it's very inefficient to ResolveStyleFor(kid) here - * and then just throw it away, when we end up doing a ResolveStyleFor(kid) - * again when we actually create a frame for the content. - * It would be nice to cache the resolved style, maybe in the reflow state? - */ -PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) +nsBlockFrame::MoreToReflow(nsBlockReflowState& aState) { PRBool rv = PR_FALSE; - if (IsPseudoFrame()) { + if (aState.mBlockIsPseudo) { // Get the next content object that we would like to reflow PRInt32 kidIndex = NextChildOffset(); nsIContentPtr kid = mContent->ChildAt(kidIndex); if (kid.IsNotNull()) { // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); + nsIStyleContextPtr kidSC = + aState.mPresContext->ResolveStyleContextFor(kid, this); nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*) kidSC->GetData(kStyleDisplaySID); switch (kidStyleDisplay->mDisplay) { @@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) return rv; } -/** - * Create new frames for content we haven't yet mapped - * - * @param aCX presentation context to use - * @return frComplete if all content has been mapped and frNotComplete - * if we should be continued - */ -nsIFrame::ReflowStatus -nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) +nsBlockReflowState* +nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext, + nsIFrame* aFrame) { -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif - nsIFrame* kidPrevInFlow = nsnull; - ReflowStatus result = frNotComplete; - - // If we have no children and we have a prev-in-flow then we need to pick - // up where it left off. If we have children, e.g. we're being resized, then - // our content offset should already be set correctly... - if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { - nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; - NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow"); - mFirstContentOffset = prev->NextChildOffset(); - if (PR_FALSE == prev->mLastContentIsComplete) { - // Our prev-in-flow's last child is not complete - prev->LastChild(kidPrevInFlow); - } - } - - // Place our children, one at a time until we are out of children - PRInt32 kidIndex = NextChildOffset(); - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (kid.IsNull()) { - result = frComplete; - break; - } - - // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - nsStylePosition* kidPosition = (nsStylePosition*) - kidSC->GetData(kStylePositionSID); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); - - // Check whether it wants to floated or absolutely positioned - if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) { - AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (nsnull == kidPrevInFlow) { - // Create initial frame for the child - nsIContentDelegate* kidDel; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // If we're being used as a pseudo frame, i.e. we map the same - // content as our parent then we want to indicate we're complete; - // otherwise we'll be continued and go on mapping children... - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - result = frComplete; - goto done; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aCX); - kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow"); - nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); + nsBlockReflowState* state = nsnull; + if (nsnull != aFrame) { + nsIFrame* parent; + aFrame->GetGeometricParent(parent); + while (nsnull != parent) { + nsBlockFrame* block; + nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block); + if (NS_OK == rv) { + nsIPresShell* shell = aPresContext->GetShell(); + state = (nsBlockReflowState*) shell->GetCachedData(block); + NS_RELEASE(shell); break; } - kidFrame->SetStyleContext(aCX,kidSC); - } else { - // Since kid has a prev-in-flow, use that to create the next - // frame. - kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame); + parent->GetGeometricParent(parent); } - - // Link child frame into the list of children. If the frame ends - // up not fitting and getting pushed, the PushKids code will fixup - // the child count for us. - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - mChildCount++; - - // Reflow child frame as many times as necessary until it is - // complete. - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // We ran out of room. - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - goto push_done; - } - - // Did the child complete? - prevKidFrame = kidFrame; - if (frNotComplete == status) { - // Child didn't complete so create a continuing frame - kidPrevInFlow = kidFrame; - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to the sibling list - nsIFrame* kidNextSibling; - - kidFrame->GetNextSibling(kidNextSibling); - continuingFrame->SetNextSibling(kidNextSibling); - kidFrame->SetNextSibling(continuingFrame); - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "huh?"); -#endif - } while (frNotComplete == status); - - // The child that we just reflowed is complete -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); -#endif - kidIndex++; - kidPrevInFlow = nsnull; } - - done: - // To get here we either completely reflowed all our appended - // children OR we are a pseudo-frame and we ran into a block - // element. In either case our last content MUST be complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - - push_done: -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - return result; + return state; } -/** - * Pullup frames from our next in flow and try to place them. Before - * this is called our previously mapped children, if any have been - * reflowed which means that the block reflow state's x and y - * coordinates and other data are ready to go. - * - * Return true if we pulled everything up. - */ -PRBool -nsBlockFrame::PullUpChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - while (nsnull != nextInFlow) { - // Get first available frame from the next-in-flow - nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame); - if (nsnull == kidFrame) { - // We've pulled up all the children from that next-in-flow, so - // move to the next next-in-flow. - nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; - continue; - } - - // Get style information for the pulled up kid - nsIContentPtr kid; - - kidFrame->GetContent(kid.AssignRef()); - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // Push the kids that didn't fit back down to the next-in-flow - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - result = PR_FALSE; - goto push_done; - } - - if (frNotComplete == status) { - // Child is not complete - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (nsnull == kidNextInFlow) { - // Create a continuing frame for the incomplete child - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to our sibling list. - nsIFrame* nextSibling; - - kidFrame->GetNextSibling(nextSibling); - continuingFrame->SetNextSibling(nextSibling); - kidFrame->SetNextSibling(continuingFrame); - prevKidFrame = kidFrame; - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } else { - // The child has a next-in-flow, but it's not one of ours. - // It *must* be in one of our next-in-flows. Collect it - // then. - NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow"); - break; - } - } - } while (frNotComplete == status); - - prevKidFrame = kidFrame; - } - - if (nsnull != prevKidFrame) { - // The only way we can get here is by pulling up every last child - // in our next-in-flows (and reflowing any continunations they - // have). Therefore we KNOW that our last child is complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - } - - push_done:; - - if (result == PR_FALSE) { - // If our next-in-flow is empty OR our next next-in-flow is empty - // then adjust the offsets of all of the empty next-in-flows. - nextInFlow = (nsBlockFrame*) mNextInFlow; - if ((0 == nextInFlow->mChildCount) || - ((nsnull != nextInFlow->mNextInFlow) && - (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) { - // We didn't pullup everything and we need to fixup one of our - // next-in-flows content offsets. - AdjustOffsetOfEmptyNextInFlows(); - } - } - - -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -void nsBlockFrame::SetupState(nsIPresContext* aCX, - nsBlockReflowState& aState, - const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsISpaceManager* aSpaceManager) -{ - // Setup reflow state - aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager); - - nsStyleSpacing* mySpacing = (nsStyleSpacing*) - mStyleContext->GetData(kStyleSpacingSID); - - // Apply border and padding adjustments for regular frames only - if (PR_FALSE == IsPseudoFrame()) { - aState.borderPadding = mySpacing->mBorderPadding; - aState.y = mySpacing->mBorderPadding.top; - aState.availSize.width -= - (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); - aState.availSize.height -= - (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); - } else { - aState.borderPadding.SizeTo(0, 0, 0, 0); - } - - // Setup initial list ordinal value - nsIAtom* tag = mContent->GetTag(); - if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || - (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { - nsHTMLValue value; - if (eContentAttr_HasValue == - ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { - if (eHTMLUnit_Integer == value.GetUnit()) { - aState.nextListOrdinal = value.GetIntValue(); - } - } - } - NS_RELEASE(tag); - - mCurrentState = &aState; -} - -#include "nsUnitConversion.h"/* XXX */ -NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsSize* aMaxElementSize, - ReflowStatus& aStatus) -{ - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager); - return DoResizeReflow(aCX, state, aDesiredRect, aStatus); -} - -nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsRect& aDesiredRect, - ReflowStatus& aStatus) +nsresult +nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + ReflowStatus& aStatus) { #ifdef NS_DEBUG + VerifyLines(PR_TRUE); PreReflowCheck(); #endif -#ifdef NOISY +#ifdef NOISY_REFLOW ListTag(stdout); - printf(": resize reflow %g,%g\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height)); - DumpFlow(); + printf(": Before:\n"); + List(stdout, 1); #endif - // Zap old line data - if (nsnull != mLines) { - delete mLines; - mLines = nsnull; - } - mNumLines = 0; + nsresult rv = NS_OK; - // Check for an overflow list - MoveOverflowToChildList(); - - // Before we start reflowing, cache a pointer to our state structure - // so that inline frames can find it. - nsIPresShell* shell = aCX->GetShell(); + nsIPresShell* shell = aState.mPresContext->GetShell(); shell->PutCachedData(this, &aState); - // First reflow any existing frames - PRBool reflowMappedOK = PR_TRUE; - aStatus = frComplete; - if (nsnull != mFirstChild) { - reflowMappedOK = ReflowMappedChildren(aCX, aState); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } + // Check for an overflow list + DrainOverflowList(); + + if (nsnull != mLines) { + rv = ReflowMapped(aState); } - if (reflowMappedOK) { - // Any space left? - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if ((nsnull != mFirstChild) && (aState.y >= yb)) { - // No space left. Don't try to pull-up children or reflow - // unmapped. We need to return the correct completion status, - // so see if there is more to reflow. - if (MoreToReflow(aCX)) { - aStatus = frNotComplete; - } - } else if (MoreToReflow(aCX)) { - // Try and pull-up some children from a next-in-flow - if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) { - // If we still have unmapped children then create some new frames - if (MoreToReflow(aCX)) { - aStatus = ReflowAppendedChildren(aCX, aState); - } - } else { - // We were unable to pull-up all the existing frames from the - // next in flow - aStatus = frNotComplete; - } + if (NS_OK == rv) { + if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) { + // We are out of space } - } - - // Deal with last line - make sure it gets vertically and - // horizontally aligned. This also updates the state's y coordinate - // which is good because that's how we size ourselves. - if (0 != aState.lineLength) { - if (!AdvanceToNextLine(aCX, aState)) { - // The last line of output doesn't fit. Push all of the kids to - // the next in flow and change our reflow status to not complete - // so that we are continued. -#ifdef NOISY - ListTag(stdout); - printf(": pushing kids since last line doesn't fit\n"); -#endif - - PushKids(aState); - aStatus = frNotComplete; + if (MoreToReflow(aState)) { + rv = ReflowUnmapped(aState); } } - if (frComplete == aStatus) { - // Don't forget to add in the bottom margin from our last child. - // Only add it in if there is room for it. - nscoord margin = aState.prevMaxPosBottomMargin - - aState.prevMaxNegBottomMargin; - nscoord y = aState.y + margin; - if (y <= aState.borderPadding.top + aState.availSize.height) { - aState.y = y; - } - } - - // Now that reflow has finished, remove the cached pointer - shell->RemoveCachedData(this); - NS_RELEASE(shell); - - // Translate state.lineLengths into an integer array - mNumLines = aState.lineLengths.Count(); - if (mNumLines > 0) { - mLines = new PRInt32[mNumLines]; - for (PRInt32 i = 0; i < mNumLines; i++) { - PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i); - mLines[i] = ll; - } - } - - if (!aState.unconstrainedWidth && aState.justifying) { - // Perform justification now that we know how many lines we have. - JustifyLines(aCX, aState); - } - // Return our desired rect and our status aDesiredRect.x = 0; aDesiredRect.y = 0; - aDesiredRect.width = aState.kidXMost + aState.borderPadding.right; - if (!aState.unconstrainedWidth) { + aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right; + if (!aState.mUnconstrainedWidth) { // Make sure we're at least as wide as the max size we were given - nscoord maxWidth = aState.availSize.width + aState.borderPadding.left + - aState.borderPadding.right; - + nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left + + aState.mBorderPadding.right; if (aDesiredRect.width < maxWidth) { aDesiredRect.width = maxWidth; } } - aState.y += aState.borderPadding.bottom; - aDesiredRect.height = aState.y; - -#ifdef NS_DEBUG - PostReflowCheck(aStatus); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height), - ((status == frNotComplete) ? "NOT " : ""), - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F') - ); - DumpFlow(); -#endif - mCurrentState = nsnull; - return NS_OK; -} - -void nsBlockFrame::JustifyLines(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - // XXX we don't justify the last line; what if we are continued, - // should we do it then? - nsIFrame* kid = mFirstChild; - for (PRInt32 i = 0; i < mNumLines; i++) { - nsIFrame* lineStart = kid; - PRInt32 lineLength = mLines[i]; - if (i < mNumLines - 1) { - if (1 == lineLength) { - // For lines with one element on them we can take a shortcut and - // let them do the justification. Note that we still call - // JustifyReflow even if the available space is zero in case the - // child is a hunk of text that ends in whitespace. The whitespace - // will be distributed elsewhere causing a proper flush right look - // for the last word. - nsRect r; - kid->GetRect(r); - nscoord maxWidth = aState.availSize.width; - nscoord availableSpace = maxWidth - r.width; - nscoord maxAvailSpace = nscoord(maxWidth * 0.1f); - if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) { - kid->JustifyReflow(aCX, availableSpace); - kid->SizeTo(r.width + availableSpace, r.height); - } - kid->GetNextSibling(kid); - } else { - // XXX Get justification of multiple elements working - while (--lineLength >= 0) { - kid->GetNextSibling(kid); - } + aState.mY += aState.mBorderPadding.bottom; + nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) { + // It's possible that we don't have room for the last bottom + // margin (the last bottom margin is the margin following a block + // element that we contain; it isn't applied immediately because + // of the margin collapsing logic). This can happen when we are + // reflowed in a limited amount of space because we don't know in + // advance what the last bottom margin will be. + nscoord maxY = aMaxSize.height; + if (aState.mY + lastBottomMargin > maxY) { + lastBottomMargin = maxY - aState.mY; + if (lastBottomMargin < 0) { + lastBottomMargin = 0; } } - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]); + aState.mY += lastBottomMargin; } -} + aDesiredRect.height = aState.mY; -NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const -{ - aIsSplittable = frSplittableNonRectangular; - return NS_OK; -} - -NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, - nsIFrame* aParent, - nsIFrame*& aContinuingFrame) -{ - nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); - PrepareContinuingFrame(aCX, aParent, cf); - aContinuingFrame = cf; - return NS_OK; -} - -NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsReflowCommand& aReflowCommand, - ReflowStatus& aStatus) -{ + // Set return status aStatus = frComplete; - - if (aReflowCommand.GetTarget() == this) { - // XXX for now, just do a complete reflow mapped (it'll kinda - // work, but it's slow) - - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager); - PRBool reflowMappedOK = ReflowMappedChildren(aCX, state); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } - } else { - // XXX not yet implemented - NS_ABORT(); - // XXX work to compute initial state goes *HERE* - aDesiredRect.x = 0; - aDesiredRect.y = 0; - aDesiredRect.width = 0; - aDesiredRect.height = 0; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; } +#ifdef NS_DEBUG + // Verify that the line layout code pulled everything up when it + // indicates a complete reflow. + if (frComplete == aStatus) { + nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow; + while (nsnull != nextBlock) { + NS_ASSERTION((nsnull == nextBlock->mLines) && + (nextBlock->mChildCount == 0) && + (nsnull == nextBlock->mFirstChild), + "bad completion status"); + nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow; + } - mCurrentState = nsnull; - return NS_OK; +#if XXX + // We better not be in the same parent frame as our prev-in-flow. + // If we are it means that we were continued instead of pulling up + // children. + if (nsnull != mPrevInFlow) { + nsIFrame* ourParent = mGeometricParent; + nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent; + NS_ASSERTION(ourParent != prevParent, "bad continuation"); + } +#endif + } +#endif + + // Now that reflow has finished, remove the cached pointer + shell->RemoveCachedData(this); + NS_RELEASE(shell); + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); + PostReflowCheck(aStatus); +#endif +#ifdef NOISY_REFLOW + ListTag(stdout); + printf(": After:\n"); + List(stdout, 1); +#endif + return rv; } -PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ContentAppended(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentInserted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentReplaced(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentDeleted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext, + nsReflowMetrics& aMetrics) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aSpaceManager, aMaxSize, + aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus); + } + return rv; +} + +NS_METHOD +nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsReflowCommand& aReflowCommand, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +PRBool +nsBlockFrame::AddFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + + if (nsnull != state) { + // Get the frame associated with the space manager, and get its + // nsIAnchoredItems interface + nsIFrame* frame = state->mSpaceManager->GetFrame(); + nsIAnchoredItems* anchoredItems = nsnull; + + frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); + NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); + if (nsnull != anchoredItems) { + anchoredItems->AddAnchoredItem(aFloater, + nsIAnchoredItems::anHTMLFloater, + this); + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + return PR_TRUE; + } + } + + return PR_FALSE; +} + +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + if (nsnull != state) { + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + } +} + +PRBool +nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) { do { nsIFrame* parent; - aFrame->GetGeometricParent(parent); - // See if there are any non-zero sized child frames that precede aFrame - // in the child list + // See if there are any non-zero sized child frames that precede + // aFrame in the child list nsIFrame* child; - parent->FirstChild(child); while ((nsnull != child) && (aFrame != child)) { nsSize size; @@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // We found a non-zero sized child frame that precedes aFrame return PR_FALSE; } - child->GetNextSibling(child); } @@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // Walk up one level and check that its parent is left-most as well aFrame = parent; } while (aFrame != this); - return PR_TRUE; } -PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) -{ - // Get the frame associated with the space manager, and get its nsIAnchoredItems - // interface - nsIFrame* frame = mCurrentState->spaceManager->GetFrame(); - nsIAnchoredItems* anchoredItems = nsnull; - - frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); - NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); - - if (nsnull != anchoredItems) { - anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this); - PlaceFloater(aCX, aFloater, aPlaceholder); - return PR_TRUE; - } - - return PR_FALSE; -} - -// XXX The size of the floater needs to be taken into consideration if we're -// computing a maximum element size -void nsBlockFrame::PlaceFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder, + nsBlockReflowState& aState) { // If the floater is the left-most non-zero size child frame then insert // it before the current line; otherwise add it to the below-current-line // todo list and we'll handle it when we flush out the line if (IsLeftMostChild(aPlaceholder)) { - // Get the type of floater - nsIStyleContextPtr styleContext; + nsISpaceManager* sm = aState.mSpaceManager; - aFloater->GetStyleContext(aCX, styleContext.AssignRef()); + // Get the type of floater + nsIStyleContextPtr styleContext; + aFloater->GetStyleContext(aPresContext, styleContext.AssignRef()); nsStyleDisplay* floaterDisplay = (nsStyleDisplay*) styleContext->GetData(kStyleDisplaySID); - if (!mCurrentState->isInline) { - // Get the current band for this line - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); - } - - // Commit some space in the space manager - nsRect region; - + // Commit some space in the space manager and adjust our current + // band of available space. + nsRect region; aFloater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; - + region.y = aState.mY; if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; + region.x = aState.mX; } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, + "bad float type"); + region.x = aState.mCurrentBand.availSpace.XMost() - region.width; } // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0); - mCurrentState->spaceManager->AddRectRegion(region, aFloater); + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, aFloater); // Set the origin of the floater in world coordinates nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); + sm->GetTranslation(worldX, worldY); aFloater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); + sm->Translate(-aState.mBorderPadding.left, 0); - // Update the band of available space to reflect space taken up by the floater - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); + // Update the band of available space to reflect space taken up by + // the floater + GetAvailableSpace(aState, aState.mY); + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); + } } else { // Add the floater to our to-do list - mCurrentState->floaterToDo.AppendElement(aFloater); + aState.mPendingFloaters.AppendElement(aFloater); } } -NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell, - nsIPresContext* aPresContext, - nsIContent* aContainer) +void +nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState, + nscoord aY) { - // Get the last-in-flow - nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow(); - PRInt32 kidIndex = flow->NextChildOffset(); - PRInt32 startIndex = kidIndex; + NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters"); -#if 0 - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - flow->LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (nsnull == kid) { - break; - } + nsISpaceManager* sm = aState.mSpaceManager; + nsBlockBandData* bd = &aState.mCurrentBand; - // Resolve style for the kid - nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); + // XXX Factor this code with PlaceFloater()... + PRInt32 numFloaters = aState.mPendingFloaters.Count(); + for (PRInt32 i = 0; i < numFloaters; i++) { + nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i]; + nsRect region; - // Is it a floater? - if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); + // Get the band of available space + // XXX This is inefficient to do this inside the loop... + GetAvailableSpace(aState, aY); + + // Get the type of floater + nsIStyleContextPtr styleContext; + floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef()); + nsStyleDisplay* sd = (nsStyleDisplay*) + styleContext->GetData(kStyleDisplaySID); + + floater->GetRect(region); + region.y = bd->availSpace.y; + if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { + region.x = bd->availSpace.x; } else { - // Create initial frame for the child - nsIContentDelegate* kidDel; - nsresult fr; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // Update last content offset now that we are done drawing - // children from our parent. - SetLastContentOffset(prevKidFrame); - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - return NS_OK; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aPresContext); - kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); - break; - } + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); + region.x = bd->availSpace.XMost() - region.width; } - kidFrame->SetStyleContext(aCX,kidSC); - // Link child frame into the list of children - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; + // XXX Don't forget the floater's margins... + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, floater); - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - kidIndex++; - mChildCount++; + // Set the origin of the floater in world coordinates + nscoord worldX, worldY; + sm->GetTranslation(worldX, worldY); + floater->MoveTo(region.x + worldX, region.y + worldY); + sm->Translate(-aState.mBorderPadding.left, 0); } - SetLastContentOffset(prevKidFrame); -#endif - // If this is a pseudo-frame then our parent will generate the - // reflow command. Otherwise, if the container is us then we should - // generate the reflow command because we were directly called. - if (!IsPseudoFrame() && (aContainer == mContent)) { - nsReflowCommand* rc = - new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended, - startIndex); - aShell->AppendReflowCommand(rc); + aState.mPendingFloaters.Clear(); + + // Pass on updated available space to the current line + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); } - return NS_OK; } -PRIntn nsBlockFrame::GetSkipSides() const +//---------------------------------------------------------------------- + +nsLineData* +nsBlockFrame::GetFirstLine() +{ + return mLines; +} + +PRIntn +nsBlockFrame::GetSkipSides() const { PRIntn skip = 0; if (nsnull != mPrevInFlow) { @@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const } return skip; } - -nsHTMLFrameType nsBlockFrame::GetFrameType() const -{ - return eHTMLFrame_Block; -} - -NS_METHOD nsBlockFrame::ListTag(FILE* out) const -{ - if ((nsnull != mGeometricParent) && IsPseudoFrame()) { - fprintf(out, "*block<"); - nsIAtom* atom = mContent->GetTag(); - if (nsnull != atom) { - nsAutoString tmp; - atom->ToString(tmp); - fputs(tmp, out); - } - fprintf(out, ">(%d)@%p", mIndexInParent, this); - } else { - nsHTMLContainerFrame::ListTag(out); - } - return NS_OK; -} - -#ifdef NS_DEBUG -void nsBlockFrame::DumpFlow() const -{ -#ifdef NOISY_FLOW - nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow; - while (flow != 0) { - printf(" %p: [%d,%d,%c]\n", - flow, flow->mFirstContentOffset, flow->mLastContentOffset, - (flow->mLastContentIsComplete ? 'T' : 'F')); - flow = (nsBlockFrame*) flow->mNextInFlow; - } -#endif -} -#endif diff --git a/layout/generic/nsHTMLContainerFrame.h b/layout/generic/nsHTMLContainerFrame.h index 9364e3f870b9..0d2d6879e04f 100644 --- a/layout/generic/nsHTMLContainerFrame.h +++ b/layout/generic/nsHTMLContainerFrame.h @@ -42,14 +42,6 @@ public: nsIFrame** aFrame, PRInt32& aCursor); -#if 0 - virtual ReflowStatus IncrementalReflow(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsSize& aMaxSize, - nsISpaceManager* aSpaceManager, - nsReflowCommand& aReflowCommand); -#endif - protected: virtual ~nsHTMLContainerFrame(); @@ -59,32 +51,6 @@ protected: const nsString& aBase, const nsString& aURLSpec, const nsString& aTargetSpec); - -#if 0 - virtual ReflowStatus ReflowAppended(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsSize& aMaxSize, - nsISpaceManager* aSpaceManager, - nsReflowCommand& aReflowCommand); - - virtual ReflowStatus ReflowInserted(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsSize& aMaxSize, - nsISpaceManager* aSpaceManager, - nsReflowCommand& aReflowCommand); - - virtual ReflowStatus ReflowDeleted(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsSize& aMaxSize, - nsISpaceManager* aSpaceManager, - nsReflowCommand& aReflowCommand); - - virtual ReflowStatus ReflowChanged(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsSize& aMaxSize, - nsISpaceManager* aSpaceManager, - nsReflowCommand& aReflowCommand); -#endif }; #endif /* nsHTMLContainerFrame_h___ */ diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp index 4864674f0487..9cd92de6cf59 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -592,42 +592,6 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, // FALLTHROUGH case NS_STYLE_DISPLAY_INLINE: - // XXX temporary hack to make plain BR's in inlines "work" - // get style for break-before-after; get break-type (line, page, etc.) - { - nsIAtom* tag = kid->GetTag(); - if (nsHTMLAtoms::br == tag) { - // Set break-after flag so we stop mapping children (we - // will end up being continued if there are more children) - breakAfter = PR_TRUE; - - // Get cached state for containing block frame - // XXX how about QueryInterface(kIHTMLBlockFrameIID)? DOH! - nsBlockReflowState* state = nsnull; - nsIFrame* parent = mGeometricParent; - while (nsnull != parent) { - nsIHTMLFrameType* ft; - nsresult status = - parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft); - if (NS_OK == status) { - nsHTMLFrameType type = ft->GetFrameType(); - if (eHTMLFrame_Block == type) { - break; - } - } - parent->GetGeometricParent(parent); - } - if (nsnull != parent) { - nsIPresShell* shell = aPresContext->GetShell(); - state = (nsBlockReflowState*) shell->GetCachedData(parent); - // XXX Of course this won't work if the inline span is nested - // in another inline span! - state->breakAfterChild = PR_TRUE; - NS_RELEASE(shell); - } - } - NS_IF_RELEASE(tag); - } kidDel = kid->GetDelegate(aPresContext); kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this); NS_RELEASE(kidDel); diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index e2371682c2e1..d3ec7ec27864 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -841,10 +841,10 @@ nsLineLayout::CreateFrameFor(nsIContent* aKid) nsIFrame* kidFrame; if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) { AbsoluteFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock); - kidFrame->SetStyleContext(kidSC); + kidFrame->SetStyleContext(mPresContext, kidSC); } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { PlaceholderFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock); - kidFrame->SetStyleContext(kidSC); + kidFrame->SetStyleContext(mPresContext, kidSC); } else if (nsnull == mKidPrevInFlow) { // Create initial frame for the child @@ -882,7 +882,7 @@ nsLineLayout::CreateFrameFor(nsIContent* aKid) nsFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock); break; } - kidFrame->SetStyleContext(kidSC); + kidFrame->SetStyleContext(mPresContext, kidSC); } else { // Since kid has a prev-in-flow, use that to create the next // frame. diff --git a/layout/html/base/src/makefile.win b/layout/html/base/src/makefile.win index 789faf639de4..fe83b5e0d169 100644 --- a/layout/html/base/src/makefile.win +++ b/layout/html/base/src/makefile.win @@ -42,6 +42,7 @@ CPPSRCS= \ nsImageMap.cpp \ nsInlineFrame.cpp \ nsLeafFrame.cpp \ + nsLineLayout.cpp \ nsListItemFrame.cpp \ nsPageFrame.cpp \ nsPlaceholderFrame.cpp \ @@ -73,6 +74,7 @@ CPP_OBJS= \ .\$(OBJDIR)\nsImageMap.obj \ .\$(OBJDIR)\nsInlineFrame.obj \ .\$(OBJDIR)\nsLeafFrame.obj \ + .\$(OBJDIR)\nsLineLayout.obj \ .\$(OBJDIR)\nsListItemFrame.obj \ .\$(OBJDIR)\nsPageFrame.obj \ .\$(OBJDIR)\nsPlaceholderFrame.obj \ diff --git a/layout/html/base/src/nsBRPart.cpp b/layout/html/base/src/nsBRPart.cpp index df04ebd6036a..b2cf9a7e5a9a 100644 --- a/layout/html/base/src/nsBRPart.cpp +++ b/layout/html/base/src/nsBRPart.cpp @@ -28,8 +28,10 @@ #include "nsIHTMLAttributes.h" #include "nsIStyleContext.h" #include "nsIFontMetrics.h" +#include "nsIRenderingContext.h" static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID); +static NS_DEFINE_IID(kStyleColorSID, NS_STYLECOLOR_SID); class BRFrame : public nsFrame { @@ -37,6 +39,10 @@ public: BRFrame(nsIContent* aContent, PRInt32 aIndexInParent, nsIFrame* aParentFrame); + + NS_IMETHOD Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); NS_IMETHOD ResizeReflow(nsIPresContext* aPresContext, nsReflowMetrics& aDesiredSize, const nsSize& aMaxSize, @@ -60,13 +66,34 @@ BRFrame::~BRFrame() { } +NS_METHOD BRFrame::Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + if (nsIFrame::GetShowFrameBorders()) { + nsStyleColor* color = (nsStyleColor*) + mStyleContext->GetData(kStyleColorSID); + float p2t = aPresContext.GetPixelsToTwips(); + aRenderingContext.SetColor(color->mColor); + aRenderingContext.FillRect(0, 0, nscoord(5 * p2t), mRect.height); + } + return NS_OK; +} + NS_METHOD BRFrame::GetReflowMetrics(nsIPresContext* aPresContext, nsReflowMetrics& aMetrics) { // We have no width, but we're the height of the default font nsStyleFont* font = (nsStyleFont*)mStyleContext->GetData(kStyleFontSID); nsIFontMetrics* fm = aPresContext->GetMetricsFor(font->mFont); + aMetrics.width = 0; + if (nsIFrame::GetShowFrameBorders()) { + // Reserve a tiny bit of space so that our frame won't be zero + // sized so we get a chance to paint. + aMetrics.width = 1; + } + aMetrics.height = fm->GetHeight(); aMetrics.ascent = fm->GetMaxAscent(); aMetrics.descent = fm->GetMaxDescent(); @@ -81,27 +108,15 @@ NS_METHOD BRFrame::ResizeReflow(nsIPresContext* aPresContext, ReflowStatus& aStatus) { // Get cached state for containing block frame - nsBlockReflowState* state = nsnull; - nsIFrame* parent = mGeometricParent; - while (nsnull != parent) { - nsIHTMLFrameType* ft; - nsresult status = parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft); - if (NS_OK == status) { - nsHTMLFrameType type = ft->GetFrameType(); - if (eHTMLFrame_Block == type) { - break; - } - } - parent->GetGeometricParent(parent); - } - if (nsnull != parent) { - nsIPresShell* shell = aPresContext->GetShell(); - state = (nsBlockReflowState*) shell->GetCachedData(parent); - NS_RELEASE(shell); - } + nsLineLayout* lineLayoutState = nsnull; + nsBlockReflowState* state = + nsBlockFrame::FindBlockReflowState(aPresContext, this); if (nsnull != state) { - // XXX
- state->breakAfterChild = PR_TRUE; + lineLayoutState = state->mCurrentLine; + if (nsnull != lineLayoutState) { + lineLayoutState->mReflowResult = + NS_LINE_LAYOUT_REFLOW_RESULT_BREAK_AFTER; + } } GetReflowMetrics(aPresContext, aDesiredSize); diff --git a/layout/html/base/src/nsBlockFrame.cpp b/layout/html/base/src/nsBlockFrame.cpp index 59d7407ec5b1..0228ca80d413 100644 --- a/layout/html/base/src/nsBlockFrame.cpp +++ b/layout/html/base/src/nsBlockFrame.cpp @@ -16,66 +16,37 @@ * Reserved. */ #include "nsBlockFrame.h" -#include "nsSize.h" -#include "nsIAnchoredItems.h" -#include "nsIContent.h" -#include "nsIContentDelegate.h" -#include "nsISpaceManager.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" -#include "nsIPresContext.h" -#include "nsMargin.h" -#include "nsHTMLIIDs.h" -#include "nsCSSLayout.h" -#include "nsCRT.h" -#include "nsIPresShell.h" -#include "nsReflowCommand.h" -#include "nsPlaceholderFrame.h" -#include "nsHTMLAtoms.h" -#include "nsHTMLValue.h" #include "nsIHTMLContent.h" -#include "nsAbsoluteFrame.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIAnchoredItems.h" +#include "nsPlaceholderFrame.h" #include "nsIPtr.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLIIDs.h" +#include "nsHTMLValue.h" -#ifdef NS_DEBUG -#undef NOISY -#undef NOISY_FLOW -#else -#undef NOISY -#undef NOISY_FLOW -#endif +// XXX what do we do with catastrophic errors (rv < 0)? What is the +// state of the reflow world after such an error? -static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); -static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); -static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +#undef NOISY_REFLOW static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID); -static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID); -static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID); -static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID); static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID); -NS_DEF_PTR(nsIStyleContext); +static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); +static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); + NS_DEF_PTR(nsIContent); +NS_DEF_PTR(nsIStyleContext); -struct BlockBandData : public nsBandData { - nsBandTrapezoid data[12]; +//---------------------------------------------------------------------- - // Bounding rect of available space between any left and right floaters - nsRect availSpace; - // Constructor - BlockBandData() {size = 12; trapezoids = data;} - - // Computes the bounding rect of the available space, i.e. space between - // any left and right floaters - // - // Uses the current trapezoid data, see nsISpaceManager::GetBandData(). - // Updates member data "availSpace" - void ComputeAvailSpaceRect(); -}; - -void BlockBandData::ComputeAvailSpaceRect() +void nsBlockBandData::ComputeAvailSpaceRect() { nsBandTrapezoid* trapezoid = data; @@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect() } } -// XXX Bugs -// 1. right to left reflow can generate negative x coordinates. - -// XXX Speedup idea (all containers) - -// If I reflow a child and it gives back not-complete status then -// there is no sense in trying to pullup children. For blocks, it's a -// little more complicated unless the child is a block - if the child -// is a block, then we must be out of room hence we should stop. If -// the child is not a block then our line should be flushed (see #2 -// below) if our line is already empty then we must be out of room. - -// For inline frames and column frames, if we reflow a child and get -// back not-complete status then we should bail immediately because we -// are out of room. - -// XXX Speedup ideas: -// 1. change pullup code to use line information from next in flow -// 2. we can advance to next line immediately after reflowing something -// and noticing that it's not complete. -// 3. pass down last child information in aState so that pullup, etc., -// don't need to recompute it - -// XXX TODO: -// 0. Move justification into line flushing code - -// 1. To get ebina margins I need "auto" information from the style -// system margin's. A bottom/top margin of auto will then be computed like -// ebina computes it [however the heck that is]. - -// 2. kicking out floaters and talking with floater container to adjust -// left and right margins +//---------------------------------------------------------------------- nsBlockReflowState::nsBlockReflowState() { } -void nsBlockReflowState::Init(const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsIStyleContext* aBlockSC, - nsISpaceManager* aSpaceManager) +nsBlockReflowState::~nsBlockReflowState() { - firstLine = PR_TRUE; - allowLeadingWhitespace = PR_FALSE; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - firstChildIsInsideBullet = PR_FALSE; - nextListOrdinal = -1; - column = 0; +} - spaceManager = aSpaceManager; - currentBand = new BlockBandData; +nsresult +nsBlockReflowState::Initialize(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockFrame* aBlock) +{ + nsresult rv = NS_OK; - styleContext = aBlockSC; - styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID); - styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID); - styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID); + mPresContext = aPresContext; + mBlock = aBlock; + mSpaceManager = aSpaceManager; + mBlockIsPseudo = aBlock->IsPseudoFrame(); + mCurrentLine = nsnull; + mPrevKidFrame = nsnull; - justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) && - (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace); - - availSize.width = aMaxSize.width; - availSize.height = aMaxSize.height; - maxElementSize = aMaxElementSize; + mX = 0; + mY = 0; + mAvailSize = aMaxSize; + mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE); + mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE); + mMaxElementSizePointer = aMaxElementSize; if (nsnull != aMaxElementSize) { aMaxElementSize->width = 0; aMaxElementSize->height = 0; } + mKidXMost = 0; - kidXMost = 0; - x = 0; - y = 0; + mPrevMaxPosBottomMargin = 0; + mPrevMaxNegBottomMargin = 0; - isInline = PR_FALSE; - currentLineNumber = 0; - lineStart = nsnull; - lineLength = 0; - ascents = ascentBuf; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; - lineMaxElementSize.width = 0; - lineMaxElementSize.height = 0; - lastContentIsComplete = PR_TRUE; + mNextListOrdinal = -1; + mFirstChildIsInsideBullet = PR_FALSE; - maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]); - needRelativePos = PR_FALSE; - - prevLineLastFrame = nsnull; - prevLineHeight = 0; - topMargin = 0; - prevMaxPosBottomMargin = 0; - prevMaxNegBottomMargin = 0; - prevLineLastContentIsComplete = PR_TRUE; - - unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE); - unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE); - - reflowStatus = nsIFrame::frNotComplete; + return rv; } -nsBlockReflowState::~nsBlockReflowState() -{ - if (ascents != ascentBuf) { - delete ascents; - } - delete currentBand; -} - -void nsBlockReflowState::AddAscent(nscoord aAscent) -{ - NS_PRECONDITION(lineLength <= maxAscents, "bad line length"); - if (lineLength == maxAscents) { - maxAscents *= 2; - nscoord* newAscents = new nscoord[maxAscents]; - if (nsnull != newAscents) { - nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength); - if (ascents != ascentBuf) { - delete ascents; - } - ascents = newAscents; - } else { - // Yikes! Out of memory! - return; - } - } - ascents[lineLength] = aAscent; -} - -void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame, - nscoord aPrevLineHeight) -{ - firstLine = PR_FALSE; - allowLeadingWhitespace = PR_FALSE; - column = 0; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - lineStart = nsnull; - lineLength = 0; - currentLineNumber++; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - needRelativePos = PR_FALSE; - - prevLineLastFrame = aPrevLineLastFrame; - prevLineHeight = aPrevLineHeight; - prevMaxPosBottomMargin = maxPosBottomMargin; - prevMaxNegBottomMargin = maxNegBottomMargin; - - // Remember previous line's lastContentIsComplete - prevLineLastContentIsComplete = lastContentIsComplete; - lastContentIsComplete = PR_TRUE; - - topMargin = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; -} - -#ifdef NS_DEBUG -void nsBlockReflowState::DumpLine() -{ - nsIFrame* f = lineStart; - PRInt32 ll = lineLength; - while (--ll >= 0) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} - -void nsBlockReflowState::DumpList() -{ - nsIFrame* f = lineStart; - while (nsnull != f) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} -#endif - //---------------------------------------------------------------------- -nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, - nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) +nsresult +nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + PRInt32 aIndexInParent, + nsIFrame* aParent) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); if (nsnull == aInstancePtrResult) { @@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, } nsBlockFrame::nsBlockFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) + PRInt32 aIndexInParent, + nsIFrame* aParent) : nsHTMLContainerFrame(aContent, aIndexInParent, aParent) { } nsBlockFrame::~nsBlockFrame() { - if (nsnull != mLines) { - delete mLines; - } + DestroyLines(); } -nsresult +void nsBlockFrame::DestroyLines() +{ +} + +NS_METHOD nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) { NS_PRECONDITION(0 != aInstancePtr, "null ptr"); if (NULL == aInstancePtr) { return NS_ERROR_NULL_POINTER; } - if (aIID.Equals(kIHTMLFrameTypeIID)) { - *aInstancePtr = (void*) ((nsIHTMLFrameType*) this); + if (aIID.Equals(kBlockFrameCID)) { + *aInstancePtr = (void*) (this); return NS_OK; - } else if (aIID.Equals(kIRunaroundIID)) { + } + else if (aIID.Equals(kIRunaroundIID)) { *aInstancePtr = (void*) ((nsIRunaround*) this); return NS_OK; - } else if (aIID.Equals(kIFloaterContainerIID)) { + } + else if (aIID.Equals(kIFloaterContainerIID)) { *aInstancePtr = (void*) ((nsIFloaterContainer*) this); return NS_OK; } return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr); } -// Computes the top margin to use for this child frames based on its display -// type and the display type of the previous child frame. -// -// Adjacent vertical margins between block-level elements are collapsed. -nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC, - PRBool aIsInline) +NS_METHOD +nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const { - if (aIsInline) { - // Just use whatever the previous bottom margin was - return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin; + aIsSplittable = frSplittableNonRectangular; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, + nsIFrame* aParent, + nsIFrame*& aContinuingFrame) +{ + nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); + PrepareContinuingFrame(aCX, aParent, cf); + aContinuingFrame = cf; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::ListTag(FILE* out) const +{ + if ((nsnull != mGeometricParent) && IsPseudoFrame()) { + fprintf(out, "*block<"); + nsIAtom* atom = mContent->GetTag(); + if (nsnull != atom) { + nsAutoString tmp; + atom->ToString(tmp); + fputs(tmp, out); + } + fprintf(out, ">(%d)@%p", mIndexInParent, this); } else { - // Does the frame have a prev-in-flow? - nsIFrame* kidPrevInFlow; + nsHTMLContainerFrame::ListTag(out); + } + return NS_OK; +} - aKidFrame->GetPrevInFlow(kidPrevInFlow); +NS_METHOD +nsBlockFrame::List(FILE* out, PRInt32 aIndent) const +{ + // Indent + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); - if (nsnull == kidPrevInFlow) { + // Output the tag + ListTag(out); + + // Output the first/last content offset + fprintf(out, "[%d,%d,%c] pif=%p nif=%p", + mFirstContentOffset, mLastContentOffset, + (mLastContentIsComplete ? 'T' : 'F'), + mPrevInFlow, mNextInFlow); + + // Output the rect + out << mRect; + + // Output the children, one line at a time + if (nsnull != mLines) { + fputs("<\n", out); + aIndent++; + + nsLineData* line = mLines; + while (nsnull != line) { + line->List(out, aIndent); + line = line->mNextLine; + } + + aIndent--; + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); + fputs(">\n", out); + } else { + fputs("<>\n", out); + } + + return NS_OK; +} + +NS_METHOD +nsBlockFrame::VerifyTree() const +{ + nsresult rv = nsHTMLContainerFrame::VerifyTree(); + if (NS_OK != rv) { + return rv; + } + rv = VerifyLines(PR_TRUE); + return rv; +} + +nsresult +nsBlockFrame::VerifyLines(PRBool aFinalCheck) const +{ + nsresult rv = NS_OK; + + // Make sure that the list of children agrees with our child count. + // If this is not the case then the child list and the line list are + // not properly arranged. + PRInt32 len = LengthOf(mFirstChild); + NS_ASSERTION(mChildCount == len, "bad child list"); + + // Verify that our lines are correctly setup + PRInt32 offset = mFirstContentOffset; + PRInt32 lineChildCount = 0; + nsLineData* line = mLines; + nsLineData* prevLine = nsnull; + while (nsnull != line) { + if (aFinalCheck) { + NS_ASSERTION(((offset == line->mFirstContentOffset) && + (line->mFirstContentOffset <= line->mLastContentOffset)), + "bad line mFirstContentOffset"); + NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset, + "bad line mLastContentOffset"); + offset = line->mLastContentOffset; + if (line->mLastContentIsComplete) { + offset++; + } + } + lineChildCount += line->mChildCount; + rv = line->Verify(aFinalCheck); + if (NS_OK != rv) { + return rv; + } + prevLine = line; + line = line->mNextLine; + } + if (aFinalCheck && (nsnull != prevLine)) { + NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset, + "bad mLastContentOffset"); + NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete, + "bad mLastContentIsComplete"); + } + NS_ASSERTION(lineChildCount == mChildCount, "bad line counts"); + + return rv; +} + +//---------------------------------------------------------------------- + +// Remove a next-in-flow from from this block's list of lines + +// XXX problems here: +// 1. we always have to start from the first line: slow! +// 2. we can avoid this when the child is not the last child in a line + +void +nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow) +{ + // When a reflow indicates completion it's possible that + // next-in-flows were just removed. We have to remove them from any + // nsLineData's that follow the current line. + nsLineData* line = mLines; + while (nsnull != line) { + if (line->mFirstChild == aNextInFlow) { + // Remove child from line. + if (0 == --line->mChildCount) { + line->mFirstChild = nsnull; + } + else { + // Fixup the line + nsIFrame* nextKid; + aNextInFlow->GetNextSibling(nextKid); + line->mFirstChild = nextKid; + nextKid->GetIndexInParent(line->mFirstContentOffset); + } + break; + } + line = line->mNextLine; + } +} + +nsresult +nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize, + aMaxElementSize); + return NS_OK; +} + +nsresult +nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager, + aMaxSize, aDesiredRect, aMaxElementSize); + return NS_OK; +} + +nsLineData* +nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList) +{ + nsLineData* newLine = new nsLineData(); + if (nsnull != newLine) { + nsIFrame* kid = aOverflowList; + newLine->mFirstChild = kid; + kid->GetIndexInParent(newLine->mFirstContentOffset); + newLine->mLastContentOffset = -1; + newLine->mLastContentIsComplete = PRPackedBool(0x255); + PRInt32 kids = 0; + while (nsnull != kid) { + kids++; + kid->GetNextSibling(kid); + } + newLine->mChildCount = kids; + } + return newLine; +} + +void +nsBlockFrame::DrainOverflowList() +{ + nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow; + if (nsnull != prevBlock) { + nsIFrame* overflowList = prevBlock->mOverflowList; + if (nsnull != overflowList) { + NS_ASSERTION(nsnull == mFirstChild, "bad overflow list"); + NS_ASSERTION(nsnull == mLines, "bad overflow list"); + + // Create a line to hold the entire overflow list + nsLineData* newLine = CreateLineForOverflowList(overflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(overflowList); + prevBlock->mOverflowList = nsnull; + + // The new line is the first line + mLines = newLine; + } + } + + if (nsnull != mOverflowList) { + NS_ASSERTION(nsnull != mFirstChild, + "overflow list but no mapped children"); + + // Create a line to hold the overflow list + nsLineData* newLine = CreateLineForOverflowList(mOverflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(mOverflowList, PR_FALSE); + mOverflowList = nsnull; + + // The new line is appended after our other lines + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + while (nsnull != line) { + prevLine = line; + line = line->mNextLine; + } + if (nsnull == prevLine) { + mLines = newLine; + } + else { + prevLine->mNextLine = newLine; + newLine->mPrevLine = prevLine; + } + } +#ifdef NS_DEBUG + VerifyLines(PR_FALSE); +#endif +} + +// XXX add in code here that notices if margin's were not provided by +// the style system and when that is the case to apply the old layout +// engines margin calculations. + +nsresult +nsBlockFrame::PlaceLine(nsBlockReflowState& aState, + nsLineLayout& aLineLayout, + nsLineData* aLine) +{ + nsresult rv = NS_LINE_LAYOUT_COMPLETE; + + nscoord topMargin = 0; + nscoord bottomMargin = 0; + nscoord maxNegBottomMargin = 0; + nscoord maxPosBottomMargin = 0; + + // See if block margins apply to this line or not + PRBool isBlockLine = PR_FALSE; + if (1 == aLine->mChildCount) { + nsIStyleContextPtr kidSC; + nsIFrame* kid = aLine->mFirstChild; + rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef()); + if (NS_OK != rv) return rv; + + nsStyleDisplay* display = (nsStyleDisplay*) + kidSC->GetData(kStyleDisplaySID); + switch (display->mDisplay) { + case NS_STYLE_DISPLAY_BLOCK: + case NS_STYLE_DISPLAY_LIST_ITEM: + isBlockLine = PR_TRUE; + nsStyleSpacing* spacing = (nsStyleSpacing*) + kidSC->GetData(kStyleSpacingSID); + + // Calculate top margin by collapsing with previous bottom margin + // if any. nscoord maxNegTopMargin = 0; nscoord maxPosTopMargin = 0; - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - if (ss->mMargin.top < 0) { - maxNegTopMargin = -ss->mMargin.top; + if (spacing->mMargin.top < 0) { + maxNegTopMargin = -spacing->mMargin.top; } else { - maxPosTopMargin = ss->mMargin.top; + maxPosTopMargin = spacing->mMargin.top; } - - nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin); - nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin); - return maxPos - maxNeg; - } else { - return 0; + nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin); + nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin); + topMargin = maxPos - maxNeg; + + // Save away bottom information for later promotion into aState + if (spacing->mMargin.bottom < 0) { + maxNegBottomMargin = -spacing->mMargin.bottom; + } else { + maxPosBottomMargin = spacing->mMargin.bottom; + } + break; } } + + // Before we move the line, make sure that it will fit in it's new + // location. It always fits if the height isn't constrained or it's + // the first line. + nscoord totalHeight = topMargin + aLine->mBounds.height; + if (!aState.mUnconstrainedHeight && (aLine != mLines)) { + if (aState.mY + totalHeight > aState.mAvailSize.height) { + // The line will not fit + rv = PushLines(aState, aLine); + goto done; + } + } + if (isBlockLine) { + if (0 != topMargin) { + // We have to move the line now that we know the top margin + // for it. + aLine->MoveLineBy(0, topMargin); + aState.mY += topMargin; + } + } + else { + // Apply previous line's bottom margin before the inline-line. + nscoord bottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (0 != bottomMargin) { + aLine->MoveLineBy(0, bottomMargin); + aState.mY += topMargin; + } + } + + // Consume space and advance running values + aState.mY += aLine->mBounds.height; + aState.mPrevMaxNegBottomMargin = maxNegBottomMargin; + aState.mPrevMaxPosBottomMargin = maxPosBottomMargin; + if (nsnull != aState.mMaxElementSizePointer) { + nsSize* maxSize = aState.mMaxElementSizePointer; + if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) { + maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width; + } + if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) { + maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height; + } + } + { + nscoord xmost = aLine->mBounds.XMost(); + if (xmost > aState.mKidXMost) { + aState.mKidXMost = xmost; + } + } + + // Any below current line floaters to place? + if (aState.mPendingFloaters.Count() > 0) { + PlaceBelowCurrentLineFloaters(aState, aState.mY); + // XXX Factor in the height of the floaters as well when considering + // whether the line fits. + // The default policy is that if there isn't room for the floaters then + // both the line and the floaters are pushed to the next-in-flow... + } + + if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) { + // The current y coordinate is now past our available space + // rectangle. Get a new band of space. + GetAvailableSpace(aState, aState.mY); + } + +done: + return rv; } -void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX, - nsBlockReflowState& aState, - nscoord aY) +// aY has borderpadding.top already factored in +// aResult is relative to left,aY +nsresult +nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY) { - NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters"); + nsresult rv = NS_OK; - // XXX Factor this code with PlaceFloater()... - PRInt32 numFloaters = aState.floaterToDo.Count(); - - for (PRInt32 i = 0; i < numFloaters; i++) { - nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i]; - nsRect region; + nsISpaceManager* sm = aState.mSpaceManager; - // Get the band of available space - // XXX This is inefficient to do this inside the loop... - GetAvailableSpaceBand(aState, aY); + // Fill in band data for the specific Y coordinate + sm->Translate(aState.mBorderPadding.left, 0); + sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand); + sm->Translate(-aState.mBorderPadding.left, 0); - // Get the type of floater - nsIStyleContextPtr styleContext; - - floater->GetStyleContext(aCX, styleContext.AssignRef()); - nsStyleDisplay* sd = (nsStyleDisplay*) - styleContext->GetData(kStyleDisplaySID); - - floater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; + // Compute the bounding rect of the available space, i.e. space + // between any left and right floaters + aState.mCurrentBand.ComputeAvailSpaceRect(); - if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; - } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; - } - - // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, - 0); - mCurrentState->spaceManager->AddRectRegion(region, floater); - - // Set the origin of the floater in world coordinates - nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); - floater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); - } - aState.floaterToDo.Clear(); -} - -/** - * Flush a line out. Return true if the line fits in our available - * height. If the line does not fit then return false. When the line - * fits we advance the y coordinate, reset the x coordinate and - * prepare the nsBlockReflowState for the next line. - */ -PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - NS_PRECONDITION(aState.lineLength > 0, "bad line"); - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nscoord y = aState.y + aState.topMargin; - nscoord lineHeight; - - if (aState.isInline) { - // Vertically align the children on this line, returning the height of - // the line upon completion. - lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this, - aState.styleFont, - y, - aState.lineStart, - aState.lineLength, - aState.ascents, - aState.maxAscent); - - // Any below current line floaters to place? - if (aState.floaterToDo.Count() > 0) { - PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight); - // XXX Factor in the height of the floaters as well when considering - // whether the line fits. - // The default policy is that if there isn't room for the floaters then - // both the line and the floaters are pushed to the next-in-flow... - } - } else { - nsSize size; - - aState.lineStart->GetSize(size); - lineHeight = size.height; - } - - // The first line always fits - if (aState.currentLineNumber > 0) { - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if (y + lineHeight > yb) { - // After vertical alignment of the children and factoring in the - // proper margin, the line doesn't fit. - return PR_FALSE; - } - } - - if (aState.isInline) { - // Check if the right-edge of the line exceeds our running x-most - nscoord xMost = aState.borderPadding.left + aState.lineWidth; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - } - - // Advance the y coordinate to the new position where the next - // line or block element will go. - aState.y = y + lineHeight; - aState.x = 0; - - // Now that the vertical alignment is done we can perform horizontal - // alignment and relative positioning. Skip all of these if we are - // doing an unconstrained (in x) reflow. There's no point in doing - // the work if we *know* we are going to reflowed again. - if (!aState.unconstrainedWidth) { - nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText, - aState.lineStart, aState.lineLength, - aState.lineWidth, - aState.availSize.width); - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - if (!aState.justifying) { - nsCSSLayout::RelativePositionChildren(aCX, this, - aState.lineStart, - aState.lineLength); - } - } - - // Record line length - aState.lineLengths.AppendElement((void*)aState.lineLength); - - // Find the last frame in the line - // XXX keep this as running state in the nsBlockReflowState - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - // Update maxElementSize - if (nsnull != aState.maxElementSize) { - nsSize& lineMax = aState.lineMaxElementSize; - nsSize* maxMax = aState.maxElementSize; - if (lineMax.width > maxMax->width) { - maxMax->width = lineMax.width; - } - if (lineMax.height > maxMax->height) { - maxMax->height = lineMax.height; - } - aState.lineMaxElementSize.width = 0; - aState.lineMaxElementSize.height = 0; - } - - // Advance to the next line - aState.AdvanceToNextLine(lastFrame, lineHeight); - - return PR_TRUE; -} - -/** - * Add an inline child to the current line. Advance various running - * values after placement. - */ -void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsReflowMetrics& aKidSize, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) -{ - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; - } - - // Place and size the child - // XXX add in left margin from kid - nsRect r; - r.y = aState.y; - r.width = aKidSize.width; - r.height = aKidSize.height; - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - // Left to right positioning. - r.x = aState.borderPadding.left + aState.x + ss->mMargin.left; - aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right; - } else { - // Right to left positioning - // XXX what should we do when aState.x goes negative??? - r.x = aState.x - aState.borderPadding.right - ss->mMargin.right - - aKidSize.width; - aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left; - } - aKidFrame->SetRect(r); - aState.AddAscent(aKidSize.ascent); - aState.lineWidth += aKidSize.width; - aState.lineLength++; - - // Update maximums for the line - if (aKidSize.ascent > aState.maxAscent) { - aState.maxAscent = aKidSize.ascent; - } - if (aKidSize.descent > aState.maxDescent) { - aState.maxDescent = aKidSize.descent; - } - // Update running margin maximums - if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) { - // XXX temporary code. Since the molecule for the bullet frame - // is the same as the LI frame, we get bad style information. - // ignore it. - } else { - nscoord margin; #if 0 - // XXX CSS2 spec says that top/bottom margin don't affect line height - // calculation. We're waiting for clarification on this issue... - if ((margin = ss->mMargin.top) < 0) { - margin = -margin; - if (margin > aState.maxNegTopMargin) { - aState.maxNegTopMargin = margin; - } - } else { - if (margin > aState.maxPosTopMargin) { - aState.maxPosTopMargin = margin; - } - } + // XXX For now we assume that there are no height restrictions + // (e.g. no "float to bottom of column/page") + nsRect& availSpace = aState.mCurrentBand.availSpace; + aResult.x = availSpace.x; + aResult.y = availSpace.y; + aResult.width = availSpace.width; + aResult.height = aState.mAvailSize.height; +#else + aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left, + aState.mY); #endif - if ((margin = ss->mMargin.bottom) < 0) { - margin = -margin; - if (margin > aState.maxNegBottomMargin) { - aState.maxNegBottomMargin = margin; - } - } else { - if (margin > aState.maxPosBottomMargin) { - aState.maxPosBottomMargin = margin; - } - } - } - // Update line max element size - nsSize& mes = aState.lineMaxElementSize; - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > mes.width) { - mes.width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > mes.height) { - mes.height = aKidMaxElementSize->height; - } - } + return rv; } -// Places and sizes the block-level element, and advances the line. -// The rect is in the local coordinate space of the kid frame. -void nsBlockFrame::AddBlockChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsRect& aKidRect, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) +// Give aLine and any successive lines to the block's next-in-flow; if +// we don't have a next-in-flow then push all the children onto our +// overflow list. +nsresult +nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine) { - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); + PRInt32 i; - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; + // Split our child-list in two; revert our last content offset and + // completion status to the previous line. + nsLineData* prevLine = aLine->mPrevLine; + NS_PRECONDITION(nsnull != prevLine, "pushing first line"); + nsIFrame* prevKidFrame = prevLine->mFirstChild; + for (i = prevLine->mChildCount - 1; --i >= 0; ) { + prevKidFrame->GetNextSibling(prevKidFrame); } - - // Translate from the kid's coordinate space to our coordinate space - aKidRect.x += aState.borderPadding.left + ss->mMargin.left; - aKidRect.y += aState.y + aState.topMargin; - - // Place and size the child - aKidFrame->SetRect(aKidRect); - - aState.AddAscent(aKidRect.height); - aState.lineLength++; - - // Is this the widest child frame? - nscoord xMost = aKidRect.XMost() + ss->mMargin.right; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - - // Update the max element size - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > aState.maxElementSize->width) { - aState.maxElementSize->width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > aState.maxElementSize->height) { - aState.maxElementSize->height = aKidMaxElementSize->height; - } - } - - // and the bottom line margin information which we'll use when placing - // the next child - if (ss->mMargin.bottom < 0) { - aState.maxNegBottomMargin = -ss->mMargin.bottom; - } else { - aState.maxPosBottomMargin = ss->mMargin.bottom; - } - - // Update the running y-offset - aState.y += aKidRect.height + aState.topMargin; - - // Apply relative positioning if necessary - nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1); - - // Advance to the next line - aState.AdvanceToNextLine(aKidFrame, aKidRect.height); -} - -/** - * Compute the available size for reflowing the given child at the - * current x,y position in the state. Note that this may return - * negative or zero width/height's if we are out of room. - */ -void nsBlockFrame::GetAvailSize(nsSize& aResult, - nsBlockReflowState& aState, - nsIStyleContext* aKidSC, - PRBool aIsInline) -{ - // Determine the maximum available reflow height for the child - nscoord yb = aState.borderPadding.top + aState.availSize.height; - aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE : - yb - aState.y - aState.topMargin; - - // Determine the maximum available reflow width for the child - if (aState.unconstrainedWidth) { - aResult.width = NS_UNCONSTRAINEDSIZE; - } else if (aIsInline) { - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - aResult.width = aState.currentBand->availSpace.XMost() - aState.x; - } else { - aResult.width = aState.x - aState.currentBand->availSpace.x; - } - } else { - // It's a block. Don't adjust for the left/right margin here. That happens - // later on once we know the current left/right edge - aResult.width = aState.availSize.width; - } -} - -/** - * Push all of the kids that we have not reflowed, starting at - * aState.lineStart. aPrevKid is the kid previous to aState.lineStart - * and is also our last child. Note that line length is NOT a - * reflection of the number of children we are actually pushing - * (because we don't break the sibling list as we add children to the - * line). - */ -void nsBlockFrame::PushKids(nsBlockReflowState& aState) -{ - nsIFrame* prevFrame = aState.prevLineLastFrame; - NS_PRECONDITION(nsnull != prevFrame, "pushing all kids"); #ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevFrame->GetNextSibling(nextSibling); - NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line"); + nsIFrame* nextFrame; + prevKidFrame->GetNextSibling(nextFrame); + NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list"); #endif + prevKidFrame->SetNextSibling(nsnull); + prevLine->mNextLine = nsnull; + mLastContentOffset = prevLine->mLastContentOffset; + mLastContentIsComplete = prevLine->mLastContentIsComplete; + + // Push children to our next-in-flow if we have, or to our overflow list + nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; + PRInt32 pushCount = 0; + if (nsnull == nextInFlow) { + // Place children on the overflow list + mOverflowList = aLine->mFirstChild; + + // Destroy the lines + nsLineData* line = aLine; + while (nsnull != line) { + pushCount += line->mChildCount; + nsLineData* next = line->mNextLine; + delete line; + line = next; + } + } + else { + aLine->mPrevLine = nsnull; + + // Pass on the children to our next-in-flow + nsLineData* line = aLine; + prevLine = line; + nsIFrame* lastKid = aLine->mFirstChild; + nsIFrame* kid = lastKid; + while (nsnull != line) { + i = line->mChildCount; + pushCount += i; + NS_ASSERTION(kid == line->mFirstChild, "bad line list"); + while (--i >= 0) { + kid->SetGeometricParent(nextInFlow); + nsIFrame* contentParent; + kid->GetContentParent(contentParent); + if (this == contentParent) { + kid->SetContentParent(nextInFlow); + } + lastKid = kid; + kid->GetNextSibling(kid); + } + prevLine = line; + line = line->mNextLine; + } + + // Join the two line lists + nsLineData* nextInFlowLine = nextInFlow->mLines; + if (nsnull != nextInFlowLine) { + lastKid->SetNextSibling(nextInFlowLine->mFirstChild); + nextInFlowLine->mPrevLine = prevLine; + } + nsIFrame* firstKid = aLine->mFirstChild; + prevLine->mNextLine = nextInFlowLine; + nextInFlow->mLines = aLine; + nextInFlow->mFirstChild = firstKid; + nextInFlow->mChildCount += pushCount; + firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset); #ifdef NS_DEBUG - PRInt32 numKids = LengthOf(mFirstChild); - NS_ASSERTION(numKids == mChildCount, "bad child count"); + nextInFlow->VerifyLines(PR_FALSE); +#endif + } + mChildCount -= pushCount; + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return NS_LINE_LAYOUT_NOT_COMPLETE; +} + +nsresult +nsBlockFrame::ReflowMapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != line) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // If we have no children and we have a prev-in-flow then we need to + // pick up where it left off. If we have children, e.g. we're being + // resized, then our content offset will have already been set + // correctly. + nsIFrame* kidPrevInFlow = nsnull; + if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { + nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; + mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary? + if (PR_FALSE == prev->mLastContentIsComplete) { + // Our prev-in-flow's last child is not complete + prev->LastChild(kidPrevInFlow); + } + } + + // Get to the last line where the new content may be added + nsLineData* line = nsnull; + nsLineData* prevLine = nsnull; + if (nsnull != mLines) { + line = mLines; + while (nsnull != line->mNextLine) { + line = line->mNextLine; + } + prevLine = line; + + // If the last line is not complete then kidPrevInFlow should be + // set to the last-line's last child. + if (!prevLine->mLastContentIsComplete) { + kidPrevInFlow = prevLine->GetLastChild(); + } + } + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + // Now reflow the new content until we are out of new content or out + // of vertical space. + PRInt32 kidIndex = NextChildOffset(); + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + lineLayout.mKidPrevInFlow = kidPrevInFlow; + PRInt32 contentChildCount = mContent->ChildCount(); + while (kidIndex < contentChildCount) { + if (nsnull == line) { + if (!MoreToReflow(aState)) { + break; + } + line = new nsLineData(); + if (nsnull == line) { + rv = NS_ERROR_OUT_OF_MEMORY; + goto done; + } + line->mFirstContentOffset = kidIndex; + } + + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mKidPrevInFlow = kidPrevInFlow; + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Add line to the block; do this before placing the line in case + // PushLines is needed. + if (nsnull == prevLine) { + // For the first line, initialize mFirstContentOffset + mFirstContentOffset = line->mFirstContentOffset; + mFirstChild = line->mFirstChild; + mLines = line; + } + else { + prevLine->mNextLine = line; + line->mPrevLine = prevLine; + } + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + kidIndex = lineLayout.mKidIndex; + kidPrevInFlow = lineLayout.mKidPrevInFlow; + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; + if (aState.mBlockIsPseudo) { + PropagateContentOffsets(); + } + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::InitializeState(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockReflowState& aState) +{ + nsresult rv; + rv = aState.Initialize(aPresContext, aSpaceManager, + aMaxSize, aMaxElementSize, this); + + nsStyleSpacing* mySpacing = (nsStyleSpacing*) + mStyleContext->GetData(kStyleSpacingSID); + + // Apply border and padding adjustments for regular frames only + if (!aState.mBlockIsPseudo) { + aState.mY = mySpacing->mBorderPadding.top; + aState.mX = mySpacing->mBorderPadding.left; + aState.mAvailSize.width -= + (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); + aState.mAvailSize.height -= + (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); + aState.mBorderPadding = mySpacing->mBorderPadding; + } + else { + aState.mBorderPadding.SizeTo(0, 0, 0, 0); + } + + // Setup initial list ordinal value + nsIAtom* tag = mContent->GetTag(); + if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || + (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { + nsHTMLValue value; + if (eContentAttr_HasValue == + ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { + if (eHTMLUnit_Integer == value.GetUnit()) { + aState.mNextListOrdinal = value.GetIntValue(); + } + } + } + NS_RELEASE(tag); + + return rv; +} + +#if XXX +NS_METHOD +nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight) +{ + printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth), + NS_TWIPS_TO_POINTS_FLOAT(aHeight)); + return nsHTMLContainerFrame::SizeTo(aWidth, aHeight); +} + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, desiredRect, aStatus); + aDesiredSize.width = desiredRect.width; + aDesiredSize.height = desiredRect.height; + aDesiredSize.ascent = aDesiredSize.height; + aDesiredSize.descent = 0; + } + return rv; +} #endif -#ifdef NOISY - ListTag(stdout); - printf(": push kids (childCount=%d)\n", mChildCount); - DumpFlow(); -#endif - - PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete); - SetLastContentOffset(prevFrame); - - // Set mLastContentIsComplete to the previous lines last content is - // complete now that the previous line's last child is our last - // child. - mLastContentIsComplete = aState.prevLineLastContentIsComplete; - - // Fix up child count - // XXX is there a better way? aState.lineLength doesn't work because - // we might be pushing more than just the pending line. - nsIFrame* kid = mFirstChild; - PRInt32 kids = 0; - while (nsnull != kid) { - kids++; - kid->GetNextSibling(kid); - } - mChildCount = kids; - - // Make sure we have no lingering line data - aState.lineLength = 0; - aState.lineStart = nsnull; - -#ifdef NOISY - ListTag(stdout); - printf(": push kids done (childCount=%d) [%c]\n", mChildCount, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif -} - -/** - * Gets a band of available space starting at the specified y-offset. Assumes - * the local coordinate space is currently set to the upper-left origin of the - * bounding rect - * - * Updates "currentBand" and "x" member data of the block reflow state - */ -void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY) -{ - // Gets a band of available space. - aState.spaceManager->Translate(aState.borderPadding.left, 0); - aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand); - - // Compute the bounding rect of the available space, i.e. space between any - // left and right floaters - aState.currentBand->ComputeAvailSpaceRect(); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - aState.x = aState.currentBand->availSpace.x; -} - -void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear) -{ - // Translate the coordinate space - aState.spaceManager->Translate(aState.borderPadding.left, 0); - -getBand: - nscoord y = aState.y + aState.topMargin; - PRBool isLeftFloater = PR_FALSE; - PRBool isRightFloater = PR_FALSE; - - // Get a band of available space - aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand); - - // Scan the trapezoids looking for left and right floaters - nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids; - for (PRInt32 i = 0; i < aState.currentBand->count; i++) { - // XXX Handle multiply occupied - if (nsBandTrapezoid::smOccupied == trapezoid->state) { - nsStyleDisplay* display; - - trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display); - if (NS_STYLE_FLOAT_LEFT == display->mFloats) { - isLeftFloater = PR_TRUE; - } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) { - isRightFloater = PR_TRUE; - } - } - - trapezoid++; - } - - if (isLeftFloater) { - if ((aClear == NS_STYLE_CLEAR_LEFT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - if (isRightFloater) { - if ((aClear == NS_STYLE_CLEAR_RIGHT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - - aState.spaceManager->Translate(-aState.borderPadding.left, 0); -} - -// Bit's for PlaceAndReflowChild return value -#define PLACE_FIT 0x1 -#define PLACE_FLOWED 0x2 - -PRIntn -nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC) -{ - nsSize kidMaxElementSize; - nsSize* pKidMaxElementSize = - (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; - - // Get line start setup if we are at the start of a new line - if (nsnull == aState.lineStart) { - NS_ASSERTION(0 == aState.lineLength, "bad line length"); - aState.lineStart = aKidFrame; - } - - // Get kid and its style - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - - // Figure out if kid is a block element or not - PRBool isInline = PR_TRUE; - PRIntn display = styleDisplay->mDisplay; - if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) { - // XXX Special hack for properly reflowing bullets that have the - // inside value for list-style-position. - display = NS_STYLE_DISPLAY_INLINE; - } - if ((NS_STYLE_DISPLAY_BLOCK == display) || - (NS_STYLE_DISPLAY_LIST_ITEM == display)) { - // Block elements always end up on the next line (unless they are - // already at the start of the line). - isInline = PR_FALSE; - if (aState.lineLength > 0) { - aState.breakAfterChild = PR_TRUE; - } - } - - // Handle forced break first - if (aState.breakAfterChild) { - NS_ASSERTION(aState.lineStart != aKidFrame, "bad line"); - - // Get the last child in the current line - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - if (!AdvanceToNextLine(aCX, aState)) { - // The previous line didn't fit. - return 0; - } - aState.lineStart = aKidFrame; - - // Get the style for the last child, and see if it wanted to clear - // floaters. This handles the BR tag, which is the only inline - // element for which clear applies - nsIStyleContextPtr lastChildSC; - - lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef()); - nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*) - lastChildSC->GetData(kStyleDisplaySID); - switch (lastChildDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, lastChildDisplay->mBreakType); - break; - } - } - - // Now that we've handled force breaks (and maybe called AdvanceToNextLine() - // which checks), remember whether it's an inline frame - aState.isInline = isInline; - - // If we're at the beginning of a line then compute the top margin that we - // should use - if (aState.lineStart == aKidFrame) { - // Compute the top margin to use for this line - aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC, - aState.isInline); - - // If it's an inline element then get a band of available space - // - // XXX If we have a current band and there's unused space in that band - // then avoid this call to get a band... - if (aState.isInline) { - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - } - } - - // Compute the available space to reflow the child into and then - // reflow it into that space. - nsSize kidAvailSize; - GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - - ReflowStatus status; - - if (aState.isInline) { - nsReflowMetrics kidSize; - - // Inline elements are never passed the space manager - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - NS_ASSERTION(nsnull != aState.lineStart, "bad line start"); - - if (aKidFrame == aState.lineStart) { - // Width always fits when we are at the logical left margin. - // Just check the height. - // - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall - return PLACE_FLOWED; - } - } else { - // Examine state and if the breakBeforeChild is set and we - // aren't already on the new line, do the forcing now. - // XXX Why aren't we doing this check BEFORE we resize reflow the child? - if (aState.breakBeforeChild) { - aState.breakBeforeChild = PR_FALSE; - if (aKidFrame != aState.lineStart) { - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow child now that it has the line to itself - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - } - } - - // When we are not at the logical left margin then we need - // to check the width first. If we are too wide then advance - // to the next line and try reflowing again. - if (kidSize.width > kidAvailSize.width) { - // Too wide. Try next line - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow splittable children - SplittableType isSplittable; - - aKidFrame->IsSplittable(isSplittable); - if (isSplittable != frNotSplittable) { - // Update size info now that we are on the next line. Then - // reflow the child into the new available space. - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - } - - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall on the next line - return PLACE_FLOWED; - } - // It's ok if it's too wide on the next line. - } - } - } - - // Add child to the line - AddInlineChildToLine(aCX, aState, aKidFrame, kidSize, - pKidMaxElementSize, aKidSC); - } else { - nsRect kidRect; - - // Does the block-level element want to clear any floaters that impact - // it? Note that the clear property only applies to block-level elements - // and the BR tag - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - switch (styleDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, styleDisplay->mBreakType); - GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - break; - } - - // Give the block its own local coordinate space.. Note: ReflowChild() - // will adjust for the child's left/right margin after determining the - // current left/right edge - aState.spaceManager->Translate(aState.borderPadding.left, 0); - // Give the block-level element the opportunity to use the space manager - status = ReflowChild(aKidFrame, aCX, aState.spaceManager, - kidAvailSize, kidRect, pKidMaxElementSize); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - // Block elements always fit horizontally (because they are - // always placed at the logical left margin). Check to see if - // the block fits vertically - if (kidRect.YMost() > kidAvailSize.height) { - // Nope - return PLACE_FLOWED; - } - } - - // Add block child - // XXX We need to set lastContentIsComplete here, because AddBlockChild() - // calls AdvaneceToNextLine(). We need to restructure the flow of control, - // and use a state machine... - aState.lastContentIsComplete = PRBool(status == frComplete); - AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC); - } - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - - aState.lastContentIsComplete = PRBool(status == frComplete); - if (aState.isInline && (frNotComplete == status)) { - // Since the inline child didn't complete its reflow we *know* - // that a continuation of it can't possibly fit on the current - // line. Therefore, set a flag in the state that will cause the - // a line break before the next frame is placed. - aState.breakAfterChild = PR_TRUE; - } - - aState.reflowStatus = status; - return PLACE_FLOWED | PLACE_FIT; -} - -/** - * Reflow the existing frames. - * - * @param aCX presentation context to use - * @param aState in out parameter which tracks the state of - * reflow for the block frame. - * @return true if we successfully reflowed all the mapped children and false - * otherwise, e.g. we pushed children to the next in flow - */ PRBool -nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsIFrame* kidFrame; - - for (kidFrame = mFirstChild; nsnull != kidFrame; ) { - - /* we get the kid's style from kidFrame's cached style context */ - nsIStyleContextPtr kidSC; - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - - // Attempt to place and reflow the child - - // XXX if child is not splittable and it fits just place it where - // it is, otherwise advance to the next line and place it there if - // possible - - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - ReflowStatus status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // The child doesn't fit. Push it and any remaining children. - PushKids(aState); - result = PR_FALSE; - goto push_done; - } - - // Is the child complete? - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (frComplete == status) { - // Yes, the child is complete - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); - } else { - // No the child isn't complete - if (nsnull == kidNextInFlow) { - // The child doesn't have a next-in-flow so create a continuing - // frame. This hooks the child into the flow - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); - - // Add the continuing frame to the sibling list - nsIFrame* nextSib; - - kidFrame->GetNextSibling(nextSib); - continuingFrame->SetNextSibling(nextSib); - kidFrame->SetNextSibling(continuingFrame); - mChildCount++; - } - - // Unlike the inline frame code we can't assume that we used - // up all of our space because the child's reflow status is - // frNotComplete. Instead, the child is probably split and - // we need to reflow the continuations as well. - } - - // Get the next child frame - kidFrame->GetNextSibling(kidFrame); - } - - push_done: -#ifdef NS_DEBUG - nsIFrame* lastChild; - PRInt32 lastIndexInParent; - - LastChild(lastChild); - lastChild->GetIndexInParent(lastIndexInParent); - NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset"); - - PRInt32 len = LengthOf(mFirstChild); - NS_POSTCONDITION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -/* XXX: - * In this method, we seem to be getting the style for the next child, - * and checking that child's style for the child's display type. - * The problem is that it's very inefficient to ResolveStyleFor(kid) here - * and then just throw it away, when we end up doing a ResolveStyleFor(kid) - * again when we actually create a frame for the content. - * It would be nice to cache the resolved style, maybe in the reflow state? - */ -PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) +nsBlockFrame::MoreToReflow(nsBlockReflowState& aState) { PRBool rv = PR_FALSE; - if (IsPseudoFrame()) { + if (aState.mBlockIsPseudo) { // Get the next content object that we would like to reflow PRInt32 kidIndex = NextChildOffset(); nsIContentPtr kid = mContent->ChildAt(kidIndex); if (kid.IsNotNull()) { // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); + nsIStyleContextPtr kidSC = + aState.mPresContext->ResolveStyleContextFor(kid, this); nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*) kidSC->GetData(kStyleDisplaySID); switch (kidStyleDisplay->mDisplay) { @@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) return rv; } -/** - * Create new frames for content we haven't yet mapped - * - * @param aCX presentation context to use - * @return frComplete if all content has been mapped and frNotComplete - * if we should be continued - */ -nsIFrame::ReflowStatus -nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) +nsBlockReflowState* +nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext, + nsIFrame* aFrame) { -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif - nsIFrame* kidPrevInFlow = nsnull; - ReflowStatus result = frNotComplete; - - // If we have no children and we have a prev-in-flow then we need to pick - // up where it left off. If we have children, e.g. we're being resized, then - // our content offset should already be set correctly... - if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { - nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; - NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow"); - mFirstContentOffset = prev->NextChildOffset(); - if (PR_FALSE == prev->mLastContentIsComplete) { - // Our prev-in-flow's last child is not complete - prev->LastChild(kidPrevInFlow); - } - } - - // Place our children, one at a time until we are out of children - PRInt32 kidIndex = NextChildOffset(); - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (kid.IsNull()) { - result = frComplete; - break; - } - - // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - nsStylePosition* kidPosition = (nsStylePosition*) - kidSC->GetData(kStylePositionSID); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); - - // Check whether it wants to floated or absolutely positioned - if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) { - AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (nsnull == kidPrevInFlow) { - // Create initial frame for the child - nsIContentDelegate* kidDel; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // If we're being used as a pseudo frame, i.e. we map the same - // content as our parent then we want to indicate we're complete; - // otherwise we'll be continued and go on mapping children... - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - result = frComplete; - goto done; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aCX); - kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow"); - nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); + nsBlockReflowState* state = nsnull; + if (nsnull != aFrame) { + nsIFrame* parent; + aFrame->GetGeometricParent(parent); + while (nsnull != parent) { + nsBlockFrame* block; + nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block); + if (NS_OK == rv) { + nsIPresShell* shell = aPresContext->GetShell(); + state = (nsBlockReflowState*) shell->GetCachedData(block); + NS_RELEASE(shell); break; } - kidFrame->SetStyleContext(aCX,kidSC); - } else { - // Since kid has a prev-in-flow, use that to create the next - // frame. - kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame); + parent->GetGeometricParent(parent); } - - // Link child frame into the list of children. If the frame ends - // up not fitting and getting pushed, the PushKids code will fixup - // the child count for us. - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - mChildCount++; - - // Reflow child frame as many times as necessary until it is - // complete. - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // We ran out of room. - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - goto push_done; - } - - // Did the child complete? - prevKidFrame = kidFrame; - if (frNotComplete == status) { - // Child didn't complete so create a continuing frame - kidPrevInFlow = kidFrame; - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to the sibling list - nsIFrame* kidNextSibling; - - kidFrame->GetNextSibling(kidNextSibling); - continuingFrame->SetNextSibling(kidNextSibling); - kidFrame->SetNextSibling(continuingFrame); - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "huh?"); -#endif - } while (frNotComplete == status); - - // The child that we just reflowed is complete -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); -#endif - kidIndex++; - kidPrevInFlow = nsnull; } - - done: - // To get here we either completely reflowed all our appended - // children OR we are a pseudo-frame and we ran into a block - // element. In either case our last content MUST be complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - - push_done: -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - return result; + return state; } -/** - * Pullup frames from our next in flow and try to place them. Before - * this is called our previously mapped children, if any have been - * reflowed which means that the block reflow state's x and y - * coordinates and other data are ready to go. - * - * Return true if we pulled everything up. - */ -PRBool -nsBlockFrame::PullUpChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - while (nsnull != nextInFlow) { - // Get first available frame from the next-in-flow - nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame); - if (nsnull == kidFrame) { - // We've pulled up all the children from that next-in-flow, so - // move to the next next-in-flow. - nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; - continue; - } - - // Get style information for the pulled up kid - nsIContentPtr kid; - - kidFrame->GetContent(kid.AssignRef()); - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // Push the kids that didn't fit back down to the next-in-flow - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - result = PR_FALSE; - goto push_done; - } - - if (frNotComplete == status) { - // Child is not complete - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (nsnull == kidNextInFlow) { - // Create a continuing frame for the incomplete child - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to our sibling list. - nsIFrame* nextSibling; - - kidFrame->GetNextSibling(nextSibling); - continuingFrame->SetNextSibling(nextSibling); - kidFrame->SetNextSibling(continuingFrame); - prevKidFrame = kidFrame; - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } else { - // The child has a next-in-flow, but it's not one of ours. - // It *must* be in one of our next-in-flows. Collect it - // then. - NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow"); - break; - } - } - } while (frNotComplete == status); - - prevKidFrame = kidFrame; - } - - if (nsnull != prevKidFrame) { - // The only way we can get here is by pulling up every last child - // in our next-in-flows (and reflowing any continunations they - // have). Therefore we KNOW that our last child is complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - } - - push_done:; - - if (result == PR_FALSE) { - // If our next-in-flow is empty OR our next next-in-flow is empty - // then adjust the offsets of all of the empty next-in-flows. - nextInFlow = (nsBlockFrame*) mNextInFlow; - if ((0 == nextInFlow->mChildCount) || - ((nsnull != nextInFlow->mNextInFlow) && - (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) { - // We didn't pullup everything and we need to fixup one of our - // next-in-flows content offsets. - AdjustOffsetOfEmptyNextInFlows(); - } - } - - -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -void nsBlockFrame::SetupState(nsIPresContext* aCX, - nsBlockReflowState& aState, - const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsISpaceManager* aSpaceManager) -{ - // Setup reflow state - aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager); - - nsStyleSpacing* mySpacing = (nsStyleSpacing*) - mStyleContext->GetData(kStyleSpacingSID); - - // Apply border and padding adjustments for regular frames only - if (PR_FALSE == IsPseudoFrame()) { - aState.borderPadding = mySpacing->mBorderPadding; - aState.y = mySpacing->mBorderPadding.top; - aState.availSize.width -= - (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); - aState.availSize.height -= - (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); - } else { - aState.borderPadding.SizeTo(0, 0, 0, 0); - } - - // Setup initial list ordinal value - nsIAtom* tag = mContent->GetTag(); - if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || - (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { - nsHTMLValue value; - if (eContentAttr_HasValue == - ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { - if (eHTMLUnit_Integer == value.GetUnit()) { - aState.nextListOrdinal = value.GetIntValue(); - } - } - } - NS_RELEASE(tag); - - mCurrentState = &aState; -} - -#include "nsUnitConversion.h"/* XXX */ -NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsSize* aMaxElementSize, - ReflowStatus& aStatus) -{ - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager); - return DoResizeReflow(aCX, state, aDesiredRect, aStatus); -} - -nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsRect& aDesiredRect, - ReflowStatus& aStatus) +nsresult +nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + ReflowStatus& aStatus) { #ifdef NS_DEBUG + VerifyLines(PR_TRUE); PreReflowCheck(); #endif -#ifdef NOISY +#ifdef NOISY_REFLOW ListTag(stdout); - printf(": resize reflow %g,%g\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height)); - DumpFlow(); + printf(": Before:\n"); + List(stdout, 1); #endif - // Zap old line data - if (nsnull != mLines) { - delete mLines; - mLines = nsnull; - } - mNumLines = 0; + nsresult rv = NS_OK; - // Check for an overflow list - MoveOverflowToChildList(); - - // Before we start reflowing, cache a pointer to our state structure - // so that inline frames can find it. - nsIPresShell* shell = aCX->GetShell(); + nsIPresShell* shell = aState.mPresContext->GetShell(); shell->PutCachedData(this, &aState); - // First reflow any existing frames - PRBool reflowMappedOK = PR_TRUE; - aStatus = frComplete; - if (nsnull != mFirstChild) { - reflowMappedOK = ReflowMappedChildren(aCX, aState); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } + // Check for an overflow list + DrainOverflowList(); + + if (nsnull != mLines) { + rv = ReflowMapped(aState); } - if (reflowMappedOK) { - // Any space left? - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if ((nsnull != mFirstChild) && (aState.y >= yb)) { - // No space left. Don't try to pull-up children or reflow - // unmapped. We need to return the correct completion status, - // so see if there is more to reflow. - if (MoreToReflow(aCX)) { - aStatus = frNotComplete; - } - } else if (MoreToReflow(aCX)) { - // Try and pull-up some children from a next-in-flow - if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) { - // If we still have unmapped children then create some new frames - if (MoreToReflow(aCX)) { - aStatus = ReflowAppendedChildren(aCX, aState); - } - } else { - // We were unable to pull-up all the existing frames from the - // next in flow - aStatus = frNotComplete; - } + if (NS_OK == rv) { + if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) { + // We are out of space } - } - - // Deal with last line - make sure it gets vertically and - // horizontally aligned. This also updates the state's y coordinate - // which is good because that's how we size ourselves. - if (0 != aState.lineLength) { - if (!AdvanceToNextLine(aCX, aState)) { - // The last line of output doesn't fit. Push all of the kids to - // the next in flow and change our reflow status to not complete - // so that we are continued. -#ifdef NOISY - ListTag(stdout); - printf(": pushing kids since last line doesn't fit\n"); -#endif - - PushKids(aState); - aStatus = frNotComplete; + if (MoreToReflow(aState)) { + rv = ReflowUnmapped(aState); } } - if (frComplete == aStatus) { - // Don't forget to add in the bottom margin from our last child. - // Only add it in if there is room for it. - nscoord margin = aState.prevMaxPosBottomMargin - - aState.prevMaxNegBottomMargin; - nscoord y = aState.y + margin; - if (y <= aState.borderPadding.top + aState.availSize.height) { - aState.y = y; - } - } - - // Now that reflow has finished, remove the cached pointer - shell->RemoveCachedData(this); - NS_RELEASE(shell); - - // Translate state.lineLengths into an integer array - mNumLines = aState.lineLengths.Count(); - if (mNumLines > 0) { - mLines = new PRInt32[mNumLines]; - for (PRInt32 i = 0; i < mNumLines; i++) { - PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i); - mLines[i] = ll; - } - } - - if (!aState.unconstrainedWidth && aState.justifying) { - // Perform justification now that we know how many lines we have. - JustifyLines(aCX, aState); - } - // Return our desired rect and our status aDesiredRect.x = 0; aDesiredRect.y = 0; - aDesiredRect.width = aState.kidXMost + aState.borderPadding.right; - if (!aState.unconstrainedWidth) { + aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right; + if (!aState.mUnconstrainedWidth) { // Make sure we're at least as wide as the max size we were given - nscoord maxWidth = aState.availSize.width + aState.borderPadding.left + - aState.borderPadding.right; - + nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left + + aState.mBorderPadding.right; if (aDesiredRect.width < maxWidth) { aDesiredRect.width = maxWidth; } } - aState.y += aState.borderPadding.bottom; - aDesiredRect.height = aState.y; - -#ifdef NS_DEBUG - PostReflowCheck(aStatus); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height), - ((status == frNotComplete) ? "NOT " : ""), - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F') - ); - DumpFlow(); -#endif - mCurrentState = nsnull; - return NS_OK; -} - -void nsBlockFrame::JustifyLines(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - // XXX we don't justify the last line; what if we are continued, - // should we do it then? - nsIFrame* kid = mFirstChild; - for (PRInt32 i = 0; i < mNumLines; i++) { - nsIFrame* lineStart = kid; - PRInt32 lineLength = mLines[i]; - if (i < mNumLines - 1) { - if (1 == lineLength) { - // For lines with one element on them we can take a shortcut and - // let them do the justification. Note that we still call - // JustifyReflow even if the available space is zero in case the - // child is a hunk of text that ends in whitespace. The whitespace - // will be distributed elsewhere causing a proper flush right look - // for the last word. - nsRect r; - kid->GetRect(r); - nscoord maxWidth = aState.availSize.width; - nscoord availableSpace = maxWidth - r.width; - nscoord maxAvailSpace = nscoord(maxWidth * 0.1f); - if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) { - kid->JustifyReflow(aCX, availableSpace); - kid->SizeTo(r.width + availableSpace, r.height); - } - kid->GetNextSibling(kid); - } else { - // XXX Get justification of multiple elements working - while (--lineLength >= 0) { - kid->GetNextSibling(kid); - } + aState.mY += aState.mBorderPadding.bottom; + nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) { + // It's possible that we don't have room for the last bottom + // margin (the last bottom margin is the margin following a block + // element that we contain; it isn't applied immediately because + // of the margin collapsing logic). This can happen when we are + // reflowed in a limited amount of space because we don't know in + // advance what the last bottom margin will be. + nscoord maxY = aMaxSize.height; + if (aState.mY + lastBottomMargin > maxY) { + lastBottomMargin = maxY - aState.mY; + if (lastBottomMargin < 0) { + lastBottomMargin = 0; } } - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]); + aState.mY += lastBottomMargin; } -} + aDesiredRect.height = aState.mY; -NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const -{ - aIsSplittable = frSplittableNonRectangular; - return NS_OK; -} - -NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, - nsIFrame* aParent, - nsIFrame*& aContinuingFrame) -{ - nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); - PrepareContinuingFrame(aCX, aParent, cf); - aContinuingFrame = cf; - return NS_OK; -} - -NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsReflowCommand& aReflowCommand, - ReflowStatus& aStatus) -{ + // Set return status aStatus = frComplete; - - if (aReflowCommand.GetTarget() == this) { - // XXX for now, just do a complete reflow mapped (it'll kinda - // work, but it's slow) - - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager); - PRBool reflowMappedOK = ReflowMappedChildren(aCX, state); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } - } else { - // XXX not yet implemented - NS_ABORT(); - // XXX work to compute initial state goes *HERE* - aDesiredRect.x = 0; - aDesiredRect.y = 0; - aDesiredRect.width = 0; - aDesiredRect.height = 0; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; } +#ifdef NS_DEBUG + // Verify that the line layout code pulled everything up when it + // indicates a complete reflow. + if (frComplete == aStatus) { + nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow; + while (nsnull != nextBlock) { + NS_ASSERTION((nsnull == nextBlock->mLines) && + (nextBlock->mChildCount == 0) && + (nsnull == nextBlock->mFirstChild), + "bad completion status"); + nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow; + } - mCurrentState = nsnull; - return NS_OK; +#if XXX + // We better not be in the same parent frame as our prev-in-flow. + // If we are it means that we were continued instead of pulling up + // children. + if (nsnull != mPrevInFlow) { + nsIFrame* ourParent = mGeometricParent; + nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent; + NS_ASSERTION(ourParent != prevParent, "bad continuation"); + } +#endif + } +#endif + + // Now that reflow has finished, remove the cached pointer + shell->RemoveCachedData(this); + NS_RELEASE(shell); + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); + PostReflowCheck(aStatus); +#endif +#ifdef NOISY_REFLOW + ListTag(stdout); + printf(": After:\n"); + List(stdout, 1); +#endif + return rv; } -PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ContentAppended(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentInserted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentReplaced(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentDeleted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext, + nsReflowMetrics& aMetrics) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aSpaceManager, aMaxSize, + aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus); + } + return rv; +} + +NS_METHOD +nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsReflowCommand& aReflowCommand, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +PRBool +nsBlockFrame::AddFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + + if (nsnull != state) { + // Get the frame associated with the space manager, and get its + // nsIAnchoredItems interface + nsIFrame* frame = state->mSpaceManager->GetFrame(); + nsIAnchoredItems* anchoredItems = nsnull; + + frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); + NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); + if (nsnull != anchoredItems) { + anchoredItems->AddAnchoredItem(aFloater, + nsIAnchoredItems::anHTMLFloater, + this); + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + return PR_TRUE; + } + } + + return PR_FALSE; +} + +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + if (nsnull != state) { + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + } +} + +PRBool +nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) { do { nsIFrame* parent; - aFrame->GetGeometricParent(parent); - // See if there are any non-zero sized child frames that precede aFrame - // in the child list + // See if there are any non-zero sized child frames that precede + // aFrame in the child list nsIFrame* child; - parent->FirstChild(child); while ((nsnull != child) && (aFrame != child)) { nsSize size; @@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // We found a non-zero sized child frame that precedes aFrame return PR_FALSE; } - child->GetNextSibling(child); } @@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // Walk up one level and check that its parent is left-most as well aFrame = parent; } while (aFrame != this); - return PR_TRUE; } -PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) -{ - // Get the frame associated with the space manager, and get its nsIAnchoredItems - // interface - nsIFrame* frame = mCurrentState->spaceManager->GetFrame(); - nsIAnchoredItems* anchoredItems = nsnull; - - frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); - NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); - - if (nsnull != anchoredItems) { - anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this); - PlaceFloater(aCX, aFloater, aPlaceholder); - return PR_TRUE; - } - - return PR_FALSE; -} - -// XXX The size of the floater needs to be taken into consideration if we're -// computing a maximum element size -void nsBlockFrame::PlaceFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder, + nsBlockReflowState& aState) { // If the floater is the left-most non-zero size child frame then insert // it before the current line; otherwise add it to the below-current-line // todo list and we'll handle it when we flush out the line if (IsLeftMostChild(aPlaceholder)) { - // Get the type of floater - nsIStyleContextPtr styleContext; + nsISpaceManager* sm = aState.mSpaceManager; - aFloater->GetStyleContext(aCX, styleContext.AssignRef()); + // Get the type of floater + nsIStyleContextPtr styleContext; + aFloater->GetStyleContext(aPresContext, styleContext.AssignRef()); nsStyleDisplay* floaterDisplay = (nsStyleDisplay*) styleContext->GetData(kStyleDisplaySID); - if (!mCurrentState->isInline) { - // Get the current band for this line - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); - } - - // Commit some space in the space manager - nsRect region; - + // Commit some space in the space manager and adjust our current + // band of available space. + nsRect region; aFloater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; - + region.y = aState.mY; if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; + region.x = aState.mX; } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, + "bad float type"); + region.x = aState.mCurrentBand.availSpace.XMost() - region.width; } // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0); - mCurrentState->spaceManager->AddRectRegion(region, aFloater); + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, aFloater); // Set the origin of the floater in world coordinates nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); + sm->GetTranslation(worldX, worldY); aFloater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); + sm->Translate(-aState.mBorderPadding.left, 0); - // Update the band of available space to reflect space taken up by the floater - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); + // Update the band of available space to reflect space taken up by + // the floater + GetAvailableSpace(aState, aState.mY); + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); + } } else { // Add the floater to our to-do list - mCurrentState->floaterToDo.AppendElement(aFloater); + aState.mPendingFloaters.AppendElement(aFloater); } } -NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell, - nsIPresContext* aPresContext, - nsIContent* aContainer) +void +nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState, + nscoord aY) { - // Get the last-in-flow - nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow(); - PRInt32 kidIndex = flow->NextChildOffset(); - PRInt32 startIndex = kidIndex; + NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters"); -#if 0 - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - flow->LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (nsnull == kid) { - break; - } + nsISpaceManager* sm = aState.mSpaceManager; + nsBlockBandData* bd = &aState.mCurrentBand; - // Resolve style for the kid - nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); + // XXX Factor this code with PlaceFloater()... + PRInt32 numFloaters = aState.mPendingFloaters.Count(); + for (PRInt32 i = 0; i < numFloaters; i++) { + nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i]; + nsRect region; - // Is it a floater? - if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); + // Get the band of available space + // XXX This is inefficient to do this inside the loop... + GetAvailableSpace(aState, aY); + + // Get the type of floater + nsIStyleContextPtr styleContext; + floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef()); + nsStyleDisplay* sd = (nsStyleDisplay*) + styleContext->GetData(kStyleDisplaySID); + + floater->GetRect(region); + region.y = bd->availSpace.y; + if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { + region.x = bd->availSpace.x; } else { - // Create initial frame for the child - nsIContentDelegate* kidDel; - nsresult fr; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // Update last content offset now that we are done drawing - // children from our parent. - SetLastContentOffset(prevKidFrame); - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - return NS_OK; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aPresContext); - kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); - break; - } + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); + region.x = bd->availSpace.XMost() - region.width; } - kidFrame->SetStyleContext(aCX,kidSC); - // Link child frame into the list of children - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; + // XXX Don't forget the floater's margins... + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, floater); - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - kidIndex++; - mChildCount++; + // Set the origin of the floater in world coordinates + nscoord worldX, worldY; + sm->GetTranslation(worldX, worldY); + floater->MoveTo(region.x + worldX, region.y + worldY); + sm->Translate(-aState.mBorderPadding.left, 0); } - SetLastContentOffset(prevKidFrame); -#endif - // If this is a pseudo-frame then our parent will generate the - // reflow command. Otherwise, if the container is us then we should - // generate the reflow command because we were directly called. - if (!IsPseudoFrame() && (aContainer == mContent)) { - nsReflowCommand* rc = - new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended, - startIndex); - aShell->AppendReflowCommand(rc); + aState.mPendingFloaters.Clear(); + + // Pass on updated available space to the current line + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); } - return NS_OK; } -PRIntn nsBlockFrame::GetSkipSides() const +//---------------------------------------------------------------------- + +nsLineData* +nsBlockFrame::GetFirstLine() +{ + return mLines; +} + +PRIntn +nsBlockFrame::GetSkipSides() const { PRIntn skip = 0; if (nsnull != mPrevInFlow) { @@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const } return skip; } - -nsHTMLFrameType nsBlockFrame::GetFrameType() const -{ - return eHTMLFrame_Block; -} - -NS_METHOD nsBlockFrame::ListTag(FILE* out) const -{ - if ((nsnull != mGeometricParent) && IsPseudoFrame()) { - fprintf(out, "*block<"); - nsIAtom* atom = mContent->GetTag(); - if (nsnull != atom) { - nsAutoString tmp; - atom->ToString(tmp); - fputs(tmp, out); - } - fprintf(out, ">(%d)@%p", mIndexInParent, this); - } else { - nsHTMLContainerFrame::ListTag(out); - } - return NS_OK; -} - -#ifdef NS_DEBUG -void nsBlockFrame::DumpFlow() const -{ -#ifdef NOISY_FLOW - nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow; - while (flow != 0) { - printf(" %p: [%d,%d,%c]\n", - flow, flow->mFirstContentOffset, flow->mLastContentOffset, - (flow->mLastContentIsComplete ? 'T' : 'F')); - flow = (nsBlockFrame*) flow->mNextInFlow; - } -#endif -} -#endif diff --git a/layout/html/base/src/nsBlockFrame.h b/layout/html/base/src/nsBlockFrame.h index f11bba52ca10..f3fa810539b5 100644 --- a/layout/html/base/src/nsBlockFrame.h +++ b/layout/html/base/src/nsBlockFrame.h @@ -20,158 +20,101 @@ #include "nsHTMLContainerFrame.h" #include "nsIFloaterContainer.h" -#include "nsIHTMLFrameType.h" #include "nsIRunaround.h" +#include "nsISpaceManager.h" +#include "nsLineLayout.h" #include "nsVoidArray.h" -struct BlockBandData; + struct nsMargin; struct nsStyleDisplay; struct nsStyleFont; struct nsStyleText; +class nsBlockFrame; +struct nsBandData; + +struct nsBlockBandData : public nsBandData { + // Trapezoid's used during band processing + nsBandTrapezoid data[12]; + + // Bounding rect of available space between any left and right floaters + nsRect availSpace; + + nsBlockBandData() { + size = 12; + trapezoids = data; + } + + /** + * Computes the bounding rect of the available space, i.e. space + * between any left and right floaters Uses the current trapezoid + * data, see nsISpaceManager::GetBandData(). Also updates member + * data "availSpace". + */ + void ComputeAvailSpaceRect(); +}; -/** - * Block frames have some state which needs to be made - * available to child frames for proper reflow. This structure - * describes that state. - */ struct nsBlockReflowState { - // True if this is the first line of text in a block container - PRPackedBool firstLine; - - // True if leading whitespace is allowed to show - PRPackedBool allowLeadingWhitespace; - - // This is set when some child frame needs a break after it's placed - PRPackedBool breakAfterChild; - - // This is set when a child needs a break before it's placed. Note - // that this value is set by a child AFTER we have called it's - // ResizeReflow method. - PRPackedBool breakBeforeChild; - - // This is set when the first child should be treated specially - // because it's an inside list item bullet. - // XXX this can go away once we have a way for the bullet's style - // molecule to *not* be the same as it's parent's - PRPackedBool firstChildIsInsideBullet; - - // For pre-formatted text, this is our current column - PRIntn column; - - // The next list ordinal value - PRInt32 nextListOrdinal; - - //---------------------------------------------------------------------- - // State from here on down is not to be used by block child frames! - - // Space manager to use - nsISpaceManager* spaceManager; - - // Block's style context - nsIStyleContext* styleContext; - nsStyleText* styleText; - nsStyleFont* styleFont; - nsStyleDisplay* styleDisplay; - - // Block's available size (computed from the block's parent) - nsSize availSize; - - // Current band of available space. Used for doing runaround - BlockBandData* currentBand; - - // Pointer to a max-element-size (nsnull if none required) - nsSize* maxElementSize; - - // The maximum x-most of our lines and block-level elements. This is used to - // compute our desired size, and includes our left border/padding. For block- - // level elements this also includes the block's right margin. - nscoord kidXMost; - - // Current reflow position - nscoord y; - - // Current line state - nscoord x; // inline elements only. not include border/padding - PRBool isInline; // whether the current is inline or block - nsVoidArray lineLengths; // line length temporary storage - PRIntn currentLineNumber; // index into mLines - nsIFrame* lineStart; // frame starting the line - PRInt32 lineLength; // length of line - nscoord* ascents; // ascent information for each child - nscoord maxAscent; // max ascent for this line - nscoord maxDescent; // max descent for this line - nscoord lineWidth; // current width of line -#if 0 - nscoord maxPosTopMargin; // maximum positive top margin - nscoord maxNegTopMargin; // maximum negative top margin -#else - nscoord topMargin; // current top margin -#endif - nscoord maxPosBottomMargin; // maximum positive bottom margin - nscoord maxNegBottomMargin; // maximum negative bottom margin - nsSize lineMaxElementSize; // max element size for current line - PRBool lastContentIsComplete; // reflow status of last child on line - nsVoidArray floaterToDo; // list of floaters to place below current line - - PRInt32 maxAscents; // size of ascent buffer - nscoord ascentBuf[20]; - - PRPackedBool needRelativePos; // some kid in line needs relative pos - - // Previous line state that we carry forward to the next line - nsIFrame* prevLineLastFrame; - nscoord prevLineHeight; // height of the previous line - nscoord prevMaxPosBottomMargin; // maximum posative bottom margin - nscoord prevMaxNegBottomMargin; // maximum negative bottom margin - PRBool prevLineLastContentIsComplete; - - // Sanitized version of mol->borderPadding; if this block frame is a - // pseudo-frame then the margin will be zero'd. - nsMargin borderPadding; - - PRPackedBool justifying; // we are justifying - - // Status from last PlaceAndReflowChild - nsIFrame::ReflowStatus reflowStatus; - - // Flags for whether the max size is unconstrained - PRBool unconstrainedWidth; - PRBool unconstrainedHeight; - nsBlockReflowState(); - ~nsBlockReflowState(); - void Init(const nsSize& aMaxSize, nsSize* aMaxElementSize, - nsIStyleContext* aStyleContext, nsISpaceManager* aSpaceManager); + nsresult Initialize(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockFrame* aBlock); - void AddAscent(nscoord aAscent); - void AdvanceToNextLine(nsIFrame* aPrevLineLastFrame, nscoord aPrevLineHeight); + nsIPresContext* mPresContext; -#ifdef NS_DEBUG - void DumpLine(); - void DumpList(); -#endif + nsBlockFrame* mBlock; + PRBool mBlockIsPseudo; + + // Current line being reflowed + nsLineLayout* mCurrentLine; + + // Previous line's last child frame + nsIFrame* mPrevKidFrame; + + // Layout position information + nscoord mX; + nscoord mY; + nsSize mAvailSize; + PRPackedBool mUnconstrainedWidth; + PRPackedBool mUnconstrainedHeight; + nsSize* mMaxElementSizePointer; + nscoord mKidXMost; + + // Bottom margin information from the previous line + nscoord mPrevMaxNegBottomMargin; + nscoord mPrevMaxPosBottomMargin; + + // Block frame border+padding information + nsMargin mBorderPadding; + + // Space manager and current band information + nsISpaceManager* mSpaceManager; + nsBlockBandData mCurrentBand; + + // Array of floaters to place below current line + nsVoidArray mPendingFloaters; + + PRInt32 mNextListOrdinal; + PRPackedBool mFirstChildIsInsideBullet; }; //---------------------------------------------------------------------- +/* 94e8e410-de21-11d1-89bf-006008911b81 */ +#define NS_BLOCKFRAME_CID \ + {0x94e8e410, 0xde21, 0x11d1, {0x89, 0xbf, 0x00, 0x60, 0x08, 0x91, 0x1b, 0x81}} + /** *

Block Reflow

* * The block frame reflow machinery performs "2D" layout. Inline * elements are flowed into logical lines (left to right or right to - * left) and the lines are stacked vertically. Block elements are - * flowed onto their own line after flushing out any preceeding line.

- * - * After a line is ready to be flushed out, vertical alignment is - * performed. Vertical alignment may require the line to consume more - * vertical space than is available thus causing the entire line to - * be pushed.

- * - * After vertical alignment is done, horizontal alignment (including - * justification) is performed. Finally, relative positioning is done - * on any elements that require it.

+ * left) and the lines are stacked vertically. nsLineLayout is used + * for this part of the process. Block elements are flowed directly by + * the block reflow logic after flushing out any preceeding line.

* * During reflow, the block frame will make available to child frames * it's reflow state using the presentation shell's cached data @@ -186,144 +129,155 @@ struct nsBlockReflowState { * *

Assertions

* mLastContentIsComplete always reflects the state of the last - * child frame on our chlid list. + * child frame on our chlid list. */ -class nsBlockFrame : public nsHTMLContainerFrame, public nsIHTMLFrameType, - public nsIRunaround, public nsIFloaterContainer + +// XXX we don't use nsContainerFrame mOverFlowList!!! wasted memory + +class nsBlockFrame : public nsHTMLContainerFrame, + public nsIRunaround, + public nsIFloaterContainer { public: /** * Create a new block frame that maps the given piece of content. */ - static nsresult NewFrame(nsIFrame** aInstancePtrResult, + static nsresult NewFrame(nsIFrame** aInstancePtrResult, nsIContent* aContent, PRInt32 aIndexInParent, nsIFrame* aParent); + // nsISupports NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); - NS_IMETHOD ResizeReflow(nsIPresContext* aPresContext, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsSize* aMaxElementSize, - ReflowStatus& aStatus); - - NS_IMETHOD IncrementalReflow(nsIPresContext* aPresContext, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsReflowCommand& aReflowCommand, - ReflowStatus& aStatus); - - NS_IMETHOD ContentAppended(nsIPresShell* aShell, + // nsIFrame + NS_IMETHOD ContentAppended(nsIPresShell* aShell, nsIPresContext* aPresContext, - nsIContent* aContainer); - + nsIContent* aContainer); + NS_IMETHOD ContentInserted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent); + NS_IMETHOD ContentReplaced(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInParent); + NS_IMETHOD ContentDeleted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent); + NS_IMETHOD GetReflowMetrics(nsIPresContext* aPresContext, + nsReflowMetrics& aMetrics); NS_IMETHOD IsSplittable(SplittableType& aIsSplittable) const; - NS_IMETHOD CreateContinuingFrame(nsIPresContext* aPresContext, - nsIFrame* aParent, - nsIFrame*& aContinuingFrame); + nsIFrame* aParent, + nsIFrame*& aContinuingFrame); + NS_IMETHOD List(FILE* out = stdout, PRInt32 aIndent = 0) const; + NS_IMETHOD ListTag(FILE* out) const; + NS_IMETHOD VerifyTree() const; - virtual PRBool AddFloater(nsIPresContext* aCX, - nsIFrame* aFloater, + // nsIRunaround + NS_IMETHOD ResizeReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + nsIFrame::ReflowStatus& aStatus); + NS_IMETHOD IncrementalReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsReflowCommand& aReflowCommand, + nsIFrame::ReflowStatus& aStatus); + + // nsIFloaterContainer + virtual PRBool AddFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, PlaceholderFrame* aPlaceholder); - virtual void PlaceFloater(nsIPresContext* aCX, - nsIFrame* aFloater, + virtual void PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, PlaceholderFrame* aPlaceholder); - NS_IMETHOD ListTag(FILE* out = stdout) const; + // nsBlockFrame + nsresult ReflowInlineChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus); - virtual nsHTMLFrameType GetFrameType() const; + nsresult ReflowBlockChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + ReflowStatus& aStatus); + + nsLineData* GetFirstLine(); + + static nsBlockReflowState* FindBlockReflowState(nsIPresContext* aPresContext, + nsIFrame* aFrame); protected: nsBlockFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent); + PRInt32 aIndexInParent, + nsIFrame* aParent); virtual ~nsBlockFrame(); virtual PRIntn GetSkipSides() const; - PRBool MoreToReflow(nsIPresContext* aCX); + virtual void WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow); - nscoord GetTopMarginFor(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC, - PRBool aIsInline); - - PRBool AdvanceToNextLine(nsIPresContext* aPresContext, + nsresult InitializeState(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, nsBlockReflowState& aState); - void AddInlineChildToLine(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsReflowMetrics& aKidSize, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC); + nsresult DoResizeReflow(nsBlockReflowState& aState, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + ReflowStatus& aStatus); - void AddBlockChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsRect& aKidRect, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC); + void DestroyLines(); - void GetAvailSize(nsSize& aResult, - nsBlockReflowState& aState, - nsIStyleContext* aKidSC, - PRBool aIsInline); + void DrainOverflowList(); - PRIntn PlaceAndReflowChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* kidFrame, - nsIStyleContext* aKidSC); + nsLineData* CreateLineForOverflowList(nsIFrame* aOverflowList); - void PushKids(nsBlockReflowState& aState); + nsresult VerifyLines(PRBool aFinalCheck) const; - void SetupState(nsIPresContext* aCX, nsBlockReflowState& aState, - const nsSize& aMaxSize, nsSize* aMaxElementSize, - nsISpaceManager* aSpaceManager); - - nsresult DoResizeReflow(nsIPresContext* aPresContext, - nsBlockReflowState& aState, - nsRect& aDesiredRect, - ReflowStatus& aStatus); - - PRBool ReflowMappedChildren(nsIPresContext* aPresContext, - nsBlockReflowState& aState); - - PRBool PullUpChildren(nsIPresContext* aCX, - nsBlockReflowState& aState); - - ReflowStatus ReflowAppendedChildren(nsIPresContext* aPresContext, - nsBlockReflowState& aState); - - void JustifyLines(nsIPresContext* aPresContext, nsBlockReflowState& aState); + nsresult PlaceLine(nsBlockReflowState& aState, + nsLineLayout& aLineLayout, + nsLineData* aLine); PRBool IsLeftMostChild(nsIFrame* aFrame); - void GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY); - - void PlaceBelowCurrentLineFloaters(nsIPresContext* aCX, - nsBlockReflowState& aState, + void PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder, + nsBlockReflowState& aState); + void PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState, nscoord aY); - void ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear); + nsresult GetAvailableSpace(nsBlockReflowState& aState, nscoord aY); -#ifdef NS_DEBUG - void DumpFlow() const; -#endif + PRBool MoreToReflow(nsBlockReflowState& aState); - /** - * Array of lines lengths. For each logical line of children, this array - * contains a count of the number of children on the line. - */ - PRInt32* mLines; - PRInt32 mNumLines; - nsBlockReflowState* mCurrentState; + nsresult PushLines(nsBlockReflowState& aState, + nsLineData* aLine); + + nsresult ReflowMapped(nsBlockReflowState& aState); + + nsresult ReflowUnmapped(nsBlockReflowState& aState); + + nsLineData* mLines; }; #endif /* nsBlockFrame_h___ */ diff --git a/layout/html/base/src/nsBlockReflowState.cpp b/layout/html/base/src/nsBlockReflowState.cpp index 59d7407ec5b1..0228ca80d413 100644 --- a/layout/html/base/src/nsBlockReflowState.cpp +++ b/layout/html/base/src/nsBlockReflowState.cpp @@ -16,66 +16,37 @@ * Reserved. */ #include "nsBlockFrame.h" -#include "nsSize.h" -#include "nsIAnchoredItems.h" -#include "nsIContent.h" -#include "nsIContentDelegate.h" -#include "nsISpaceManager.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" -#include "nsIPresContext.h" -#include "nsMargin.h" -#include "nsHTMLIIDs.h" -#include "nsCSSLayout.h" -#include "nsCRT.h" -#include "nsIPresShell.h" -#include "nsReflowCommand.h" -#include "nsPlaceholderFrame.h" -#include "nsHTMLAtoms.h" -#include "nsHTMLValue.h" #include "nsIHTMLContent.h" -#include "nsAbsoluteFrame.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIAnchoredItems.h" +#include "nsPlaceholderFrame.h" #include "nsIPtr.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLIIDs.h" +#include "nsHTMLValue.h" -#ifdef NS_DEBUG -#undef NOISY -#undef NOISY_FLOW -#else -#undef NOISY -#undef NOISY_FLOW -#endif +// XXX what do we do with catastrophic errors (rv < 0)? What is the +// state of the reflow world after such an error? -static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); -static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); -static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +#undef NOISY_REFLOW static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID); -static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID); -static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID); -static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID); static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID); -NS_DEF_PTR(nsIStyleContext); +static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); +static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); + NS_DEF_PTR(nsIContent); +NS_DEF_PTR(nsIStyleContext); -struct BlockBandData : public nsBandData { - nsBandTrapezoid data[12]; +//---------------------------------------------------------------------- - // Bounding rect of available space between any left and right floaters - nsRect availSpace; - // Constructor - BlockBandData() {size = 12; trapezoids = data;} - - // Computes the bounding rect of the available space, i.e. space between - // any left and right floaters - // - // Uses the current trapezoid data, see nsISpaceManager::GetBandData(). - // Updates member data "availSpace" - void ComputeAvailSpaceRect(); -}; - -void BlockBandData::ComputeAvailSpaceRect() +void nsBlockBandData::ComputeAvailSpaceRect() { nsBandTrapezoid* trapezoid = data; @@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect() } } -// XXX Bugs -// 1. right to left reflow can generate negative x coordinates. - -// XXX Speedup idea (all containers) - -// If I reflow a child and it gives back not-complete status then -// there is no sense in trying to pullup children. For blocks, it's a -// little more complicated unless the child is a block - if the child -// is a block, then we must be out of room hence we should stop. If -// the child is not a block then our line should be flushed (see #2 -// below) if our line is already empty then we must be out of room. - -// For inline frames and column frames, if we reflow a child and get -// back not-complete status then we should bail immediately because we -// are out of room. - -// XXX Speedup ideas: -// 1. change pullup code to use line information from next in flow -// 2. we can advance to next line immediately after reflowing something -// and noticing that it's not complete. -// 3. pass down last child information in aState so that pullup, etc., -// don't need to recompute it - -// XXX TODO: -// 0. Move justification into line flushing code - -// 1. To get ebina margins I need "auto" information from the style -// system margin's. A bottom/top margin of auto will then be computed like -// ebina computes it [however the heck that is]. - -// 2. kicking out floaters and talking with floater container to adjust -// left and right margins +//---------------------------------------------------------------------- nsBlockReflowState::nsBlockReflowState() { } -void nsBlockReflowState::Init(const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsIStyleContext* aBlockSC, - nsISpaceManager* aSpaceManager) +nsBlockReflowState::~nsBlockReflowState() { - firstLine = PR_TRUE; - allowLeadingWhitespace = PR_FALSE; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - firstChildIsInsideBullet = PR_FALSE; - nextListOrdinal = -1; - column = 0; +} - spaceManager = aSpaceManager; - currentBand = new BlockBandData; +nsresult +nsBlockReflowState::Initialize(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockFrame* aBlock) +{ + nsresult rv = NS_OK; - styleContext = aBlockSC; - styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID); - styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID); - styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID); + mPresContext = aPresContext; + mBlock = aBlock; + mSpaceManager = aSpaceManager; + mBlockIsPseudo = aBlock->IsPseudoFrame(); + mCurrentLine = nsnull; + mPrevKidFrame = nsnull; - justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) && - (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace); - - availSize.width = aMaxSize.width; - availSize.height = aMaxSize.height; - maxElementSize = aMaxElementSize; + mX = 0; + mY = 0; + mAvailSize = aMaxSize; + mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE); + mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE); + mMaxElementSizePointer = aMaxElementSize; if (nsnull != aMaxElementSize) { aMaxElementSize->width = 0; aMaxElementSize->height = 0; } + mKidXMost = 0; - kidXMost = 0; - x = 0; - y = 0; + mPrevMaxPosBottomMargin = 0; + mPrevMaxNegBottomMargin = 0; - isInline = PR_FALSE; - currentLineNumber = 0; - lineStart = nsnull; - lineLength = 0; - ascents = ascentBuf; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; - lineMaxElementSize.width = 0; - lineMaxElementSize.height = 0; - lastContentIsComplete = PR_TRUE; + mNextListOrdinal = -1; + mFirstChildIsInsideBullet = PR_FALSE; - maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]); - needRelativePos = PR_FALSE; - - prevLineLastFrame = nsnull; - prevLineHeight = 0; - topMargin = 0; - prevMaxPosBottomMargin = 0; - prevMaxNegBottomMargin = 0; - prevLineLastContentIsComplete = PR_TRUE; - - unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE); - unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE); - - reflowStatus = nsIFrame::frNotComplete; + return rv; } -nsBlockReflowState::~nsBlockReflowState() -{ - if (ascents != ascentBuf) { - delete ascents; - } - delete currentBand; -} - -void nsBlockReflowState::AddAscent(nscoord aAscent) -{ - NS_PRECONDITION(lineLength <= maxAscents, "bad line length"); - if (lineLength == maxAscents) { - maxAscents *= 2; - nscoord* newAscents = new nscoord[maxAscents]; - if (nsnull != newAscents) { - nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength); - if (ascents != ascentBuf) { - delete ascents; - } - ascents = newAscents; - } else { - // Yikes! Out of memory! - return; - } - } - ascents[lineLength] = aAscent; -} - -void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame, - nscoord aPrevLineHeight) -{ - firstLine = PR_FALSE; - allowLeadingWhitespace = PR_FALSE; - column = 0; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - lineStart = nsnull; - lineLength = 0; - currentLineNumber++; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - needRelativePos = PR_FALSE; - - prevLineLastFrame = aPrevLineLastFrame; - prevLineHeight = aPrevLineHeight; - prevMaxPosBottomMargin = maxPosBottomMargin; - prevMaxNegBottomMargin = maxNegBottomMargin; - - // Remember previous line's lastContentIsComplete - prevLineLastContentIsComplete = lastContentIsComplete; - lastContentIsComplete = PR_TRUE; - - topMargin = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; -} - -#ifdef NS_DEBUG -void nsBlockReflowState::DumpLine() -{ - nsIFrame* f = lineStart; - PRInt32 ll = lineLength; - while (--ll >= 0) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} - -void nsBlockReflowState::DumpList() -{ - nsIFrame* f = lineStart; - while (nsnull != f) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} -#endif - //---------------------------------------------------------------------- -nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, - nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) +nsresult +nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + PRInt32 aIndexInParent, + nsIFrame* aParent) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); if (nsnull == aInstancePtrResult) { @@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, } nsBlockFrame::nsBlockFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) + PRInt32 aIndexInParent, + nsIFrame* aParent) : nsHTMLContainerFrame(aContent, aIndexInParent, aParent) { } nsBlockFrame::~nsBlockFrame() { - if (nsnull != mLines) { - delete mLines; - } + DestroyLines(); } -nsresult +void nsBlockFrame::DestroyLines() +{ +} + +NS_METHOD nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) { NS_PRECONDITION(0 != aInstancePtr, "null ptr"); if (NULL == aInstancePtr) { return NS_ERROR_NULL_POINTER; } - if (aIID.Equals(kIHTMLFrameTypeIID)) { - *aInstancePtr = (void*) ((nsIHTMLFrameType*) this); + if (aIID.Equals(kBlockFrameCID)) { + *aInstancePtr = (void*) (this); return NS_OK; - } else if (aIID.Equals(kIRunaroundIID)) { + } + else if (aIID.Equals(kIRunaroundIID)) { *aInstancePtr = (void*) ((nsIRunaround*) this); return NS_OK; - } else if (aIID.Equals(kIFloaterContainerIID)) { + } + else if (aIID.Equals(kIFloaterContainerIID)) { *aInstancePtr = (void*) ((nsIFloaterContainer*) this); return NS_OK; } return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr); } -// Computes the top margin to use for this child frames based on its display -// type and the display type of the previous child frame. -// -// Adjacent vertical margins between block-level elements are collapsed. -nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC, - PRBool aIsInline) +NS_METHOD +nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const { - if (aIsInline) { - // Just use whatever the previous bottom margin was - return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin; + aIsSplittable = frSplittableNonRectangular; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, + nsIFrame* aParent, + nsIFrame*& aContinuingFrame) +{ + nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); + PrepareContinuingFrame(aCX, aParent, cf); + aContinuingFrame = cf; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::ListTag(FILE* out) const +{ + if ((nsnull != mGeometricParent) && IsPseudoFrame()) { + fprintf(out, "*block<"); + nsIAtom* atom = mContent->GetTag(); + if (nsnull != atom) { + nsAutoString tmp; + atom->ToString(tmp); + fputs(tmp, out); + } + fprintf(out, ">(%d)@%p", mIndexInParent, this); } else { - // Does the frame have a prev-in-flow? - nsIFrame* kidPrevInFlow; + nsHTMLContainerFrame::ListTag(out); + } + return NS_OK; +} - aKidFrame->GetPrevInFlow(kidPrevInFlow); +NS_METHOD +nsBlockFrame::List(FILE* out, PRInt32 aIndent) const +{ + // Indent + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); - if (nsnull == kidPrevInFlow) { + // Output the tag + ListTag(out); + + // Output the first/last content offset + fprintf(out, "[%d,%d,%c] pif=%p nif=%p", + mFirstContentOffset, mLastContentOffset, + (mLastContentIsComplete ? 'T' : 'F'), + mPrevInFlow, mNextInFlow); + + // Output the rect + out << mRect; + + // Output the children, one line at a time + if (nsnull != mLines) { + fputs("<\n", out); + aIndent++; + + nsLineData* line = mLines; + while (nsnull != line) { + line->List(out, aIndent); + line = line->mNextLine; + } + + aIndent--; + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); + fputs(">\n", out); + } else { + fputs("<>\n", out); + } + + return NS_OK; +} + +NS_METHOD +nsBlockFrame::VerifyTree() const +{ + nsresult rv = nsHTMLContainerFrame::VerifyTree(); + if (NS_OK != rv) { + return rv; + } + rv = VerifyLines(PR_TRUE); + return rv; +} + +nsresult +nsBlockFrame::VerifyLines(PRBool aFinalCheck) const +{ + nsresult rv = NS_OK; + + // Make sure that the list of children agrees with our child count. + // If this is not the case then the child list and the line list are + // not properly arranged. + PRInt32 len = LengthOf(mFirstChild); + NS_ASSERTION(mChildCount == len, "bad child list"); + + // Verify that our lines are correctly setup + PRInt32 offset = mFirstContentOffset; + PRInt32 lineChildCount = 0; + nsLineData* line = mLines; + nsLineData* prevLine = nsnull; + while (nsnull != line) { + if (aFinalCheck) { + NS_ASSERTION(((offset == line->mFirstContentOffset) && + (line->mFirstContentOffset <= line->mLastContentOffset)), + "bad line mFirstContentOffset"); + NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset, + "bad line mLastContentOffset"); + offset = line->mLastContentOffset; + if (line->mLastContentIsComplete) { + offset++; + } + } + lineChildCount += line->mChildCount; + rv = line->Verify(aFinalCheck); + if (NS_OK != rv) { + return rv; + } + prevLine = line; + line = line->mNextLine; + } + if (aFinalCheck && (nsnull != prevLine)) { + NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset, + "bad mLastContentOffset"); + NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete, + "bad mLastContentIsComplete"); + } + NS_ASSERTION(lineChildCount == mChildCount, "bad line counts"); + + return rv; +} + +//---------------------------------------------------------------------- + +// Remove a next-in-flow from from this block's list of lines + +// XXX problems here: +// 1. we always have to start from the first line: slow! +// 2. we can avoid this when the child is not the last child in a line + +void +nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow) +{ + // When a reflow indicates completion it's possible that + // next-in-flows were just removed. We have to remove them from any + // nsLineData's that follow the current line. + nsLineData* line = mLines; + while (nsnull != line) { + if (line->mFirstChild == aNextInFlow) { + // Remove child from line. + if (0 == --line->mChildCount) { + line->mFirstChild = nsnull; + } + else { + // Fixup the line + nsIFrame* nextKid; + aNextInFlow->GetNextSibling(nextKid); + line->mFirstChild = nextKid; + nextKid->GetIndexInParent(line->mFirstContentOffset); + } + break; + } + line = line->mNextLine; + } +} + +nsresult +nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize, + aMaxElementSize); + return NS_OK; +} + +nsresult +nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager, + aMaxSize, aDesiredRect, aMaxElementSize); + return NS_OK; +} + +nsLineData* +nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList) +{ + nsLineData* newLine = new nsLineData(); + if (nsnull != newLine) { + nsIFrame* kid = aOverflowList; + newLine->mFirstChild = kid; + kid->GetIndexInParent(newLine->mFirstContentOffset); + newLine->mLastContentOffset = -1; + newLine->mLastContentIsComplete = PRPackedBool(0x255); + PRInt32 kids = 0; + while (nsnull != kid) { + kids++; + kid->GetNextSibling(kid); + } + newLine->mChildCount = kids; + } + return newLine; +} + +void +nsBlockFrame::DrainOverflowList() +{ + nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow; + if (nsnull != prevBlock) { + nsIFrame* overflowList = prevBlock->mOverflowList; + if (nsnull != overflowList) { + NS_ASSERTION(nsnull == mFirstChild, "bad overflow list"); + NS_ASSERTION(nsnull == mLines, "bad overflow list"); + + // Create a line to hold the entire overflow list + nsLineData* newLine = CreateLineForOverflowList(overflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(overflowList); + prevBlock->mOverflowList = nsnull; + + // The new line is the first line + mLines = newLine; + } + } + + if (nsnull != mOverflowList) { + NS_ASSERTION(nsnull != mFirstChild, + "overflow list but no mapped children"); + + // Create a line to hold the overflow list + nsLineData* newLine = CreateLineForOverflowList(mOverflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(mOverflowList, PR_FALSE); + mOverflowList = nsnull; + + // The new line is appended after our other lines + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + while (nsnull != line) { + prevLine = line; + line = line->mNextLine; + } + if (nsnull == prevLine) { + mLines = newLine; + } + else { + prevLine->mNextLine = newLine; + newLine->mPrevLine = prevLine; + } + } +#ifdef NS_DEBUG + VerifyLines(PR_FALSE); +#endif +} + +// XXX add in code here that notices if margin's were not provided by +// the style system and when that is the case to apply the old layout +// engines margin calculations. + +nsresult +nsBlockFrame::PlaceLine(nsBlockReflowState& aState, + nsLineLayout& aLineLayout, + nsLineData* aLine) +{ + nsresult rv = NS_LINE_LAYOUT_COMPLETE; + + nscoord topMargin = 0; + nscoord bottomMargin = 0; + nscoord maxNegBottomMargin = 0; + nscoord maxPosBottomMargin = 0; + + // See if block margins apply to this line or not + PRBool isBlockLine = PR_FALSE; + if (1 == aLine->mChildCount) { + nsIStyleContextPtr kidSC; + nsIFrame* kid = aLine->mFirstChild; + rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef()); + if (NS_OK != rv) return rv; + + nsStyleDisplay* display = (nsStyleDisplay*) + kidSC->GetData(kStyleDisplaySID); + switch (display->mDisplay) { + case NS_STYLE_DISPLAY_BLOCK: + case NS_STYLE_DISPLAY_LIST_ITEM: + isBlockLine = PR_TRUE; + nsStyleSpacing* spacing = (nsStyleSpacing*) + kidSC->GetData(kStyleSpacingSID); + + // Calculate top margin by collapsing with previous bottom margin + // if any. nscoord maxNegTopMargin = 0; nscoord maxPosTopMargin = 0; - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - if (ss->mMargin.top < 0) { - maxNegTopMargin = -ss->mMargin.top; + if (spacing->mMargin.top < 0) { + maxNegTopMargin = -spacing->mMargin.top; } else { - maxPosTopMargin = ss->mMargin.top; + maxPosTopMargin = spacing->mMargin.top; } - - nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin); - nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin); - return maxPos - maxNeg; - } else { - return 0; + nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin); + nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin); + topMargin = maxPos - maxNeg; + + // Save away bottom information for later promotion into aState + if (spacing->mMargin.bottom < 0) { + maxNegBottomMargin = -spacing->mMargin.bottom; + } else { + maxPosBottomMargin = spacing->mMargin.bottom; + } + break; } } + + // Before we move the line, make sure that it will fit in it's new + // location. It always fits if the height isn't constrained or it's + // the first line. + nscoord totalHeight = topMargin + aLine->mBounds.height; + if (!aState.mUnconstrainedHeight && (aLine != mLines)) { + if (aState.mY + totalHeight > aState.mAvailSize.height) { + // The line will not fit + rv = PushLines(aState, aLine); + goto done; + } + } + if (isBlockLine) { + if (0 != topMargin) { + // We have to move the line now that we know the top margin + // for it. + aLine->MoveLineBy(0, topMargin); + aState.mY += topMargin; + } + } + else { + // Apply previous line's bottom margin before the inline-line. + nscoord bottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (0 != bottomMargin) { + aLine->MoveLineBy(0, bottomMargin); + aState.mY += topMargin; + } + } + + // Consume space and advance running values + aState.mY += aLine->mBounds.height; + aState.mPrevMaxNegBottomMargin = maxNegBottomMargin; + aState.mPrevMaxPosBottomMargin = maxPosBottomMargin; + if (nsnull != aState.mMaxElementSizePointer) { + nsSize* maxSize = aState.mMaxElementSizePointer; + if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) { + maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width; + } + if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) { + maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height; + } + } + { + nscoord xmost = aLine->mBounds.XMost(); + if (xmost > aState.mKidXMost) { + aState.mKidXMost = xmost; + } + } + + // Any below current line floaters to place? + if (aState.mPendingFloaters.Count() > 0) { + PlaceBelowCurrentLineFloaters(aState, aState.mY); + // XXX Factor in the height of the floaters as well when considering + // whether the line fits. + // The default policy is that if there isn't room for the floaters then + // both the line and the floaters are pushed to the next-in-flow... + } + + if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) { + // The current y coordinate is now past our available space + // rectangle. Get a new band of space. + GetAvailableSpace(aState, aState.mY); + } + +done: + return rv; } -void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX, - nsBlockReflowState& aState, - nscoord aY) +// aY has borderpadding.top already factored in +// aResult is relative to left,aY +nsresult +nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY) { - NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters"); + nsresult rv = NS_OK; - // XXX Factor this code with PlaceFloater()... - PRInt32 numFloaters = aState.floaterToDo.Count(); - - for (PRInt32 i = 0; i < numFloaters; i++) { - nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i]; - nsRect region; + nsISpaceManager* sm = aState.mSpaceManager; - // Get the band of available space - // XXX This is inefficient to do this inside the loop... - GetAvailableSpaceBand(aState, aY); + // Fill in band data for the specific Y coordinate + sm->Translate(aState.mBorderPadding.left, 0); + sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand); + sm->Translate(-aState.mBorderPadding.left, 0); - // Get the type of floater - nsIStyleContextPtr styleContext; - - floater->GetStyleContext(aCX, styleContext.AssignRef()); - nsStyleDisplay* sd = (nsStyleDisplay*) - styleContext->GetData(kStyleDisplaySID); - - floater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; + // Compute the bounding rect of the available space, i.e. space + // between any left and right floaters + aState.mCurrentBand.ComputeAvailSpaceRect(); - if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; - } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; - } - - // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, - 0); - mCurrentState->spaceManager->AddRectRegion(region, floater); - - // Set the origin of the floater in world coordinates - nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); - floater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); - } - aState.floaterToDo.Clear(); -} - -/** - * Flush a line out. Return true if the line fits in our available - * height. If the line does not fit then return false. When the line - * fits we advance the y coordinate, reset the x coordinate and - * prepare the nsBlockReflowState for the next line. - */ -PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - NS_PRECONDITION(aState.lineLength > 0, "bad line"); - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nscoord y = aState.y + aState.topMargin; - nscoord lineHeight; - - if (aState.isInline) { - // Vertically align the children on this line, returning the height of - // the line upon completion. - lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this, - aState.styleFont, - y, - aState.lineStart, - aState.lineLength, - aState.ascents, - aState.maxAscent); - - // Any below current line floaters to place? - if (aState.floaterToDo.Count() > 0) { - PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight); - // XXX Factor in the height of the floaters as well when considering - // whether the line fits. - // The default policy is that if there isn't room for the floaters then - // both the line and the floaters are pushed to the next-in-flow... - } - } else { - nsSize size; - - aState.lineStart->GetSize(size); - lineHeight = size.height; - } - - // The first line always fits - if (aState.currentLineNumber > 0) { - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if (y + lineHeight > yb) { - // After vertical alignment of the children and factoring in the - // proper margin, the line doesn't fit. - return PR_FALSE; - } - } - - if (aState.isInline) { - // Check if the right-edge of the line exceeds our running x-most - nscoord xMost = aState.borderPadding.left + aState.lineWidth; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - } - - // Advance the y coordinate to the new position where the next - // line or block element will go. - aState.y = y + lineHeight; - aState.x = 0; - - // Now that the vertical alignment is done we can perform horizontal - // alignment and relative positioning. Skip all of these if we are - // doing an unconstrained (in x) reflow. There's no point in doing - // the work if we *know* we are going to reflowed again. - if (!aState.unconstrainedWidth) { - nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText, - aState.lineStart, aState.lineLength, - aState.lineWidth, - aState.availSize.width); - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - if (!aState.justifying) { - nsCSSLayout::RelativePositionChildren(aCX, this, - aState.lineStart, - aState.lineLength); - } - } - - // Record line length - aState.lineLengths.AppendElement((void*)aState.lineLength); - - // Find the last frame in the line - // XXX keep this as running state in the nsBlockReflowState - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - // Update maxElementSize - if (nsnull != aState.maxElementSize) { - nsSize& lineMax = aState.lineMaxElementSize; - nsSize* maxMax = aState.maxElementSize; - if (lineMax.width > maxMax->width) { - maxMax->width = lineMax.width; - } - if (lineMax.height > maxMax->height) { - maxMax->height = lineMax.height; - } - aState.lineMaxElementSize.width = 0; - aState.lineMaxElementSize.height = 0; - } - - // Advance to the next line - aState.AdvanceToNextLine(lastFrame, lineHeight); - - return PR_TRUE; -} - -/** - * Add an inline child to the current line. Advance various running - * values after placement. - */ -void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsReflowMetrics& aKidSize, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) -{ - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; - } - - // Place and size the child - // XXX add in left margin from kid - nsRect r; - r.y = aState.y; - r.width = aKidSize.width; - r.height = aKidSize.height; - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - // Left to right positioning. - r.x = aState.borderPadding.left + aState.x + ss->mMargin.left; - aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right; - } else { - // Right to left positioning - // XXX what should we do when aState.x goes negative??? - r.x = aState.x - aState.borderPadding.right - ss->mMargin.right - - aKidSize.width; - aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left; - } - aKidFrame->SetRect(r); - aState.AddAscent(aKidSize.ascent); - aState.lineWidth += aKidSize.width; - aState.lineLength++; - - // Update maximums for the line - if (aKidSize.ascent > aState.maxAscent) { - aState.maxAscent = aKidSize.ascent; - } - if (aKidSize.descent > aState.maxDescent) { - aState.maxDescent = aKidSize.descent; - } - // Update running margin maximums - if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) { - // XXX temporary code. Since the molecule for the bullet frame - // is the same as the LI frame, we get bad style information. - // ignore it. - } else { - nscoord margin; #if 0 - // XXX CSS2 spec says that top/bottom margin don't affect line height - // calculation. We're waiting for clarification on this issue... - if ((margin = ss->mMargin.top) < 0) { - margin = -margin; - if (margin > aState.maxNegTopMargin) { - aState.maxNegTopMargin = margin; - } - } else { - if (margin > aState.maxPosTopMargin) { - aState.maxPosTopMargin = margin; - } - } + // XXX For now we assume that there are no height restrictions + // (e.g. no "float to bottom of column/page") + nsRect& availSpace = aState.mCurrentBand.availSpace; + aResult.x = availSpace.x; + aResult.y = availSpace.y; + aResult.width = availSpace.width; + aResult.height = aState.mAvailSize.height; +#else + aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left, + aState.mY); #endif - if ((margin = ss->mMargin.bottom) < 0) { - margin = -margin; - if (margin > aState.maxNegBottomMargin) { - aState.maxNegBottomMargin = margin; - } - } else { - if (margin > aState.maxPosBottomMargin) { - aState.maxPosBottomMargin = margin; - } - } - } - // Update line max element size - nsSize& mes = aState.lineMaxElementSize; - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > mes.width) { - mes.width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > mes.height) { - mes.height = aKidMaxElementSize->height; - } - } + return rv; } -// Places and sizes the block-level element, and advances the line. -// The rect is in the local coordinate space of the kid frame. -void nsBlockFrame::AddBlockChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsRect& aKidRect, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) +// Give aLine and any successive lines to the block's next-in-flow; if +// we don't have a next-in-flow then push all the children onto our +// overflow list. +nsresult +nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine) { - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); + PRInt32 i; - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; + // Split our child-list in two; revert our last content offset and + // completion status to the previous line. + nsLineData* prevLine = aLine->mPrevLine; + NS_PRECONDITION(nsnull != prevLine, "pushing first line"); + nsIFrame* prevKidFrame = prevLine->mFirstChild; + for (i = prevLine->mChildCount - 1; --i >= 0; ) { + prevKidFrame->GetNextSibling(prevKidFrame); } - - // Translate from the kid's coordinate space to our coordinate space - aKidRect.x += aState.borderPadding.left + ss->mMargin.left; - aKidRect.y += aState.y + aState.topMargin; - - // Place and size the child - aKidFrame->SetRect(aKidRect); - - aState.AddAscent(aKidRect.height); - aState.lineLength++; - - // Is this the widest child frame? - nscoord xMost = aKidRect.XMost() + ss->mMargin.right; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - - // Update the max element size - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > aState.maxElementSize->width) { - aState.maxElementSize->width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > aState.maxElementSize->height) { - aState.maxElementSize->height = aKidMaxElementSize->height; - } - } - - // and the bottom line margin information which we'll use when placing - // the next child - if (ss->mMargin.bottom < 0) { - aState.maxNegBottomMargin = -ss->mMargin.bottom; - } else { - aState.maxPosBottomMargin = ss->mMargin.bottom; - } - - // Update the running y-offset - aState.y += aKidRect.height + aState.topMargin; - - // Apply relative positioning if necessary - nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1); - - // Advance to the next line - aState.AdvanceToNextLine(aKidFrame, aKidRect.height); -} - -/** - * Compute the available size for reflowing the given child at the - * current x,y position in the state. Note that this may return - * negative or zero width/height's if we are out of room. - */ -void nsBlockFrame::GetAvailSize(nsSize& aResult, - nsBlockReflowState& aState, - nsIStyleContext* aKidSC, - PRBool aIsInline) -{ - // Determine the maximum available reflow height for the child - nscoord yb = aState.borderPadding.top + aState.availSize.height; - aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE : - yb - aState.y - aState.topMargin; - - // Determine the maximum available reflow width for the child - if (aState.unconstrainedWidth) { - aResult.width = NS_UNCONSTRAINEDSIZE; - } else if (aIsInline) { - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - aResult.width = aState.currentBand->availSpace.XMost() - aState.x; - } else { - aResult.width = aState.x - aState.currentBand->availSpace.x; - } - } else { - // It's a block. Don't adjust for the left/right margin here. That happens - // later on once we know the current left/right edge - aResult.width = aState.availSize.width; - } -} - -/** - * Push all of the kids that we have not reflowed, starting at - * aState.lineStart. aPrevKid is the kid previous to aState.lineStart - * and is also our last child. Note that line length is NOT a - * reflection of the number of children we are actually pushing - * (because we don't break the sibling list as we add children to the - * line). - */ -void nsBlockFrame::PushKids(nsBlockReflowState& aState) -{ - nsIFrame* prevFrame = aState.prevLineLastFrame; - NS_PRECONDITION(nsnull != prevFrame, "pushing all kids"); #ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevFrame->GetNextSibling(nextSibling); - NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line"); + nsIFrame* nextFrame; + prevKidFrame->GetNextSibling(nextFrame); + NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list"); #endif + prevKidFrame->SetNextSibling(nsnull); + prevLine->mNextLine = nsnull; + mLastContentOffset = prevLine->mLastContentOffset; + mLastContentIsComplete = prevLine->mLastContentIsComplete; + + // Push children to our next-in-flow if we have, or to our overflow list + nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; + PRInt32 pushCount = 0; + if (nsnull == nextInFlow) { + // Place children on the overflow list + mOverflowList = aLine->mFirstChild; + + // Destroy the lines + nsLineData* line = aLine; + while (nsnull != line) { + pushCount += line->mChildCount; + nsLineData* next = line->mNextLine; + delete line; + line = next; + } + } + else { + aLine->mPrevLine = nsnull; + + // Pass on the children to our next-in-flow + nsLineData* line = aLine; + prevLine = line; + nsIFrame* lastKid = aLine->mFirstChild; + nsIFrame* kid = lastKid; + while (nsnull != line) { + i = line->mChildCount; + pushCount += i; + NS_ASSERTION(kid == line->mFirstChild, "bad line list"); + while (--i >= 0) { + kid->SetGeometricParent(nextInFlow); + nsIFrame* contentParent; + kid->GetContentParent(contentParent); + if (this == contentParent) { + kid->SetContentParent(nextInFlow); + } + lastKid = kid; + kid->GetNextSibling(kid); + } + prevLine = line; + line = line->mNextLine; + } + + // Join the two line lists + nsLineData* nextInFlowLine = nextInFlow->mLines; + if (nsnull != nextInFlowLine) { + lastKid->SetNextSibling(nextInFlowLine->mFirstChild); + nextInFlowLine->mPrevLine = prevLine; + } + nsIFrame* firstKid = aLine->mFirstChild; + prevLine->mNextLine = nextInFlowLine; + nextInFlow->mLines = aLine; + nextInFlow->mFirstChild = firstKid; + nextInFlow->mChildCount += pushCount; + firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset); #ifdef NS_DEBUG - PRInt32 numKids = LengthOf(mFirstChild); - NS_ASSERTION(numKids == mChildCount, "bad child count"); + nextInFlow->VerifyLines(PR_FALSE); +#endif + } + mChildCount -= pushCount; + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return NS_LINE_LAYOUT_NOT_COMPLETE; +} + +nsresult +nsBlockFrame::ReflowMapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != line) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // If we have no children and we have a prev-in-flow then we need to + // pick up where it left off. If we have children, e.g. we're being + // resized, then our content offset will have already been set + // correctly. + nsIFrame* kidPrevInFlow = nsnull; + if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { + nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; + mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary? + if (PR_FALSE == prev->mLastContentIsComplete) { + // Our prev-in-flow's last child is not complete + prev->LastChild(kidPrevInFlow); + } + } + + // Get to the last line where the new content may be added + nsLineData* line = nsnull; + nsLineData* prevLine = nsnull; + if (nsnull != mLines) { + line = mLines; + while (nsnull != line->mNextLine) { + line = line->mNextLine; + } + prevLine = line; + + // If the last line is not complete then kidPrevInFlow should be + // set to the last-line's last child. + if (!prevLine->mLastContentIsComplete) { + kidPrevInFlow = prevLine->GetLastChild(); + } + } + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + // Now reflow the new content until we are out of new content or out + // of vertical space. + PRInt32 kidIndex = NextChildOffset(); + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + lineLayout.mKidPrevInFlow = kidPrevInFlow; + PRInt32 contentChildCount = mContent->ChildCount(); + while (kidIndex < contentChildCount) { + if (nsnull == line) { + if (!MoreToReflow(aState)) { + break; + } + line = new nsLineData(); + if (nsnull == line) { + rv = NS_ERROR_OUT_OF_MEMORY; + goto done; + } + line->mFirstContentOffset = kidIndex; + } + + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mKidPrevInFlow = kidPrevInFlow; + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Add line to the block; do this before placing the line in case + // PushLines is needed. + if (nsnull == prevLine) { + // For the first line, initialize mFirstContentOffset + mFirstContentOffset = line->mFirstContentOffset; + mFirstChild = line->mFirstChild; + mLines = line; + } + else { + prevLine->mNextLine = line; + line->mPrevLine = prevLine; + } + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + kidIndex = lineLayout.mKidIndex; + kidPrevInFlow = lineLayout.mKidPrevInFlow; + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; + if (aState.mBlockIsPseudo) { + PropagateContentOffsets(); + } + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::InitializeState(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockReflowState& aState) +{ + nsresult rv; + rv = aState.Initialize(aPresContext, aSpaceManager, + aMaxSize, aMaxElementSize, this); + + nsStyleSpacing* mySpacing = (nsStyleSpacing*) + mStyleContext->GetData(kStyleSpacingSID); + + // Apply border and padding adjustments for regular frames only + if (!aState.mBlockIsPseudo) { + aState.mY = mySpacing->mBorderPadding.top; + aState.mX = mySpacing->mBorderPadding.left; + aState.mAvailSize.width -= + (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); + aState.mAvailSize.height -= + (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); + aState.mBorderPadding = mySpacing->mBorderPadding; + } + else { + aState.mBorderPadding.SizeTo(0, 0, 0, 0); + } + + // Setup initial list ordinal value + nsIAtom* tag = mContent->GetTag(); + if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || + (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { + nsHTMLValue value; + if (eContentAttr_HasValue == + ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { + if (eHTMLUnit_Integer == value.GetUnit()) { + aState.mNextListOrdinal = value.GetIntValue(); + } + } + } + NS_RELEASE(tag); + + return rv; +} + +#if XXX +NS_METHOD +nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight) +{ + printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth), + NS_TWIPS_TO_POINTS_FLOAT(aHeight)); + return nsHTMLContainerFrame::SizeTo(aWidth, aHeight); +} + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, desiredRect, aStatus); + aDesiredSize.width = desiredRect.width; + aDesiredSize.height = desiredRect.height; + aDesiredSize.ascent = aDesiredSize.height; + aDesiredSize.descent = 0; + } + return rv; +} #endif -#ifdef NOISY - ListTag(stdout); - printf(": push kids (childCount=%d)\n", mChildCount); - DumpFlow(); -#endif - - PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete); - SetLastContentOffset(prevFrame); - - // Set mLastContentIsComplete to the previous lines last content is - // complete now that the previous line's last child is our last - // child. - mLastContentIsComplete = aState.prevLineLastContentIsComplete; - - // Fix up child count - // XXX is there a better way? aState.lineLength doesn't work because - // we might be pushing more than just the pending line. - nsIFrame* kid = mFirstChild; - PRInt32 kids = 0; - while (nsnull != kid) { - kids++; - kid->GetNextSibling(kid); - } - mChildCount = kids; - - // Make sure we have no lingering line data - aState.lineLength = 0; - aState.lineStart = nsnull; - -#ifdef NOISY - ListTag(stdout); - printf(": push kids done (childCount=%d) [%c]\n", mChildCount, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif -} - -/** - * Gets a band of available space starting at the specified y-offset. Assumes - * the local coordinate space is currently set to the upper-left origin of the - * bounding rect - * - * Updates "currentBand" and "x" member data of the block reflow state - */ -void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY) -{ - // Gets a band of available space. - aState.spaceManager->Translate(aState.borderPadding.left, 0); - aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand); - - // Compute the bounding rect of the available space, i.e. space between any - // left and right floaters - aState.currentBand->ComputeAvailSpaceRect(); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - aState.x = aState.currentBand->availSpace.x; -} - -void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear) -{ - // Translate the coordinate space - aState.spaceManager->Translate(aState.borderPadding.left, 0); - -getBand: - nscoord y = aState.y + aState.topMargin; - PRBool isLeftFloater = PR_FALSE; - PRBool isRightFloater = PR_FALSE; - - // Get a band of available space - aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand); - - // Scan the trapezoids looking for left and right floaters - nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids; - for (PRInt32 i = 0; i < aState.currentBand->count; i++) { - // XXX Handle multiply occupied - if (nsBandTrapezoid::smOccupied == trapezoid->state) { - nsStyleDisplay* display; - - trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display); - if (NS_STYLE_FLOAT_LEFT == display->mFloats) { - isLeftFloater = PR_TRUE; - } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) { - isRightFloater = PR_TRUE; - } - } - - trapezoid++; - } - - if (isLeftFloater) { - if ((aClear == NS_STYLE_CLEAR_LEFT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - if (isRightFloater) { - if ((aClear == NS_STYLE_CLEAR_RIGHT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - - aState.spaceManager->Translate(-aState.borderPadding.left, 0); -} - -// Bit's for PlaceAndReflowChild return value -#define PLACE_FIT 0x1 -#define PLACE_FLOWED 0x2 - -PRIntn -nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC) -{ - nsSize kidMaxElementSize; - nsSize* pKidMaxElementSize = - (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; - - // Get line start setup if we are at the start of a new line - if (nsnull == aState.lineStart) { - NS_ASSERTION(0 == aState.lineLength, "bad line length"); - aState.lineStart = aKidFrame; - } - - // Get kid and its style - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - - // Figure out if kid is a block element or not - PRBool isInline = PR_TRUE; - PRIntn display = styleDisplay->mDisplay; - if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) { - // XXX Special hack for properly reflowing bullets that have the - // inside value for list-style-position. - display = NS_STYLE_DISPLAY_INLINE; - } - if ((NS_STYLE_DISPLAY_BLOCK == display) || - (NS_STYLE_DISPLAY_LIST_ITEM == display)) { - // Block elements always end up on the next line (unless they are - // already at the start of the line). - isInline = PR_FALSE; - if (aState.lineLength > 0) { - aState.breakAfterChild = PR_TRUE; - } - } - - // Handle forced break first - if (aState.breakAfterChild) { - NS_ASSERTION(aState.lineStart != aKidFrame, "bad line"); - - // Get the last child in the current line - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - if (!AdvanceToNextLine(aCX, aState)) { - // The previous line didn't fit. - return 0; - } - aState.lineStart = aKidFrame; - - // Get the style for the last child, and see if it wanted to clear - // floaters. This handles the BR tag, which is the only inline - // element for which clear applies - nsIStyleContextPtr lastChildSC; - - lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef()); - nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*) - lastChildSC->GetData(kStyleDisplaySID); - switch (lastChildDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, lastChildDisplay->mBreakType); - break; - } - } - - // Now that we've handled force breaks (and maybe called AdvanceToNextLine() - // which checks), remember whether it's an inline frame - aState.isInline = isInline; - - // If we're at the beginning of a line then compute the top margin that we - // should use - if (aState.lineStart == aKidFrame) { - // Compute the top margin to use for this line - aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC, - aState.isInline); - - // If it's an inline element then get a band of available space - // - // XXX If we have a current band and there's unused space in that band - // then avoid this call to get a band... - if (aState.isInline) { - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - } - } - - // Compute the available space to reflow the child into and then - // reflow it into that space. - nsSize kidAvailSize; - GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - - ReflowStatus status; - - if (aState.isInline) { - nsReflowMetrics kidSize; - - // Inline elements are never passed the space manager - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - NS_ASSERTION(nsnull != aState.lineStart, "bad line start"); - - if (aKidFrame == aState.lineStart) { - // Width always fits when we are at the logical left margin. - // Just check the height. - // - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall - return PLACE_FLOWED; - } - } else { - // Examine state and if the breakBeforeChild is set and we - // aren't already on the new line, do the forcing now. - // XXX Why aren't we doing this check BEFORE we resize reflow the child? - if (aState.breakBeforeChild) { - aState.breakBeforeChild = PR_FALSE; - if (aKidFrame != aState.lineStart) { - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow child now that it has the line to itself - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - } - } - - // When we are not at the logical left margin then we need - // to check the width first. If we are too wide then advance - // to the next line and try reflowing again. - if (kidSize.width > kidAvailSize.width) { - // Too wide. Try next line - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow splittable children - SplittableType isSplittable; - - aKidFrame->IsSplittable(isSplittable); - if (isSplittable != frNotSplittable) { - // Update size info now that we are on the next line. Then - // reflow the child into the new available space. - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - } - - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall on the next line - return PLACE_FLOWED; - } - // It's ok if it's too wide on the next line. - } - } - } - - // Add child to the line - AddInlineChildToLine(aCX, aState, aKidFrame, kidSize, - pKidMaxElementSize, aKidSC); - } else { - nsRect kidRect; - - // Does the block-level element want to clear any floaters that impact - // it? Note that the clear property only applies to block-level elements - // and the BR tag - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - switch (styleDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, styleDisplay->mBreakType); - GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - break; - } - - // Give the block its own local coordinate space.. Note: ReflowChild() - // will adjust for the child's left/right margin after determining the - // current left/right edge - aState.spaceManager->Translate(aState.borderPadding.left, 0); - // Give the block-level element the opportunity to use the space manager - status = ReflowChild(aKidFrame, aCX, aState.spaceManager, - kidAvailSize, kidRect, pKidMaxElementSize); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - // Block elements always fit horizontally (because they are - // always placed at the logical left margin). Check to see if - // the block fits vertically - if (kidRect.YMost() > kidAvailSize.height) { - // Nope - return PLACE_FLOWED; - } - } - - // Add block child - // XXX We need to set lastContentIsComplete here, because AddBlockChild() - // calls AdvaneceToNextLine(). We need to restructure the flow of control, - // and use a state machine... - aState.lastContentIsComplete = PRBool(status == frComplete); - AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC); - } - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - - aState.lastContentIsComplete = PRBool(status == frComplete); - if (aState.isInline && (frNotComplete == status)) { - // Since the inline child didn't complete its reflow we *know* - // that a continuation of it can't possibly fit on the current - // line. Therefore, set a flag in the state that will cause the - // a line break before the next frame is placed. - aState.breakAfterChild = PR_TRUE; - } - - aState.reflowStatus = status; - return PLACE_FLOWED | PLACE_FIT; -} - -/** - * Reflow the existing frames. - * - * @param aCX presentation context to use - * @param aState in out parameter which tracks the state of - * reflow for the block frame. - * @return true if we successfully reflowed all the mapped children and false - * otherwise, e.g. we pushed children to the next in flow - */ PRBool -nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsIFrame* kidFrame; - - for (kidFrame = mFirstChild; nsnull != kidFrame; ) { - - /* we get the kid's style from kidFrame's cached style context */ - nsIStyleContextPtr kidSC; - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - - // Attempt to place and reflow the child - - // XXX if child is not splittable and it fits just place it where - // it is, otherwise advance to the next line and place it there if - // possible - - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - ReflowStatus status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // The child doesn't fit. Push it and any remaining children. - PushKids(aState); - result = PR_FALSE; - goto push_done; - } - - // Is the child complete? - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (frComplete == status) { - // Yes, the child is complete - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); - } else { - // No the child isn't complete - if (nsnull == kidNextInFlow) { - // The child doesn't have a next-in-flow so create a continuing - // frame. This hooks the child into the flow - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); - - // Add the continuing frame to the sibling list - nsIFrame* nextSib; - - kidFrame->GetNextSibling(nextSib); - continuingFrame->SetNextSibling(nextSib); - kidFrame->SetNextSibling(continuingFrame); - mChildCount++; - } - - // Unlike the inline frame code we can't assume that we used - // up all of our space because the child's reflow status is - // frNotComplete. Instead, the child is probably split and - // we need to reflow the continuations as well. - } - - // Get the next child frame - kidFrame->GetNextSibling(kidFrame); - } - - push_done: -#ifdef NS_DEBUG - nsIFrame* lastChild; - PRInt32 lastIndexInParent; - - LastChild(lastChild); - lastChild->GetIndexInParent(lastIndexInParent); - NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset"); - - PRInt32 len = LengthOf(mFirstChild); - NS_POSTCONDITION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -/* XXX: - * In this method, we seem to be getting the style for the next child, - * and checking that child's style for the child's display type. - * The problem is that it's very inefficient to ResolveStyleFor(kid) here - * and then just throw it away, when we end up doing a ResolveStyleFor(kid) - * again when we actually create a frame for the content. - * It would be nice to cache the resolved style, maybe in the reflow state? - */ -PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) +nsBlockFrame::MoreToReflow(nsBlockReflowState& aState) { PRBool rv = PR_FALSE; - if (IsPseudoFrame()) { + if (aState.mBlockIsPseudo) { // Get the next content object that we would like to reflow PRInt32 kidIndex = NextChildOffset(); nsIContentPtr kid = mContent->ChildAt(kidIndex); if (kid.IsNotNull()) { // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); + nsIStyleContextPtr kidSC = + aState.mPresContext->ResolveStyleContextFor(kid, this); nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*) kidSC->GetData(kStyleDisplaySID); switch (kidStyleDisplay->mDisplay) { @@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) return rv; } -/** - * Create new frames for content we haven't yet mapped - * - * @param aCX presentation context to use - * @return frComplete if all content has been mapped and frNotComplete - * if we should be continued - */ -nsIFrame::ReflowStatus -nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) +nsBlockReflowState* +nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext, + nsIFrame* aFrame) { -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif - nsIFrame* kidPrevInFlow = nsnull; - ReflowStatus result = frNotComplete; - - // If we have no children and we have a prev-in-flow then we need to pick - // up where it left off. If we have children, e.g. we're being resized, then - // our content offset should already be set correctly... - if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { - nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; - NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow"); - mFirstContentOffset = prev->NextChildOffset(); - if (PR_FALSE == prev->mLastContentIsComplete) { - // Our prev-in-flow's last child is not complete - prev->LastChild(kidPrevInFlow); - } - } - - // Place our children, one at a time until we are out of children - PRInt32 kidIndex = NextChildOffset(); - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (kid.IsNull()) { - result = frComplete; - break; - } - - // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - nsStylePosition* kidPosition = (nsStylePosition*) - kidSC->GetData(kStylePositionSID); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); - - // Check whether it wants to floated or absolutely positioned - if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) { - AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (nsnull == kidPrevInFlow) { - // Create initial frame for the child - nsIContentDelegate* kidDel; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // If we're being used as a pseudo frame, i.e. we map the same - // content as our parent then we want to indicate we're complete; - // otherwise we'll be continued and go on mapping children... - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - result = frComplete; - goto done; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aCX); - kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow"); - nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); + nsBlockReflowState* state = nsnull; + if (nsnull != aFrame) { + nsIFrame* parent; + aFrame->GetGeometricParent(parent); + while (nsnull != parent) { + nsBlockFrame* block; + nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block); + if (NS_OK == rv) { + nsIPresShell* shell = aPresContext->GetShell(); + state = (nsBlockReflowState*) shell->GetCachedData(block); + NS_RELEASE(shell); break; } - kidFrame->SetStyleContext(aCX,kidSC); - } else { - // Since kid has a prev-in-flow, use that to create the next - // frame. - kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame); + parent->GetGeometricParent(parent); } - - // Link child frame into the list of children. If the frame ends - // up not fitting and getting pushed, the PushKids code will fixup - // the child count for us. - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - mChildCount++; - - // Reflow child frame as many times as necessary until it is - // complete. - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // We ran out of room. - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - goto push_done; - } - - // Did the child complete? - prevKidFrame = kidFrame; - if (frNotComplete == status) { - // Child didn't complete so create a continuing frame - kidPrevInFlow = kidFrame; - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to the sibling list - nsIFrame* kidNextSibling; - - kidFrame->GetNextSibling(kidNextSibling); - continuingFrame->SetNextSibling(kidNextSibling); - kidFrame->SetNextSibling(continuingFrame); - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "huh?"); -#endif - } while (frNotComplete == status); - - // The child that we just reflowed is complete -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); -#endif - kidIndex++; - kidPrevInFlow = nsnull; } - - done: - // To get here we either completely reflowed all our appended - // children OR we are a pseudo-frame and we ran into a block - // element. In either case our last content MUST be complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - - push_done: -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - return result; + return state; } -/** - * Pullup frames from our next in flow and try to place them. Before - * this is called our previously mapped children, if any have been - * reflowed which means that the block reflow state's x and y - * coordinates and other data are ready to go. - * - * Return true if we pulled everything up. - */ -PRBool -nsBlockFrame::PullUpChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - while (nsnull != nextInFlow) { - // Get first available frame from the next-in-flow - nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame); - if (nsnull == kidFrame) { - // We've pulled up all the children from that next-in-flow, so - // move to the next next-in-flow. - nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; - continue; - } - - // Get style information for the pulled up kid - nsIContentPtr kid; - - kidFrame->GetContent(kid.AssignRef()); - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // Push the kids that didn't fit back down to the next-in-flow - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - result = PR_FALSE; - goto push_done; - } - - if (frNotComplete == status) { - // Child is not complete - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (nsnull == kidNextInFlow) { - // Create a continuing frame for the incomplete child - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to our sibling list. - nsIFrame* nextSibling; - - kidFrame->GetNextSibling(nextSibling); - continuingFrame->SetNextSibling(nextSibling); - kidFrame->SetNextSibling(continuingFrame); - prevKidFrame = kidFrame; - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } else { - // The child has a next-in-flow, but it's not one of ours. - // It *must* be in one of our next-in-flows. Collect it - // then. - NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow"); - break; - } - } - } while (frNotComplete == status); - - prevKidFrame = kidFrame; - } - - if (nsnull != prevKidFrame) { - // The only way we can get here is by pulling up every last child - // in our next-in-flows (and reflowing any continunations they - // have). Therefore we KNOW that our last child is complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - } - - push_done:; - - if (result == PR_FALSE) { - // If our next-in-flow is empty OR our next next-in-flow is empty - // then adjust the offsets of all of the empty next-in-flows. - nextInFlow = (nsBlockFrame*) mNextInFlow; - if ((0 == nextInFlow->mChildCount) || - ((nsnull != nextInFlow->mNextInFlow) && - (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) { - // We didn't pullup everything and we need to fixup one of our - // next-in-flows content offsets. - AdjustOffsetOfEmptyNextInFlows(); - } - } - - -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -void nsBlockFrame::SetupState(nsIPresContext* aCX, - nsBlockReflowState& aState, - const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsISpaceManager* aSpaceManager) -{ - // Setup reflow state - aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager); - - nsStyleSpacing* mySpacing = (nsStyleSpacing*) - mStyleContext->GetData(kStyleSpacingSID); - - // Apply border and padding adjustments for regular frames only - if (PR_FALSE == IsPseudoFrame()) { - aState.borderPadding = mySpacing->mBorderPadding; - aState.y = mySpacing->mBorderPadding.top; - aState.availSize.width -= - (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); - aState.availSize.height -= - (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); - } else { - aState.borderPadding.SizeTo(0, 0, 0, 0); - } - - // Setup initial list ordinal value - nsIAtom* tag = mContent->GetTag(); - if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || - (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { - nsHTMLValue value; - if (eContentAttr_HasValue == - ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { - if (eHTMLUnit_Integer == value.GetUnit()) { - aState.nextListOrdinal = value.GetIntValue(); - } - } - } - NS_RELEASE(tag); - - mCurrentState = &aState; -} - -#include "nsUnitConversion.h"/* XXX */ -NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsSize* aMaxElementSize, - ReflowStatus& aStatus) -{ - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager); - return DoResizeReflow(aCX, state, aDesiredRect, aStatus); -} - -nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsRect& aDesiredRect, - ReflowStatus& aStatus) +nsresult +nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + ReflowStatus& aStatus) { #ifdef NS_DEBUG + VerifyLines(PR_TRUE); PreReflowCheck(); #endif -#ifdef NOISY +#ifdef NOISY_REFLOW ListTag(stdout); - printf(": resize reflow %g,%g\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height)); - DumpFlow(); + printf(": Before:\n"); + List(stdout, 1); #endif - // Zap old line data - if (nsnull != mLines) { - delete mLines; - mLines = nsnull; - } - mNumLines = 0; + nsresult rv = NS_OK; - // Check for an overflow list - MoveOverflowToChildList(); - - // Before we start reflowing, cache a pointer to our state structure - // so that inline frames can find it. - nsIPresShell* shell = aCX->GetShell(); + nsIPresShell* shell = aState.mPresContext->GetShell(); shell->PutCachedData(this, &aState); - // First reflow any existing frames - PRBool reflowMappedOK = PR_TRUE; - aStatus = frComplete; - if (nsnull != mFirstChild) { - reflowMappedOK = ReflowMappedChildren(aCX, aState); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } + // Check for an overflow list + DrainOverflowList(); + + if (nsnull != mLines) { + rv = ReflowMapped(aState); } - if (reflowMappedOK) { - // Any space left? - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if ((nsnull != mFirstChild) && (aState.y >= yb)) { - // No space left. Don't try to pull-up children or reflow - // unmapped. We need to return the correct completion status, - // so see if there is more to reflow. - if (MoreToReflow(aCX)) { - aStatus = frNotComplete; - } - } else if (MoreToReflow(aCX)) { - // Try and pull-up some children from a next-in-flow - if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) { - // If we still have unmapped children then create some new frames - if (MoreToReflow(aCX)) { - aStatus = ReflowAppendedChildren(aCX, aState); - } - } else { - // We were unable to pull-up all the existing frames from the - // next in flow - aStatus = frNotComplete; - } + if (NS_OK == rv) { + if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) { + // We are out of space } - } - - // Deal with last line - make sure it gets vertically and - // horizontally aligned. This also updates the state's y coordinate - // which is good because that's how we size ourselves. - if (0 != aState.lineLength) { - if (!AdvanceToNextLine(aCX, aState)) { - // The last line of output doesn't fit. Push all of the kids to - // the next in flow and change our reflow status to not complete - // so that we are continued. -#ifdef NOISY - ListTag(stdout); - printf(": pushing kids since last line doesn't fit\n"); -#endif - - PushKids(aState); - aStatus = frNotComplete; + if (MoreToReflow(aState)) { + rv = ReflowUnmapped(aState); } } - if (frComplete == aStatus) { - // Don't forget to add in the bottom margin from our last child. - // Only add it in if there is room for it. - nscoord margin = aState.prevMaxPosBottomMargin - - aState.prevMaxNegBottomMargin; - nscoord y = aState.y + margin; - if (y <= aState.borderPadding.top + aState.availSize.height) { - aState.y = y; - } - } - - // Now that reflow has finished, remove the cached pointer - shell->RemoveCachedData(this); - NS_RELEASE(shell); - - // Translate state.lineLengths into an integer array - mNumLines = aState.lineLengths.Count(); - if (mNumLines > 0) { - mLines = new PRInt32[mNumLines]; - for (PRInt32 i = 0; i < mNumLines; i++) { - PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i); - mLines[i] = ll; - } - } - - if (!aState.unconstrainedWidth && aState.justifying) { - // Perform justification now that we know how many lines we have. - JustifyLines(aCX, aState); - } - // Return our desired rect and our status aDesiredRect.x = 0; aDesiredRect.y = 0; - aDesiredRect.width = aState.kidXMost + aState.borderPadding.right; - if (!aState.unconstrainedWidth) { + aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right; + if (!aState.mUnconstrainedWidth) { // Make sure we're at least as wide as the max size we were given - nscoord maxWidth = aState.availSize.width + aState.borderPadding.left + - aState.borderPadding.right; - + nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left + + aState.mBorderPadding.right; if (aDesiredRect.width < maxWidth) { aDesiredRect.width = maxWidth; } } - aState.y += aState.borderPadding.bottom; - aDesiredRect.height = aState.y; - -#ifdef NS_DEBUG - PostReflowCheck(aStatus); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height), - ((status == frNotComplete) ? "NOT " : ""), - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F') - ); - DumpFlow(); -#endif - mCurrentState = nsnull; - return NS_OK; -} - -void nsBlockFrame::JustifyLines(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - // XXX we don't justify the last line; what if we are continued, - // should we do it then? - nsIFrame* kid = mFirstChild; - for (PRInt32 i = 0; i < mNumLines; i++) { - nsIFrame* lineStart = kid; - PRInt32 lineLength = mLines[i]; - if (i < mNumLines - 1) { - if (1 == lineLength) { - // For lines with one element on them we can take a shortcut and - // let them do the justification. Note that we still call - // JustifyReflow even if the available space is zero in case the - // child is a hunk of text that ends in whitespace. The whitespace - // will be distributed elsewhere causing a proper flush right look - // for the last word. - nsRect r; - kid->GetRect(r); - nscoord maxWidth = aState.availSize.width; - nscoord availableSpace = maxWidth - r.width; - nscoord maxAvailSpace = nscoord(maxWidth * 0.1f); - if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) { - kid->JustifyReflow(aCX, availableSpace); - kid->SizeTo(r.width + availableSpace, r.height); - } - kid->GetNextSibling(kid); - } else { - // XXX Get justification of multiple elements working - while (--lineLength >= 0) { - kid->GetNextSibling(kid); - } + aState.mY += aState.mBorderPadding.bottom; + nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) { + // It's possible that we don't have room for the last bottom + // margin (the last bottom margin is the margin following a block + // element that we contain; it isn't applied immediately because + // of the margin collapsing logic). This can happen when we are + // reflowed in a limited amount of space because we don't know in + // advance what the last bottom margin will be. + nscoord maxY = aMaxSize.height; + if (aState.mY + lastBottomMargin > maxY) { + lastBottomMargin = maxY - aState.mY; + if (lastBottomMargin < 0) { + lastBottomMargin = 0; } } - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]); + aState.mY += lastBottomMargin; } -} + aDesiredRect.height = aState.mY; -NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const -{ - aIsSplittable = frSplittableNonRectangular; - return NS_OK; -} - -NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, - nsIFrame* aParent, - nsIFrame*& aContinuingFrame) -{ - nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); - PrepareContinuingFrame(aCX, aParent, cf); - aContinuingFrame = cf; - return NS_OK; -} - -NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsReflowCommand& aReflowCommand, - ReflowStatus& aStatus) -{ + // Set return status aStatus = frComplete; - - if (aReflowCommand.GetTarget() == this) { - // XXX for now, just do a complete reflow mapped (it'll kinda - // work, but it's slow) - - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager); - PRBool reflowMappedOK = ReflowMappedChildren(aCX, state); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } - } else { - // XXX not yet implemented - NS_ABORT(); - // XXX work to compute initial state goes *HERE* - aDesiredRect.x = 0; - aDesiredRect.y = 0; - aDesiredRect.width = 0; - aDesiredRect.height = 0; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; } +#ifdef NS_DEBUG + // Verify that the line layout code pulled everything up when it + // indicates a complete reflow. + if (frComplete == aStatus) { + nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow; + while (nsnull != nextBlock) { + NS_ASSERTION((nsnull == nextBlock->mLines) && + (nextBlock->mChildCount == 0) && + (nsnull == nextBlock->mFirstChild), + "bad completion status"); + nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow; + } - mCurrentState = nsnull; - return NS_OK; +#if XXX + // We better not be in the same parent frame as our prev-in-flow. + // If we are it means that we were continued instead of pulling up + // children. + if (nsnull != mPrevInFlow) { + nsIFrame* ourParent = mGeometricParent; + nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent; + NS_ASSERTION(ourParent != prevParent, "bad continuation"); + } +#endif + } +#endif + + // Now that reflow has finished, remove the cached pointer + shell->RemoveCachedData(this); + NS_RELEASE(shell); + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); + PostReflowCheck(aStatus); +#endif +#ifdef NOISY_REFLOW + ListTag(stdout); + printf(": After:\n"); + List(stdout, 1); +#endif + return rv; } -PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ContentAppended(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentInserted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentReplaced(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentDeleted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext, + nsReflowMetrics& aMetrics) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aSpaceManager, aMaxSize, + aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus); + } + return rv; +} + +NS_METHOD +nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsReflowCommand& aReflowCommand, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +PRBool +nsBlockFrame::AddFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + + if (nsnull != state) { + // Get the frame associated with the space manager, and get its + // nsIAnchoredItems interface + nsIFrame* frame = state->mSpaceManager->GetFrame(); + nsIAnchoredItems* anchoredItems = nsnull; + + frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); + NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); + if (nsnull != anchoredItems) { + anchoredItems->AddAnchoredItem(aFloater, + nsIAnchoredItems::anHTMLFloater, + this); + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + return PR_TRUE; + } + } + + return PR_FALSE; +} + +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + if (nsnull != state) { + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + } +} + +PRBool +nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) { do { nsIFrame* parent; - aFrame->GetGeometricParent(parent); - // See if there are any non-zero sized child frames that precede aFrame - // in the child list + // See if there are any non-zero sized child frames that precede + // aFrame in the child list nsIFrame* child; - parent->FirstChild(child); while ((nsnull != child) && (aFrame != child)) { nsSize size; @@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // We found a non-zero sized child frame that precedes aFrame return PR_FALSE; } - child->GetNextSibling(child); } @@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // Walk up one level and check that its parent is left-most as well aFrame = parent; } while (aFrame != this); - return PR_TRUE; } -PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) -{ - // Get the frame associated with the space manager, and get its nsIAnchoredItems - // interface - nsIFrame* frame = mCurrentState->spaceManager->GetFrame(); - nsIAnchoredItems* anchoredItems = nsnull; - - frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); - NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); - - if (nsnull != anchoredItems) { - anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this); - PlaceFloater(aCX, aFloater, aPlaceholder); - return PR_TRUE; - } - - return PR_FALSE; -} - -// XXX The size of the floater needs to be taken into consideration if we're -// computing a maximum element size -void nsBlockFrame::PlaceFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder, + nsBlockReflowState& aState) { // If the floater is the left-most non-zero size child frame then insert // it before the current line; otherwise add it to the below-current-line // todo list and we'll handle it when we flush out the line if (IsLeftMostChild(aPlaceholder)) { - // Get the type of floater - nsIStyleContextPtr styleContext; + nsISpaceManager* sm = aState.mSpaceManager; - aFloater->GetStyleContext(aCX, styleContext.AssignRef()); + // Get the type of floater + nsIStyleContextPtr styleContext; + aFloater->GetStyleContext(aPresContext, styleContext.AssignRef()); nsStyleDisplay* floaterDisplay = (nsStyleDisplay*) styleContext->GetData(kStyleDisplaySID); - if (!mCurrentState->isInline) { - // Get the current band for this line - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); - } - - // Commit some space in the space manager - nsRect region; - + // Commit some space in the space manager and adjust our current + // band of available space. + nsRect region; aFloater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; - + region.y = aState.mY; if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; + region.x = aState.mX; } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, + "bad float type"); + region.x = aState.mCurrentBand.availSpace.XMost() - region.width; } // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0); - mCurrentState->spaceManager->AddRectRegion(region, aFloater); + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, aFloater); // Set the origin of the floater in world coordinates nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); + sm->GetTranslation(worldX, worldY); aFloater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); + sm->Translate(-aState.mBorderPadding.left, 0); - // Update the band of available space to reflect space taken up by the floater - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); + // Update the band of available space to reflect space taken up by + // the floater + GetAvailableSpace(aState, aState.mY); + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); + } } else { // Add the floater to our to-do list - mCurrentState->floaterToDo.AppendElement(aFloater); + aState.mPendingFloaters.AppendElement(aFloater); } } -NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell, - nsIPresContext* aPresContext, - nsIContent* aContainer) +void +nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState, + nscoord aY) { - // Get the last-in-flow - nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow(); - PRInt32 kidIndex = flow->NextChildOffset(); - PRInt32 startIndex = kidIndex; + NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters"); -#if 0 - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - flow->LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (nsnull == kid) { - break; - } + nsISpaceManager* sm = aState.mSpaceManager; + nsBlockBandData* bd = &aState.mCurrentBand; - // Resolve style for the kid - nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); + // XXX Factor this code with PlaceFloater()... + PRInt32 numFloaters = aState.mPendingFloaters.Count(); + for (PRInt32 i = 0; i < numFloaters; i++) { + nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i]; + nsRect region; - // Is it a floater? - if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); + // Get the band of available space + // XXX This is inefficient to do this inside the loop... + GetAvailableSpace(aState, aY); + + // Get the type of floater + nsIStyleContextPtr styleContext; + floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef()); + nsStyleDisplay* sd = (nsStyleDisplay*) + styleContext->GetData(kStyleDisplaySID); + + floater->GetRect(region); + region.y = bd->availSpace.y; + if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { + region.x = bd->availSpace.x; } else { - // Create initial frame for the child - nsIContentDelegate* kidDel; - nsresult fr; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // Update last content offset now that we are done drawing - // children from our parent. - SetLastContentOffset(prevKidFrame); - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - return NS_OK; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aPresContext); - kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); - break; - } + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); + region.x = bd->availSpace.XMost() - region.width; } - kidFrame->SetStyleContext(aCX,kidSC); - // Link child frame into the list of children - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; + // XXX Don't forget the floater's margins... + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, floater); - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - kidIndex++; - mChildCount++; + // Set the origin of the floater in world coordinates + nscoord worldX, worldY; + sm->GetTranslation(worldX, worldY); + floater->MoveTo(region.x + worldX, region.y + worldY); + sm->Translate(-aState.mBorderPadding.left, 0); } - SetLastContentOffset(prevKidFrame); -#endif - // If this is a pseudo-frame then our parent will generate the - // reflow command. Otherwise, if the container is us then we should - // generate the reflow command because we were directly called. - if (!IsPseudoFrame() && (aContainer == mContent)) { - nsReflowCommand* rc = - new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended, - startIndex); - aShell->AppendReflowCommand(rc); + aState.mPendingFloaters.Clear(); + + // Pass on updated available space to the current line + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); } - return NS_OK; } -PRIntn nsBlockFrame::GetSkipSides() const +//---------------------------------------------------------------------- + +nsLineData* +nsBlockFrame::GetFirstLine() +{ + return mLines; +} + +PRIntn +nsBlockFrame::GetSkipSides() const { PRIntn skip = 0; if (nsnull != mPrevInFlow) { @@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const } return skip; } - -nsHTMLFrameType nsBlockFrame::GetFrameType() const -{ - return eHTMLFrame_Block; -} - -NS_METHOD nsBlockFrame::ListTag(FILE* out) const -{ - if ((nsnull != mGeometricParent) && IsPseudoFrame()) { - fprintf(out, "*block<"); - nsIAtom* atom = mContent->GetTag(); - if (nsnull != atom) { - nsAutoString tmp; - atom->ToString(tmp); - fputs(tmp, out); - } - fprintf(out, ">(%d)@%p", mIndexInParent, this); - } else { - nsHTMLContainerFrame::ListTag(out); - } - return NS_OK; -} - -#ifdef NS_DEBUG -void nsBlockFrame::DumpFlow() const -{ -#ifdef NOISY_FLOW - nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow; - while (flow != 0) { - printf(" %p: [%d,%d,%c]\n", - flow, flow->mFirstContentOffset, flow->mLastContentOffset, - (flow->mLastContentIsComplete ? 'T' : 'F')); - flow = (nsBlockFrame*) flow->mNextInFlow; - } -#endif -} -#endif diff --git a/layout/html/base/src/nsBlockReflowState.h b/layout/html/base/src/nsBlockReflowState.h index 59d7407ec5b1..0228ca80d413 100644 --- a/layout/html/base/src/nsBlockReflowState.h +++ b/layout/html/base/src/nsBlockReflowState.h @@ -16,66 +16,37 @@ * Reserved. */ #include "nsBlockFrame.h" -#include "nsSize.h" -#include "nsIAnchoredItems.h" -#include "nsIContent.h" -#include "nsIContentDelegate.h" -#include "nsISpaceManager.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" -#include "nsIPresContext.h" -#include "nsMargin.h" -#include "nsHTMLIIDs.h" -#include "nsCSSLayout.h" -#include "nsCRT.h" -#include "nsIPresShell.h" -#include "nsReflowCommand.h" -#include "nsPlaceholderFrame.h" -#include "nsHTMLAtoms.h" -#include "nsHTMLValue.h" #include "nsIHTMLContent.h" -#include "nsAbsoluteFrame.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIAnchoredItems.h" +#include "nsPlaceholderFrame.h" #include "nsIPtr.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLIIDs.h" +#include "nsHTMLValue.h" -#ifdef NS_DEBUG -#undef NOISY -#undef NOISY_FLOW -#else -#undef NOISY -#undef NOISY_FLOW -#endif +// XXX what do we do with catastrophic errors (rv < 0)? What is the +// state of the reflow world after such an error? -static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); -static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); -static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +#undef NOISY_REFLOW static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID); -static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID); -static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID); -static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID); static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID); -NS_DEF_PTR(nsIStyleContext); +static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); +static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); +static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); + NS_DEF_PTR(nsIContent); +NS_DEF_PTR(nsIStyleContext); -struct BlockBandData : public nsBandData { - nsBandTrapezoid data[12]; +//---------------------------------------------------------------------- - // Bounding rect of available space between any left and right floaters - nsRect availSpace; - // Constructor - BlockBandData() {size = 12; trapezoids = data;} - - // Computes the bounding rect of the available space, i.e. space between - // any left and right floaters - // - // Uses the current trapezoid data, see nsISpaceManager::GetBandData(). - // Updates member data "availSpace" - void ComputeAvailSpaceRect(); -}; - -void BlockBandData::ComputeAvailSpaceRect() +void nsBlockBandData::ComputeAvailSpaceRect() { nsBandTrapezoid* trapezoid = data; @@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect() } } -// XXX Bugs -// 1. right to left reflow can generate negative x coordinates. - -// XXX Speedup idea (all containers) - -// If I reflow a child and it gives back not-complete status then -// there is no sense in trying to pullup children. For blocks, it's a -// little more complicated unless the child is a block - if the child -// is a block, then we must be out of room hence we should stop. If -// the child is not a block then our line should be flushed (see #2 -// below) if our line is already empty then we must be out of room. - -// For inline frames and column frames, if we reflow a child and get -// back not-complete status then we should bail immediately because we -// are out of room. - -// XXX Speedup ideas: -// 1. change pullup code to use line information from next in flow -// 2. we can advance to next line immediately after reflowing something -// and noticing that it's not complete. -// 3. pass down last child information in aState so that pullup, etc., -// don't need to recompute it - -// XXX TODO: -// 0. Move justification into line flushing code - -// 1. To get ebina margins I need "auto" information from the style -// system margin's. A bottom/top margin of auto will then be computed like -// ebina computes it [however the heck that is]. - -// 2. kicking out floaters and talking with floater container to adjust -// left and right margins +//---------------------------------------------------------------------- nsBlockReflowState::nsBlockReflowState() { } -void nsBlockReflowState::Init(const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsIStyleContext* aBlockSC, - nsISpaceManager* aSpaceManager) +nsBlockReflowState::~nsBlockReflowState() { - firstLine = PR_TRUE; - allowLeadingWhitespace = PR_FALSE; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - firstChildIsInsideBullet = PR_FALSE; - nextListOrdinal = -1; - column = 0; +} - spaceManager = aSpaceManager; - currentBand = new BlockBandData; +nsresult +nsBlockReflowState::Initialize(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockFrame* aBlock) +{ + nsresult rv = NS_OK; - styleContext = aBlockSC; - styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID); - styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID); - styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID); + mPresContext = aPresContext; + mBlock = aBlock; + mSpaceManager = aSpaceManager; + mBlockIsPseudo = aBlock->IsPseudoFrame(); + mCurrentLine = nsnull; + mPrevKidFrame = nsnull; - justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) && - (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace); - - availSize.width = aMaxSize.width; - availSize.height = aMaxSize.height; - maxElementSize = aMaxElementSize; + mX = 0; + mY = 0; + mAvailSize = aMaxSize; + mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE); + mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE); + mMaxElementSizePointer = aMaxElementSize; if (nsnull != aMaxElementSize) { aMaxElementSize->width = 0; aMaxElementSize->height = 0; } + mKidXMost = 0; - kidXMost = 0; - x = 0; - y = 0; + mPrevMaxPosBottomMargin = 0; + mPrevMaxNegBottomMargin = 0; - isInline = PR_FALSE; - currentLineNumber = 0; - lineStart = nsnull; - lineLength = 0; - ascents = ascentBuf; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; - lineMaxElementSize.width = 0; - lineMaxElementSize.height = 0; - lastContentIsComplete = PR_TRUE; + mNextListOrdinal = -1; + mFirstChildIsInsideBullet = PR_FALSE; - maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]); - needRelativePos = PR_FALSE; - - prevLineLastFrame = nsnull; - prevLineHeight = 0; - topMargin = 0; - prevMaxPosBottomMargin = 0; - prevMaxNegBottomMargin = 0; - prevLineLastContentIsComplete = PR_TRUE; - - unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE); - unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE); - - reflowStatus = nsIFrame::frNotComplete; + return rv; } -nsBlockReflowState::~nsBlockReflowState() -{ - if (ascents != ascentBuf) { - delete ascents; - } - delete currentBand; -} - -void nsBlockReflowState::AddAscent(nscoord aAscent) -{ - NS_PRECONDITION(lineLength <= maxAscents, "bad line length"); - if (lineLength == maxAscents) { - maxAscents *= 2; - nscoord* newAscents = new nscoord[maxAscents]; - if (nsnull != newAscents) { - nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength); - if (ascents != ascentBuf) { - delete ascents; - } - ascents = newAscents; - } else { - // Yikes! Out of memory! - return; - } - } - ascents[lineLength] = aAscent; -} - -void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame, - nscoord aPrevLineHeight) -{ - firstLine = PR_FALSE; - allowLeadingWhitespace = PR_FALSE; - column = 0; - breakAfterChild = PR_FALSE; - breakBeforeChild = PR_FALSE; - lineStart = nsnull; - lineLength = 0; - currentLineNumber++; - maxAscent = 0; - maxDescent = 0; - lineWidth = 0; - needRelativePos = PR_FALSE; - - prevLineLastFrame = aPrevLineLastFrame; - prevLineHeight = aPrevLineHeight; - prevMaxPosBottomMargin = maxPosBottomMargin; - prevMaxNegBottomMargin = maxNegBottomMargin; - - // Remember previous line's lastContentIsComplete - prevLineLastContentIsComplete = lastContentIsComplete; - lastContentIsComplete = PR_TRUE; - - topMargin = 0; - maxPosBottomMargin = 0; - maxNegBottomMargin = 0; -} - -#ifdef NS_DEBUG -void nsBlockReflowState::DumpLine() -{ - nsIFrame* f = lineStart; - PRInt32 ll = lineLength; - while (--ll >= 0) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} - -void nsBlockReflowState::DumpList() -{ - nsIFrame* f = lineStart; - while (nsnull != f) { - printf(" "); - ((nsFrame*)f)->ListTag(stdout);/* XXX */ - printf("\n"); - f->GetNextSibling(f); - } -} -#endif - //---------------------------------------------------------------------- -nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, - nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) +nsresult +nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + PRInt32 aIndexInParent, + nsIFrame* aParent) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); if (nsnull == aInstancePtrResult) { @@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult, } nsBlockFrame::nsBlockFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParent) + PRInt32 aIndexInParent, + nsIFrame* aParent) : nsHTMLContainerFrame(aContent, aIndexInParent, aParent) { } nsBlockFrame::~nsBlockFrame() { - if (nsnull != mLines) { - delete mLines; - } + DestroyLines(); } -nsresult +void nsBlockFrame::DestroyLines() +{ +} + +NS_METHOD nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) { NS_PRECONDITION(0 != aInstancePtr, "null ptr"); if (NULL == aInstancePtr) { return NS_ERROR_NULL_POINTER; } - if (aIID.Equals(kIHTMLFrameTypeIID)) { - *aInstancePtr = (void*) ((nsIHTMLFrameType*) this); + if (aIID.Equals(kBlockFrameCID)) { + *aInstancePtr = (void*) (this); return NS_OK; - } else if (aIID.Equals(kIRunaroundIID)) { + } + else if (aIID.Equals(kIRunaroundIID)) { *aInstancePtr = (void*) ((nsIRunaround*) this); return NS_OK; - } else if (aIID.Equals(kIFloaterContainerIID)) { + } + else if (aIID.Equals(kIFloaterContainerIID)) { *aInstancePtr = (void*) ((nsIFloaterContainer*) this); return NS_OK; } return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr); } -// Computes the top margin to use for this child frames based on its display -// type and the display type of the previous child frame. -// -// Adjacent vertical margins between block-level elements are collapsed. -nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC, - PRBool aIsInline) +NS_METHOD +nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const { - if (aIsInline) { - // Just use whatever the previous bottom margin was - return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin; + aIsSplittable = frSplittableNonRectangular; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, + nsIFrame* aParent, + nsIFrame*& aContinuingFrame) +{ + nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); + PrepareContinuingFrame(aCX, aParent, cf); + aContinuingFrame = cf; + return NS_OK; +} + +NS_METHOD +nsBlockFrame::ListTag(FILE* out) const +{ + if ((nsnull != mGeometricParent) && IsPseudoFrame()) { + fprintf(out, "*block<"); + nsIAtom* atom = mContent->GetTag(); + if (nsnull != atom) { + nsAutoString tmp; + atom->ToString(tmp); + fputs(tmp, out); + } + fprintf(out, ">(%d)@%p", mIndexInParent, this); } else { - // Does the frame have a prev-in-flow? - nsIFrame* kidPrevInFlow; + nsHTMLContainerFrame::ListTag(out); + } + return NS_OK; +} - aKidFrame->GetPrevInFlow(kidPrevInFlow); +NS_METHOD +nsBlockFrame::List(FILE* out, PRInt32 aIndent) const +{ + // Indent + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); - if (nsnull == kidPrevInFlow) { + // Output the tag + ListTag(out); + + // Output the first/last content offset + fprintf(out, "[%d,%d,%c] pif=%p nif=%p", + mFirstContentOffset, mLastContentOffset, + (mLastContentIsComplete ? 'T' : 'F'), + mPrevInFlow, mNextInFlow); + + // Output the rect + out << mRect; + + // Output the children, one line at a time + if (nsnull != mLines) { + fputs("<\n", out); + aIndent++; + + nsLineData* line = mLines; + while (nsnull != line) { + line->List(out, aIndent); + line = line->mNextLine; + } + + aIndent--; + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); + fputs(">\n", out); + } else { + fputs("<>\n", out); + } + + return NS_OK; +} + +NS_METHOD +nsBlockFrame::VerifyTree() const +{ + nsresult rv = nsHTMLContainerFrame::VerifyTree(); + if (NS_OK != rv) { + return rv; + } + rv = VerifyLines(PR_TRUE); + return rv; +} + +nsresult +nsBlockFrame::VerifyLines(PRBool aFinalCheck) const +{ + nsresult rv = NS_OK; + + // Make sure that the list of children agrees with our child count. + // If this is not the case then the child list and the line list are + // not properly arranged. + PRInt32 len = LengthOf(mFirstChild); + NS_ASSERTION(mChildCount == len, "bad child list"); + + // Verify that our lines are correctly setup + PRInt32 offset = mFirstContentOffset; + PRInt32 lineChildCount = 0; + nsLineData* line = mLines; + nsLineData* prevLine = nsnull; + while (nsnull != line) { + if (aFinalCheck) { + NS_ASSERTION(((offset == line->mFirstContentOffset) && + (line->mFirstContentOffset <= line->mLastContentOffset)), + "bad line mFirstContentOffset"); + NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset, + "bad line mLastContentOffset"); + offset = line->mLastContentOffset; + if (line->mLastContentIsComplete) { + offset++; + } + } + lineChildCount += line->mChildCount; + rv = line->Verify(aFinalCheck); + if (NS_OK != rv) { + return rv; + } + prevLine = line; + line = line->mNextLine; + } + if (aFinalCheck && (nsnull != prevLine)) { + NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset, + "bad mLastContentOffset"); + NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete, + "bad mLastContentIsComplete"); + } + NS_ASSERTION(lineChildCount == mChildCount, "bad line counts"); + + return rv; +} + +//---------------------------------------------------------------------- + +// Remove a next-in-flow from from this block's list of lines + +// XXX problems here: +// 1. we always have to start from the first line: slow! +// 2. we can avoid this when the child is not the last child in a line + +void +nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow) +{ + // When a reflow indicates completion it's possible that + // next-in-flows were just removed. We have to remove them from any + // nsLineData's that follow the current line. + nsLineData* line = mLines; + while (nsnull != line) { + if (line->mFirstChild == aNextInFlow) { + // Remove child from line. + if (0 == --line->mChildCount) { + line->mFirstChild = nsnull; + } + else { + // Fixup the line + nsIFrame* nextKid; + aNextInFlow->GetNextSibling(nextKid); + line->mFirstChild = nextKid; + nextKid->GetIndexInParent(line->mFirstContentOffset); + } + break; + } + line = line->mNextLine; + } +} + +nsresult +nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize, + aMaxElementSize); + return NS_OK; +} + +nsresult +nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager, + aMaxSize, aDesiredRect, aMaxElementSize); + return NS_OK; +} + +nsLineData* +nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList) +{ + nsLineData* newLine = new nsLineData(); + if (nsnull != newLine) { + nsIFrame* kid = aOverflowList; + newLine->mFirstChild = kid; + kid->GetIndexInParent(newLine->mFirstContentOffset); + newLine->mLastContentOffset = -1; + newLine->mLastContentIsComplete = PRPackedBool(0x255); + PRInt32 kids = 0; + while (nsnull != kid) { + kids++; + kid->GetNextSibling(kid); + } + newLine->mChildCount = kids; + } + return newLine; +} + +void +nsBlockFrame::DrainOverflowList() +{ + nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow; + if (nsnull != prevBlock) { + nsIFrame* overflowList = prevBlock->mOverflowList; + if (nsnull != overflowList) { + NS_ASSERTION(nsnull == mFirstChild, "bad overflow list"); + NS_ASSERTION(nsnull == mLines, "bad overflow list"); + + // Create a line to hold the entire overflow list + nsLineData* newLine = CreateLineForOverflowList(overflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(overflowList); + prevBlock->mOverflowList = nsnull; + + // The new line is the first line + mLines = newLine; + } + } + + if (nsnull != mOverflowList) { + NS_ASSERTION(nsnull != mFirstChild, + "overflow list but no mapped children"); + + // Create a line to hold the overflow list + nsLineData* newLine = CreateLineForOverflowList(mOverflowList); + + // Place the children on our child list; this also reassigns + // their geometric parent and updates our mChildCount. + AppendChildren(mOverflowList, PR_FALSE); + mOverflowList = nsnull; + + // The new line is appended after our other lines + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + while (nsnull != line) { + prevLine = line; + line = line->mNextLine; + } + if (nsnull == prevLine) { + mLines = newLine; + } + else { + prevLine->mNextLine = newLine; + newLine->mPrevLine = prevLine; + } + } +#ifdef NS_DEBUG + VerifyLines(PR_FALSE); +#endif +} + +// XXX add in code here that notices if margin's were not provided by +// the style system and when that is the case to apply the old layout +// engines margin calculations. + +nsresult +nsBlockFrame::PlaceLine(nsBlockReflowState& aState, + nsLineLayout& aLineLayout, + nsLineData* aLine) +{ + nsresult rv = NS_LINE_LAYOUT_COMPLETE; + + nscoord topMargin = 0; + nscoord bottomMargin = 0; + nscoord maxNegBottomMargin = 0; + nscoord maxPosBottomMargin = 0; + + // See if block margins apply to this line or not + PRBool isBlockLine = PR_FALSE; + if (1 == aLine->mChildCount) { + nsIStyleContextPtr kidSC; + nsIFrame* kid = aLine->mFirstChild; + rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef()); + if (NS_OK != rv) return rv; + + nsStyleDisplay* display = (nsStyleDisplay*) + kidSC->GetData(kStyleDisplaySID); + switch (display->mDisplay) { + case NS_STYLE_DISPLAY_BLOCK: + case NS_STYLE_DISPLAY_LIST_ITEM: + isBlockLine = PR_TRUE; + nsStyleSpacing* spacing = (nsStyleSpacing*) + kidSC->GetData(kStyleSpacingSID); + + // Calculate top margin by collapsing with previous bottom margin + // if any. nscoord maxNegTopMargin = 0; nscoord maxPosTopMargin = 0; - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - if (ss->mMargin.top < 0) { - maxNegTopMargin = -ss->mMargin.top; + if (spacing->mMargin.top < 0) { + maxNegTopMargin = -spacing->mMargin.top; } else { - maxPosTopMargin = ss->mMargin.top; + maxPosTopMargin = spacing->mMargin.top; } - - nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin); - nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin); - return maxPos - maxNeg; - } else { - return 0; + nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin); + nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin); + topMargin = maxPos - maxNeg; + + // Save away bottom information for later promotion into aState + if (spacing->mMargin.bottom < 0) { + maxNegBottomMargin = -spacing->mMargin.bottom; + } else { + maxPosBottomMargin = spacing->mMargin.bottom; + } + break; } } + + // Before we move the line, make sure that it will fit in it's new + // location. It always fits if the height isn't constrained or it's + // the first line. + nscoord totalHeight = topMargin + aLine->mBounds.height; + if (!aState.mUnconstrainedHeight && (aLine != mLines)) { + if (aState.mY + totalHeight > aState.mAvailSize.height) { + // The line will not fit + rv = PushLines(aState, aLine); + goto done; + } + } + if (isBlockLine) { + if (0 != topMargin) { + // We have to move the line now that we know the top margin + // for it. + aLine->MoveLineBy(0, topMargin); + aState.mY += topMargin; + } + } + else { + // Apply previous line's bottom margin before the inline-line. + nscoord bottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (0 != bottomMargin) { + aLine->MoveLineBy(0, bottomMargin); + aState.mY += topMargin; + } + } + + // Consume space and advance running values + aState.mY += aLine->mBounds.height; + aState.mPrevMaxNegBottomMargin = maxNegBottomMargin; + aState.mPrevMaxPosBottomMargin = maxPosBottomMargin; + if (nsnull != aState.mMaxElementSizePointer) { + nsSize* maxSize = aState.mMaxElementSizePointer; + if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) { + maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width; + } + if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) { + maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height; + } + } + { + nscoord xmost = aLine->mBounds.XMost(); + if (xmost > aState.mKidXMost) { + aState.mKidXMost = xmost; + } + } + + // Any below current line floaters to place? + if (aState.mPendingFloaters.Count() > 0) { + PlaceBelowCurrentLineFloaters(aState, aState.mY); + // XXX Factor in the height of the floaters as well when considering + // whether the line fits. + // The default policy is that if there isn't room for the floaters then + // both the line and the floaters are pushed to the next-in-flow... + } + + if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) { + // The current y coordinate is now past our available space + // rectangle. Get a new band of space. + GetAvailableSpace(aState, aState.mY); + } + +done: + return rv; } -void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX, - nsBlockReflowState& aState, - nscoord aY) +// aY has borderpadding.top already factored in +// aResult is relative to left,aY +nsresult +nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY) { - NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters"); + nsresult rv = NS_OK; - // XXX Factor this code with PlaceFloater()... - PRInt32 numFloaters = aState.floaterToDo.Count(); - - for (PRInt32 i = 0; i < numFloaters; i++) { - nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i]; - nsRect region; + nsISpaceManager* sm = aState.mSpaceManager; - // Get the band of available space - // XXX This is inefficient to do this inside the loop... - GetAvailableSpaceBand(aState, aY); + // Fill in band data for the specific Y coordinate + sm->Translate(aState.mBorderPadding.left, 0); + sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand); + sm->Translate(-aState.mBorderPadding.left, 0); - // Get the type of floater - nsIStyleContextPtr styleContext; - - floater->GetStyleContext(aCX, styleContext.AssignRef()); - nsStyleDisplay* sd = (nsStyleDisplay*) - styleContext->GetData(kStyleDisplaySID); - - floater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; + // Compute the bounding rect of the available space, i.e. space + // between any left and right floaters + aState.mCurrentBand.ComputeAvailSpaceRect(); - if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; - } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; - } - - // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, - 0); - mCurrentState->spaceManager->AddRectRegion(region, floater); - - // Set the origin of the floater in world coordinates - nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); - floater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); - } - aState.floaterToDo.Clear(); -} - -/** - * Flush a line out. Return true if the line fits in our available - * height. If the line does not fit then return false. When the line - * fits we advance the y coordinate, reset the x coordinate and - * prepare the nsBlockReflowState for the next line. - */ -PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - NS_PRECONDITION(aState.lineLength > 0, "bad line"); - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nscoord y = aState.y + aState.topMargin; - nscoord lineHeight; - - if (aState.isInline) { - // Vertically align the children on this line, returning the height of - // the line upon completion. - lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this, - aState.styleFont, - y, - aState.lineStart, - aState.lineLength, - aState.ascents, - aState.maxAscent); - - // Any below current line floaters to place? - if (aState.floaterToDo.Count() > 0) { - PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight); - // XXX Factor in the height of the floaters as well when considering - // whether the line fits. - // The default policy is that if there isn't room for the floaters then - // both the line and the floaters are pushed to the next-in-flow... - } - } else { - nsSize size; - - aState.lineStart->GetSize(size); - lineHeight = size.height; - } - - // The first line always fits - if (aState.currentLineNumber > 0) { - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if (y + lineHeight > yb) { - // After vertical alignment of the children and factoring in the - // proper margin, the line doesn't fit. - return PR_FALSE; - } - } - - if (aState.isInline) { - // Check if the right-edge of the line exceeds our running x-most - nscoord xMost = aState.borderPadding.left + aState.lineWidth; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - } - - // Advance the y coordinate to the new position where the next - // line or block element will go. - aState.y = y + lineHeight; - aState.x = 0; - - // Now that the vertical alignment is done we can perform horizontal - // alignment and relative positioning. Skip all of these if we are - // doing an unconstrained (in x) reflow. There's no point in doing - // the work if we *know* we are going to reflowed again. - if (!aState.unconstrainedWidth) { - nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText, - aState.lineStart, aState.lineLength, - aState.lineWidth, - aState.availSize.width); - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - if (!aState.justifying) { - nsCSSLayout::RelativePositionChildren(aCX, this, - aState.lineStart, - aState.lineLength); - } - } - - // Record line length - aState.lineLengths.AppendElement((void*)aState.lineLength); - - // Find the last frame in the line - // XXX keep this as running state in the nsBlockReflowState - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - // Update maxElementSize - if (nsnull != aState.maxElementSize) { - nsSize& lineMax = aState.lineMaxElementSize; - nsSize* maxMax = aState.maxElementSize; - if (lineMax.width > maxMax->width) { - maxMax->width = lineMax.width; - } - if (lineMax.height > maxMax->height) { - maxMax->height = lineMax.height; - } - aState.lineMaxElementSize.width = 0; - aState.lineMaxElementSize.height = 0; - } - - // Advance to the next line - aState.AdvanceToNextLine(lastFrame, lineHeight); - - return PR_TRUE; -} - -/** - * Add an inline child to the current line. Advance various running - * values after placement. - */ -void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsReflowMetrics& aKidSize, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) -{ - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); - - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; - } - - // Place and size the child - // XXX add in left margin from kid - nsRect r; - r.y = aState.y; - r.width = aKidSize.width; - r.height = aKidSize.height; - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - // Left to right positioning. - r.x = aState.borderPadding.left + aState.x + ss->mMargin.left; - aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right; - } else { - // Right to left positioning - // XXX what should we do when aState.x goes negative??? - r.x = aState.x - aState.borderPadding.right - ss->mMargin.right - - aKidSize.width; - aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left; - } - aKidFrame->SetRect(r); - aState.AddAscent(aKidSize.ascent); - aState.lineWidth += aKidSize.width; - aState.lineLength++; - - // Update maximums for the line - if (aKidSize.ascent > aState.maxAscent) { - aState.maxAscent = aKidSize.ascent; - } - if (aKidSize.descent > aState.maxDescent) { - aState.maxDescent = aKidSize.descent; - } - // Update running margin maximums - if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) { - // XXX temporary code. Since the molecule for the bullet frame - // is the same as the LI frame, we get bad style information. - // ignore it. - } else { - nscoord margin; #if 0 - // XXX CSS2 spec says that top/bottom margin don't affect line height - // calculation. We're waiting for clarification on this issue... - if ((margin = ss->mMargin.top) < 0) { - margin = -margin; - if (margin > aState.maxNegTopMargin) { - aState.maxNegTopMargin = margin; - } - } else { - if (margin > aState.maxPosTopMargin) { - aState.maxPosTopMargin = margin; - } - } + // XXX For now we assume that there are no height restrictions + // (e.g. no "float to bottom of column/page") + nsRect& availSpace = aState.mCurrentBand.availSpace; + aResult.x = availSpace.x; + aResult.y = availSpace.y; + aResult.width = availSpace.width; + aResult.height = aState.mAvailSize.height; +#else + aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left, + aState.mY); #endif - if ((margin = ss->mMargin.bottom) < 0) { - margin = -margin; - if (margin > aState.maxNegBottomMargin) { - aState.maxNegBottomMargin = margin; - } - } else { - if (margin > aState.maxPosBottomMargin) { - aState.maxPosBottomMargin = margin; - } - } - } - // Update line max element size - nsSize& mes = aState.lineMaxElementSize; - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > mes.width) { - mes.width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > mes.height) { - mes.height = aKidMaxElementSize->height; - } - } + return rv; } -// Places and sizes the block-level element, and advances the line. -// The rect is in the local coordinate space of the kid frame. -void nsBlockFrame::AddBlockChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsRect& aKidRect, - nsSize* aKidMaxElementSize, - nsIStyleContext* aKidSC) +// Give aLine and any successive lines to the block's next-in-flow; if +// we don't have a next-in-flow then push all the children onto our +// overflow list. +nsresult +nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine) { - NS_PRECONDITION(nsnull != aState.lineStart, "bad line"); + PRInt32 i; - nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID); - nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID); - nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID); - - if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) { - aState.needRelativePos = PR_TRUE; + // Split our child-list in two; revert our last content offset and + // completion status to the previous line. + nsLineData* prevLine = aLine->mPrevLine; + NS_PRECONDITION(nsnull != prevLine, "pushing first line"); + nsIFrame* prevKidFrame = prevLine->mFirstChild; + for (i = prevLine->mChildCount - 1; --i >= 0; ) { + prevKidFrame->GetNextSibling(prevKidFrame); } - - // Translate from the kid's coordinate space to our coordinate space - aKidRect.x += aState.borderPadding.left + ss->mMargin.left; - aKidRect.y += aState.y + aState.topMargin; - - // Place and size the child - aKidFrame->SetRect(aKidRect); - - aState.AddAscent(aKidRect.height); - aState.lineLength++; - - // Is this the widest child frame? - nscoord xMost = aKidRect.XMost() + ss->mMargin.right; - if (xMost > aState.kidXMost) { - aState.kidXMost = xMost; - } - - // Update the max element size - if (nsnull != aKidMaxElementSize) { - if (aKidMaxElementSize->width > aState.maxElementSize->width) { - aState.maxElementSize->width = aKidMaxElementSize->width; - } - if (aKidMaxElementSize->height > aState.maxElementSize->height) { - aState.maxElementSize->height = aKidMaxElementSize->height; - } - } - - // and the bottom line margin information which we'll use when placing - // the next child - if (ss->mMargin.bottom < 0) { - aState.maxNegBottomMargin = -ss->mMargin.bottom; - } else { - aState.maxPosBottomMargin = ss->mMargin.bottom; - } - - // Update the running y-offset - aState.y += aKidRect.height + aState.topMargin; - - // Apply relative positioning if necessary - nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1); - - // Advance to the next line - aState.AdvanceToNextLine(aKidFrame, aKidRect.height); -} - -/** - * Compute the available size for reflowing the given child at the - * current x,y position in the state. Note that this may return - * negative or zero width/height's if we are out of room. - */ -void nsBlockFrame::GetAvailSize(nsSize& aResult, - nsBlockReflowState& aState, - nsIStyleContext* aKidSC, - PRBool aIsInline) -{ - // Determine the maximum available reflow height for the child - nscoord yb = aState.borderPadding.top + aState.availSize.height; - aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE : - yb - aState.y - aState.topMargin; - - // Determine the maximum available reflow width for the child - if (aState.unconstrainedWidth) { - aResult.width = NS_UNCONSTRAINEDSIZE; - } else if (aIsInline) { - if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) { - aResult.width = aState.currentBand->availSpace.XMost() - aState.x; - } else { - aResult.width = aState.x - aState.currentBand->availSpace.x; - } - } else { - // It's a block. Don't adjust for the left/right margin here. That happens - // later on once we know the current left/right edge - aResult.width = aState.availSize.width; - } -} - -/** - * Push all of the kids that we have not reflowed, starting at - * aState.lineStart. aPrevKid is the kid previous to aState.lineStart - * and is also our last child. Note that line length is NOT a - * reflection of the number of children we are actually pushing - * (because we don't break the sibling list as we add children to the - * line). - */ -void nsBlockFrame::PushKids(nsBlockReflowState& aState) -{ - nsIFrame* prevFrame = aState.prevLineLastFrame; - NS_PRECONDITION(nsnull != prevFrame, "pushing all kids"); #ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevFrame->GetNextSibling(nextSibling); - NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line"); + nsIFrame* nextFrame; + prevKidFrame->GetNextSibling(nextFrame); + NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list"); #endif + prevKidFrame->SetNextSibling(nsnull); + prevLine->mNextLine = nsnull; + mLastContentOffset = prevLine->mLastContentOffset; + mLastContentIsComplete = prevLine->mLastContentIsComplete; + + // Push children to our next-in-flow if we have, or to our overflow list + nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; + PRInt32 pushCount = 0; + if (nsnull == nextInFlow) { + // Place children on the overflow list + mOverflowList = aLine->mFirstChild; + + // Destroy the lines + nsLineData* line = aLine; + while (nsnull != line) { + pushCount += line->mChildCount; + nsLineData* next = line->mNextLine; + delete line; + line = next; + } + } + else { + aLine->mPrevLine = nsnull; + + // Pass on the children to our next-in-flow + nsLineData* line = aLine; + prevLine = line; + nsIFrame* lastKid = aLine->mFirstChild; + nsIFrame* kid = lastKid; + while (nsnull != line) { + i = line->mChildCount; + pushCount += i; + NS_ASSERTION(kid == line->mFirstChild, "bad line list"); + while (--i >= 0) { + kid->SetGeometricParent(nextInFlow); + nsIFrame* contentParent; + kid->GetContentParent(contentParent); + if (this == contentParent) { + kid->SetContentParent(nextInFlow); + } + lastKid = kid; + kid->GetNextSibling(kid); + } + prevLine = line; + line = line->mNextLine; + } + + // Join the two line lists + nsLineData* nextInFlowLine = nextInFlow->mLines; + if (nsnull != nextInFlowLine) { + lastKid->SetNextSibling(nextInFlowLine->mFirstChild); + nextInFlowLine->mPrevLine = prevLine; + } + nsIFrame* firstKid = aLine->mFirstChild; + prevLine->mNextLine = nextInFlowLine; + nextInFlow->mLines = aLine; + nextInFlow->mFirstChild = firstKid; + nextInFlow->mChildCount += pushCount; + firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset); #ifdef NS_DEBUG - PRInt32 numKids = LengthOf(mFirstChild); - NS_ASSERTION(numKids == mChildCount, "bad child count"); + nextInFlow->VerifyLines(PR_FALSE); +#endif + } + mChildCount -= pushCount; + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return NS_LINE_LAYOUT_NOT_COMPLETE; +} + +nsresult +nsBlockFrame::ReflowMapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + nsLineData* prevLine = nsnull; + nsLineData* line = mLines; + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != line) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) +{ + nsresult rv = NS_OK; + + // If we have no children and we have a prev-in-flow then we need to + // pick up where it left off. If we have children, e.g. we're being + // resized, then our content offset will have already been set + // correctly. + nsIFrame* kidPrevInFlow = nsnull; + if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { + nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; + mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary? + if (PR_FALSE == prev->mLastContentIsComplete) { + // Our prev-in-flow's last child is not complete + prev->LastChild(kidPrevInFlow); + } + } + + // Get to the last line where the new content may be added + nsLineData* line = nsnull; + nsLineData* prevLine = nsnull; + if (nsnull != mLines) { + line = mLines; + while (nsnull != line->mNextLine) { + line = line->mNextLine; + } + prevLine = line; + + // If the last line is not complete then kidPrevInFlow should be + // set to the last-line's last child. + if (!prevLine->mLastContentIsComplete) { + kidPrevInFlow = prevLine->GetLastChild(); + } + } + + // Get some space to start reflowing with + GetAvailableSpace(aState, aState.mY); + + // Now reflow the new content until we are out of new content or out + // of vertical space. + PRInt32 kidIndex = NextChildOffset(); + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + lineLayout.mKidPrevInFlow = kidPrevInFlow; + PRInt32 contentChildCount = mContent->ChildCount(); + while (kidIndex < contentChildCount) { + if (nsnull == line) { + if (!MoreToReflow(aState)) { + break; + } + line = new nsLineData(); + if (nsnull == line) { + rv = NS_ERROR_OUT_OF_MEMORY; + goto done; + } + line->mFirstContentOffset = kidIndex; + } + + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, line); + if (NS_OK != rv) { + goto done; + } + lineLayout.mKidPrevInFlow = kidPrevInFlow; + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Add line to the block; do this before placing the line in case + // PushLines is needed. + if (nsnull == prevLine) { + // For the first line, initialize mFirstContentOffset + mFirstContentOffset = line->mFirstContentOffset; + mFirstChild = line->mFirstChild; + mLines = line; + } + else { + prevLine->mNextLine = line; + line->mPrevLine = prevLine; + } + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, line); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + kidIndex = lineLayout.mKidIndex; + kidPrevInFlow = lineLayout.mKidPrevInFlow; + + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + prevLine = line; + line = line->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; + if (aState.mBlockIsPseudo) { + PropagateContentOffsets(); + } + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + +nsresult +nsBlockFrame::InitializeState(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + nsBlockReflowState& aState) +{ + nsresult rv; + rv = aState.Initialize(aPresContext, aSpaceManager, + aMaxSize, aMaxElementSize, this); + + nsStyleSpacing* mySpacing = (nsStyleSpacing*) + mStyleContext->GetData(kStyleSpacingSID); + + // Apply border and padding adjustments for regular frames only + if (!aState.mBlockIsPseudo) { + aState.mY = mySpacing->mBorderPadding.top; + aState.mX = mySpacing->mBorderPadding.left; + aState.mAvailSize.width -= + (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); + aState.mAvailSize.height -= + (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); + aState.mBorderPadding = mySpacing->mBorderPadding; + } + else { + aState.mBorderPadding.SizeTo(0, 0, 0, 0); + } + + // Setup initial list ordinal value + nsIAtom* tag = mContent->GetTag(); + if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || + (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { + nsHTMLValue value; + if (eContentAttr_HasValue == + ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { + if (eHTMLUnit_Integer == value.GetUnit()) { + aState.mNextListOrdinal = value.GetIntValue(); + } + } + } + NS_RELEASE(tag); + + return rv; +} + +#if XXX +NS_METHOD +nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight) +{ + printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth), + NS_TWIPS_TO_POINTS_FLOAT(aHeight)); + return nsHTMLContainerFrame::SizeTo(aWidth, aHeight); +} + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsSize& aMaxSize, + nsSize* aMaxElementSize, + ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, desiredRect, aStatus); + aDesiredSize.width = desiredRect.width; + aDesiredSize.height = desiredRect.height; + aDesiredSize.ascent = aDesiredSize.height; + aDesiredSize.descent = 0; + } + return rv; +} #endif -#ifdef NOISY - ListTag(stdout); - printf(": push kids (childCount=%d)\n", mChildCount); - DumpFlow(); -#endif - - PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete); - SetLastContentOffset(prevFrame); - - // Set mLastContentIsComplete to the previous lines last content is - // complete now that the previous line's last child is our last - // child. - mLastContentIsComplete = aState.prevLineLastContentIsComplete; - - // Fix up child count - // XXX is there a better way? aState.lineLength doesn't work because - // we might be pushing more than just the pending line. - nsIFrame* kid = mFirstChild; - PRInt32 kids = 0; - while (nsnull != kid) { - kids++; - kid->GetNextSibling(kid); - } - mChildCount = kids; - - // Make sure we have no lingering line data - aState.lineLength = 0; - aState.lineStart = nsnull; - -#ifdef NOISY - ListTag(stdout); - printf(": push kids done (childCount=%d) [%c]\n", mChildCount, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif -} - -/** - * Gets a band of available space starting at the specified y-offset. Assumes - * the local coordinate space is currently set to the upper-left origin of the - * bounding rect - * - * Updates "currentBand" and "x" member data of the block reflow state - */ -void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY) -{ - // Gets a band of available space. - aState.spaceManager->Translate(aState.borderPadding.left, 0); - aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand); - - // Compute the bounding rect of the available space, i.e. space between any - // left and right floaters - aState.currentBand->ComputeAvailSpaceRect(); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - aState.x = aState.currentBand->availSpace.x; -} - -void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear) -{ - // Translate the coordinate space - aState.spaceManager->Translate(aState.borderPadding.left, 0); - -getBand: - nscoord y = aState.y + aState.topMargin; - PRBool isLeftFloater = PR_FALSE; - PRBool isRightFloater = PR_FALSE; - - // Get a band of available space - aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand); - - // Scan the trapezoids looking for left and right floaters - nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids; - for (PRInt32 i = 0; i < aState.currentBand->count; i++) { - // XXX Handle multiply occupied - if (nsBandTrapezoid::smOccupied == trapezoid->state) { - nsStyleDisplay* display; - - trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display); - if (NS_STYLE_FLOAT_LEFT == display->mFloats) { - isLeftFloater = PR_TRUE; - } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) { - isRightFloater = PR_TRUE; - } - } - - trapezoid++; - } - - if (isLeftFloater) { - if ((aClear == NS_STYLE_CLEAR_LEFT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - if (isRightFloater) { - if ((aClear == NS_STYLE_CLEAR_RIGHT) || - (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) { - aState.y += aState.currentBand->trapezoids[0].GetHeight(); - goto getBand; - } - } - - aState.spaceManager->Translate(-aState.borderPadding.left, 0); -} - -// Bit's for PlaceAndReflowChild return value -#define PLACE_FIT 0x1 -#define PLACE_FLOWED 0x2 - -PRIntn -nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsIFrame* aKidFrame, - nsIStyleContext* aKidSC) -{ - nsSize kidMaxElementSize; - nsSize* pKidMaxElementSize = - (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; - - // Get line start setup if we are at the start of a new line - if (nsnull == aState.lineStart) { - NS_ASSERTION(0 == aState.lineLength, "bad line length"); - aState.lineStart = aKidFrame; - } - - // Get kid and its style - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - - // Figure out if kid is a block element or not - PRBool isInline = PR_TRUE; - PRIntn display = styleDisplay->mDisplay; - if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) { - // XXX Special hack for properly reflowing bullets that have the - // inside value for list-style-position. - display = NS_STYLE_DISPLAY_INLINE; - } - if ((NS_STYLE_DISPLAY_BLOCK == display) || - (NS_STYLE_DISPLAY_LIST_ITEM == display)) { - // Block elements always end up on the next line (unless they are - // already at the start of the line). - isInline = PR_FALSE; - if (aState.lineLength > 0) { - aState.breakAfterChild = PR_TRUE; - } - } - - // Handle forced break first - if (aState.breakAfterChild) { - NS_ASSERTION(aState.lineStart != aKidFrame, "bad line"); - - // Get the last child in the current line - nsIFrame* lastFrame = aState.lineStart; - PRInt32 lineLen = aState.lineLength - 1; - while (--lineLen >= 0) { - lastFrame->GetNextSibling(lastFrame); - } - - if (!AdvanceToNextLine(aCX, aState)) { - // The previous line didn't fit. - return 0; - } - aState.lineStart = aKidFrame; - - // Get the style for the last child, and see if it wanted to clear - // floaters. This handles the BR tag, which is the only inline - // element for which clear applies - nsIStyleContextPtr lastChildSC; - - lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef()); - nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*) - lastChildSC->GetData(kStyleDisplaySID); - switch (lastChildDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, lastChildDisplay->mBreakType); - break; - } - } - - // Now that we've handled force breaks (and maybe called AdvanceToNextLine() - // which checks), remember whether it's an inline frame - aState.isInline = isInline; - - // If we're at the beginning of a line then compute the top margin that we - // should use - if (aState.lineStart == aKidFrame) { - // Compute the top margin to use for this line - aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC, - aState.isInline); - - // If it's an inline element then get a band of available space - // - // XXX If we have a current band and there's unused space in that band - // then avoid this call to get a band... - if (aState.isInline) { - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - } - } - - // Compute the available space to reflow the child into and then - // reflow it into that space. - nsSize kidAvailSize; - GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - - ReflowStatus status; - - if (aState.isInline) { - nsReflowMetrics kidSize; - - // Inline elements are never passed the space manager - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - NS_ASSERTION(nsnull != aState.lineStart, "bad line start"); - - if (aKidFrame == aState.lineStart) { - // Width always fits when we are at the logical left margin. - // Just check the height. - // - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall - return PLACE_FLOWED; - } - } else { - // Examine state and if the breakBeforeChild is set and we - // aren't already on the new line, do the forcing now. - // XXX Why aren't we doing this check BEFORE we resize reflow the child? - if (aState.breakBeforeChild) { - aState.breakBeforeChild = PR_FALSE; - if (aKidFrame != aState.lineStart) { - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow child now that it has the line to itself - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - } - } - - // When we are not at the logical left margin then we need - // to check the width first. If we are too wide then advance - // to the next line and try reflowing again. - if (kidSize.width > kidAvailSize.width) { - // Too wide. Try next line - if (!AdvanceToNextLine(aCX, aState)) { - // Flushing out the line failed. - return PLACE_FLOWED; - } - aState.lineStart = aKidFrame; - - // Get a band of available space - GetAvailableSpaceBand(aState, aState.y + aState.topMargin); - - // Reflow splittable children - SplittableType isSplittable; - - aKidFrame->IsSplittable(isSplittable); - if (isSplittable != frNotSplittable) { - // Update size info now that we are on the next line. Then - // reflow the child into the new available space. - GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE); - status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize, - pKidMaxElementSize); - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - } - - // XXX This height check isn't correct now that we have bands of - // available space... - if (kidSize.height > kidAvailSize.height) { - // It's too tall on the next line - return PLACE_FLOWED; - } - // It's ok if it's too wide on the next line. - } - } - } - - // Add child to the line - AddInlineChildToLine(aCX, aState, aKidFrame, kidSize, - pKidMaxElementSize, aKidSC); - } else { - nsRect kidRect; - - // Does the block-level element want to clear any floaters that impact - // it? Note that the clear property only applies to block-level elements - // and the BR tag - nsStyleDisplay* styleDisplay = (nsStyleDisplay*) - aKidSC->GetData(kStyleDisplaySID); - switch (styleDisplay->mBreakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(aState, styleDisplay->mBreakType); - GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE); - if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) { - // No more room - return 0; - } - break; - } - - // Give the block its own local coordinate space.. Note: ReflowChild() - // will adjust for the child's left/right margin after determining the - // current left/right edge - aState.spaceManager->Translate(aState.borderPadding.left, 0); - // Give the block-level element the opportunity to use the space manager - status = ReflowChild(aKidFrame, aCX, aState.spaceManager, - kidAvailSize, kidRect, pKidMaxElementSize); - aState.spaceManager->Translate(-aState.borderPadding.left, 0); - - // For first children, we skip all the fit checks because we must - // fit at least one child for a parent to figure what to do with us. - if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) { - // Block elements always fit horizontally (because they are - // always placed at the logical left margin). Check to see if - // the block fits vertically - if (kidRect.YMost() > kidAvailSize.height) { - // Nope - return PLACE_FLOWED; - } - } - - // Add block child - // XXX We need to set lastContentIsComplete here, because AddBlockChild() - // calls AdvaneceToNextLine(). We need to restructure the flow of control, - // and use a state machine... - aState.lastContentIsComplete = PRBool(status == frComplete); - AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC); - } - - // If we just reflowed our last child then update the - // mLastContentIsComplete state. - nsIFrame* nextSibling; - - aKidFrame->GetNextSibling(nextSibling); - if (nsnull == nextSibling) { - // Use state from the reflow we just did - mLastContentIsComplete = PRBool(status == frComplete); - } - - aState.lastContentIsComplete = PRBool(status == frComplete); - if (aState.isInline && (frNotComplete == status)) { - // Since the inline child didn't complete its reflow we *know* - // that a continuation of it can't possibly fit on the current - // line. Therefore, set a flag in the state that will cause the - // a line break before the next frame is placed. - aState.breakAfterChild = PR_TRUE; - } - - aState.reflowStatus = status; - return PLACE_FLOWED | PLACE_FIT; -} - -/** - * Reflow the existing frames. - * - * @param aCX presentation context to use - * @param aState in out parameter which tracks the state of - * reflow for the block frame. - * @return true if we successfully reflowed all the mapped children and false - * otherwise, e.g. we pushed children to the next in flow - */ PRBool -nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsIFrame* kidFrame; - - for (kidFrame = mFirstChild; nsnull != kidFrame; ) { - - /* we get the kid's style from kidFrame's cached style context */ - nsIStyleContextPtr kidSC; - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - - // Attempt to place and reflow the child - - // XXX if child is not splittable and it fits just place it where - // it is, otherwise advance to the next line and place it there if - // possible - - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - ReflowStatus status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // The child doesn't fit. Push it and any remaining children. - PushKids(aState); - result = PR_FALSE; - goto push_done; - } - - // Is the child complete? - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (frComplete == status) { - // Yes, the child is complete - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); - } else { - // No the child isn't complete - if (nsnull == kidNextInFlow) { - // The child doesn't have a next-in-flow so create a continuing - // frame. This hooks the child into the flow - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); - - // Add the continuing frame to the sibling list - nsIFrame* nextSib; - - kidFrame->GetNextSibling(nextSib); - continuingFrame->SetNextSibling(nextSib); - kidFrame->SetNextSibling(continuingFrame); - mChildCount++; - } - - // Unlike the inline frame code we can't assume that we used - // up all of our space because the child's reflow status is - // frNotComplete. Instead, the child is probably split and - // we need to reflow the continuations as well. - } - - // Get the next child frame - kidFrame->GetNextSibling(kidFrame); - } - - push_done: -#ifdef NS_DEBUG - nsIFrame* lastChild; - PRInt32 lastIndexInParent; - - LastChild(lastChild); - lastChild->GetIndexInParent(lastIndexInParent); - NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset"); - - PRInt32 len = LengthOf(mFirstChild); - NS_POSTCONDITION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - -#ifdef NOISY - ListTag(stdout); - printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -/* XXX: - * In this method, we seem to be getting the style for the next child, - * and checking that child's style for the child's display type. - * The problem is that it's very inefficient to ResolveStyleFor(kid) here - * and then just throw it away, when we end up doing a ResolveStyleFor(kid) - * again when we actually create a frame for the content. - * It would be nice to cache the resolved style, maybe in the reflow state? - */ -PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) +nsBlockFrame::MoreToReflow(nsBlockReflowState& aState) { PRBool rv = PR_FALSE; - if (IsPseudoFrame()) { + if (aState.mBlockIsPseudo) { // Get the next content object that we would like to reflow PRInt32 kidIndex = NextChildOffset(); nsIContentPtr kid = mContent->ChildAt(kidIndex); if (kid.IsNotNull()) { // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); + nsIStyleContextPtr kidSC = + aState.mPresContext->ResolveStyleContextFor(kid, this); nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*) kidSC->GetData(kStyleDisplaySID); switch (kidStyleDisplay->mDisplay) { @@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX) return rv; } -/** - * Create new frames for content we haven't yet mapped - * - * @param aCX presentation context to use - * @return frComplete if all content has been mapped and frNotComplete - * if we should be continued - */ -nsIFrame::ReflowStatus -nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) +nsBlockReflowState* +nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext, + nsIFrame* aFrame) { -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif - nsIFrame* kidPrevInFlow = nsnull; - ReflowStatus result = frNotComplete; - - // If we have no children and we have a prev-in-flow then we need to pick - // up where it left off. If we have children, e.g. we're being resized, then - // our content offset should already be set correctly... - if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { - nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow; - NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow"); - mFirstContentOffset = prev->NextChildOffset(); - if (PR_FALSE == prev->mLastContentIsComplete) { - // Our prev-in-flow's last child is not complete - prev->LastChild(kidPrevInFlow); - } - } - - // Place our children, one at a time until we are out of children - PRInt32 kidIndex = NextChildOffset(); - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (kid.IsNull()) { - result = frComplete; - break; - } - - // Resolve style for the kid - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - nsStylePosition* kidPosition = (nsStylePosition*) - kidSC->GetData(kStylePositionSID); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); - - // Check whether it wants to floated or absolutely positioned - if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) { - AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); - kidFrame->SetStyleContext(aCX,kidSC); - } else if (nsnull == kidPrevInFlow) { - // Create initial frame for the child - nsIContentDelegate* kidDel; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // If we're being used as a pseudo frame, i.e. we map the same - // content as our parent then we want to indicate we're complete; - // otherwise we'll be continued and go on mapping children... - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - result = frComplete; - goto done; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aCX); - kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow"); - nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); + nsBlockReflowState* state = nsnull; + if (nsnull != aFrame) { + nsIFrame* parent; + aFrame->GetGeometricParent(parent); + while (nsnull != parent) { + nsBlockFrame* block; + nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block); + if (NS_OK == rv) { + nsIPresShell* shell = aPresContext->GetShell(); + state = (nsBlockReflowState*) shell->GetCachedData(block); + NS_RELEASE(shell); break; } - kidFrame->SetStyleContext(aCX,kidSC); - } else { - // Since kid has a prev-in-flow, use that to create the next - // frame. - kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame); + parent->GetGeometricParent(parent); } - - // Link child frame into the list of children. If the frame ends - // up not fitting and getting pushed, the PushKids code will fixup - // the child count for us. - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; - - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - mChildCount++; - - // Reflow child frame as many times as necessary until it is - // complete. - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // We ran out of room. - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - goto push_done; - } - - // Did the child complete? - prevKidFrame = kidFrame; - if (frNotComplete == status) { - // Child didn't complete so create a continuing frame - kidPrevInFlow = kidFrame; - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to the sibling list - nsIFrame* kidNextSibling; - - kidFrame->GetNextSibling(kidNextSibling); - continuingFrame->SetNextSibling(kidNextSibling); - kidFrame->SetNextSibling(continuingFrame); - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "huh?"); -#endif - } while (frNotComplete == status); - - // The child that we just reflowed is complete -#ifdef NS_DEBUG - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); -#endif - kidIndex++; - kidPrevInFlow = nsnull; } - - done: - // To get here we either completely reflowed all our appended - // children OR we are a pseudo-frame and we ran into a block - // element. In either case our last content MUST be complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - - push_done: -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif - return result; + return state; } -/** - * Pullup frames from our next in flow and try to place them. Before - * this is called our previously mapped children, if any have been - * reflowed which means that the block reflow state's x and y - * coordinates and other data are ready to go. - * - * Return true if we pulled everything up. - */ -PRBool -nsBlockFrame::PullUpChildren(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ -#ifdef NS_DEBUG - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup (childCount=%d) [%d,%d,%c]\n", - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - - PRBool result = PR_TRUE; - nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - while (nsnull != nextInFlow) { - // Get first available frame from the next-in-flow - nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame); - if (nsnull == kidFrame) { - // We've pulled up all the children from that next-in-flow, so - // move to the next next-in-flow. - nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; - continue; - } - - // Get style information for the pulled up kid - nsIContentPtr kid; - - kidFrame->GetContent(kid.AssignRef()); - nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this); - - ReflowStatus status; - do { - PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC); - status = aState.reflowStatus; - if (0 == (placeStatus & PLACE_FIT)) { - // Push the kids that didn't fit back down to the next-in-flow - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - mLastContentIsComplete = PRBool(nsnull == kidNextInFlow); - PushKids(aState); - - result = PR_FALSE; - goto push_done; - } - - if (frNotComplete == status) { - // Child is not complete - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (nsnull == kidNextInFlow) { - // Create a continuing frame for the incomplete child - nsIFrame* continuingFrame; - - kidFrame->CreateContinuingFrame(aCX, this, continuingFrame); - - // Add the continuing frame to our sibling list. - nsIFrame* nextSibling; - - kidFrame->GetNextSibling(nextSibling); - continuingFrame->SetNextSibling(nextSibling); - kidFrame->SetNextSibling(continuingFrame); - prevKidFrame = kidFrame; - kidFrame = continuingFrame; - mChildCount++; - - // Switch to new kid style - kidFrame->GetStyleContext(aCX, kidSC.AssignRef()); - } else { - // The child has a next-in-flow, but it's not one of ours. - // It *must* be in one of our next-in-flows. Collect it - // then. - NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow"); - break; - } - } - } while (frNotComplete == status); - - prevKidFrame = kidFrame; - } - - if (nsnull != prevKidFrame) { - // The only way we can get here is by pulling up every last child - // in our next-in-flows (and reflowing any continunations they - // have). Therefore we KNOW that our last child is complete. - NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state"); - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - } - - push_done:; - - if (result == PR_FALSE) { - // If our next-in-flow is empty OR our next next-in-flow is empty - // then adjust the offsets of all of the empty next-in-flows. - nextInFlow = (nsBlockFrame*) mNextInFlow; - if ((0 == nextInFlow->mChildCount) || - ((nsnull != nextInFlow->mNextInFlow) && - (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) { - // We didn't pullup everything and we need to fixup one of our - // next-in-flows content offsets. - AdjustOffsetOfEmptyNextInFlows(); - } - } - - -#ifdef NS_DEBUG - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n", - (result ? "" : "NOT "), - mChildCount, - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F')); - DumpFlow(); -#endif - return result; -} - -void nsBlockFrame::SetupState(nsIPresContext* aCX, - nsBlockReflowState& aState, - const nsSize& aMaxSize, - nsSize* aMaxElementSize, - nsISpaceManager* aSpaceManager) -{ - // Setup reflow state - aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager); - - nsStyleSpacing* mySpacing = (nsStyleSpacing*) - mStyleContext->GetData(kStyleSpacingSID); - - // Apply border and padding adjustments for regular frames only - if (PR_FALSE == IsPseudoFrame()) { - aState.borderPadding = mySpacing->mBorderPadding; - aState.y = mySpacing->mBorderPadding.top; - aState.availSize.width -= - (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right); - aState.availSize.height -= - (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom); - } else { - aState.borderPadding.SizeTo(0, 0, 0, 0); - } - - // Setup initial list ordinal value - nsIAtom* tag = mContent->GetTag(); - if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || - (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { - nsHTMLValue value; - if (eContentAttr_HasValue == - ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) { - if (eHTMLUnit_Integer == value.GetUnit()) { - aState.nextListOrdinal = value.GetIntValue(); - } - } - } - NS_RELEASE(tag); - - mCurrentState = &aState; -} - -#include "nsUnitConversion.h"/* XXX */ -NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsSize* aMaxElementSize, - ReflowStatus& aStatus) -{ - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager); - return DoResizeReflow(aCX, state, aDesiredRect, aStatus); -} - -nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX, - nsBlockReflowState& aState, - nsRect& aDesiredRect, - ReflowStatus& aStatus) +nsresult +nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + ReflowStatus& aStatus) { #ifdef NS_DEBUG + VerifyLines(PR_TRUE); PreReflowCheck(); #endif -#ifdef NOISY +#ifdef NOISY_REFLOW ListTag(stdout); - printf(": resize reflow %g,%g\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height)); - DumpFlow(); + printf(": Before:\n"); + List(stdout, 1); #endif - // Zap old line data - if (nsnull != mLines) { - delete mLines; - mLines = nsnull; - } - mNumLines = 0; + nsresult rv = NS_OK; - // Check for an overflow list - MoveOverflowToChildList(); - - // Before we start reflowing, cache a pointer to our state structure - // so that inline frames can find it. - nsIPresShell* shell = aCX->GetShell(); + nsIPresShell* shell = aState.mPresContext->GetShell(); shell->PutCachedData(this, &aState); - // First reflow any existing frames - PRBool reflowMappedOK = PR_TRUE; - aStatus = frComplete; - if (nsnull != mFirstChild) { - reflowMappedOK = ReflowMappedChildren(aCX, aState); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } + // Check for an overflow list + DrainOverflowList(); + + if (nsnull != mLines) { + rv = ReflowMapped(aState); } - if (reflowMappedOK) { - // Any space left? - nscoord yb = aState.borderPadding.top + aState.availSize.height; - if ((nsnull != mFirstChild) && (aState.y >= yb)) { - // No space left. Don't try to pull-up children or reflow - // unmapped. We need to return the correct completion status, - // so see if there is more to reflow. - if (MoreToReflow(aCX)) { - aStatus = frNotComplete; - } - } else if (MoreToReflow(aCX)) { - // Try and pull-up some children from a next-in-flow - if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) { - // If we still have unmapped children then create some new frames - if (MoreToReflow(aCX)) { - aStatus = ReflowAppendedChildren(aCX, aState); - } - } else { - // We were unable to pull-up all the existing frames from the - // next in flow - aStatus = frNotComplete; - } + if (NS_OK == rv) { + if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) { + // We are out of space } - } - - // Deal with last line - make sure it gets vertically and - // horizontally aligned. This also updates the state's y coordinate - // which is good because that's how we size ourselves. - if (0 != aState.lineLength) { - if (!AdvanceToNextLine(aCX, aState)) { - // The last line of output doesn't fit. Push all of the kids to - // the next in flow and change our reflow status to not complete - // so that we are continued. -#ifdef NOISY - ListTag(stdout); - printf(": pushing kids since last line doesn't fit\n"); -#endif - - PushKids(aState); - aStatus = frNotComplete; + if (MoreToReflow(aState)) { + rv = ReflowUnmapped(aState); } } - if (frComplete == aStatus) { - // Don't forget to add in the bottom margin from our last child. - // Only add it in if there is room for it. - nscoord margin = aState.prevMaxPosBottomMargin - - aState.prevMaxNegBottomMargin; - nscoord y = aState.y + margin; - if (y <= aState.borderPadding.top + aState.availSize.height) { - aState.y = y; - } - } - - // Now that reflow has finished, remove the cached pointer - shell->RemoveCachedData(this); - NS_RELEASE(shell); - - // Translate state.lineLengths into an integer array - mNumLines = aState.lineLengths.Count(); - if (mNumLines > 0) { - mLines = new PRInt32[mNumLines]; - for (PRInt32 i = 0; i < mNumLines; i++) { - PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i); - mLines[i] = ll; - } - } - - if (!aState.unconstrainedWidth && aState.justifying) { - // Perform justification now that we know how many lines we have. - JustifyLines(aCX, aState); - } - // Return our desired rect and our status aDesiredRect.x = 0; aDesiredRect.y = 0; - aDesiredRect.width = aState.kidXMost + aState.borderPadding.right; - if (!aState.unconstrainedWidth) { + aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right; + if (!aState.mUnconstrainedWidth) { // Make sure we're at least as wide as the max size we were given - nscoord maxWidth = aState.availSize.width + aState.borderPadding.left + - aState.borderPadding.right; - + nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left + + aState.mBorderPadding.right; if (aDesiredRect.width < maxWidth) { aDesiredRect.width = maxWidth; } } - aState.y += aState.borderPadding.bottom; - aDesiredRect.height = aState.y; - -#ifdef NS_DEBUG - PostReflowCheck(aStatus); -#endif -#ifdef NOISY - ListTag(stdout); - printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n", - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width), - NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height), - ((status == frNotComplete) ? "NOT " : ""), - mFirstContentOffset, mLastContentOffset, - (mLastContentIsComplete ? 'T' : 'F') - ); - DumpFlow(); -#endif - mCurrentState = nsnull; - return NS_OK; -} - -void nsBlockFrame::JustifyLines(nsIPresContext* aCX, - nsBlockReflowState& aState) -{ - // XXX we don't justify the last line; what if we are continued, - // should we do it then? - nsIFrame* kid = mFirstChild; - for (PRInt32 i = 0; i < mNumLines; i++) { - nsIFrame* lineStart = kid; - PRInt32 lineLength = mLines[i]; - if (i < mNumLines - 1) { - if (1 == lineLength) { - // For lines with one element on them we can take a shortcut and - // let them do the justification. Note that we still call - // JustifyReflow even if the available space is zero in case the - // child is a hunk of text that ends in whitespace. The whitespace - // will be distributed elsewhere causing a proper flush right look - // for the last word. - nsRect r; - kid->GetRect(r); - nscoord maxWidth = aState.availSize.width; - nscoord availableSpace = maxWidth - r.width; - nscoord maxAvailSpace = nscoord(maxWidth * 0.1f); - if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) { - kid->JustifyReflow(aCX, availableSpace); - kid->SizeTo(r.width + availableSpace, r.height); - } - kid->GetNextSibling(kid); - } else { - // XXX Get justification of multiple elements working - while (--lineLength >= 0) { - kid->GetNextSibling(kid); - } + aState.mY += aState.mBorderPadding.bottom; + nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin - + aState.mPrevMaxNegBottomMargin; + if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) { + // It's possible that we don't have room for the last bottom + // margin (the last bottom margin is the margin following a block + // element that we contain; it isn't applied immediately because + // of the margin collapsing logic). This can happen when we are + // reflowed in a limited amount of space because we don't know in + // advance what the last bottom margin will be. + nscoord maxY = aMaxSize.height; + if (aState.mY + lastBottomMargin > maxY) { + lastBottomMargin = maxY - aState.mY; + if (lastBottomMargin < 0) { + lastBottomMargin = 0; } } - - // Finally, now that the in-flow positions of the line's frames are - // known we can apply relative positioning if any of them need it. - nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]); + aState.mY += lastBottomMargin; } -} + aDesiredRect.height = aState.mY; -NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const -{ - aIsSplittable = frSplittableNonRectangular; - return NS_OK; -} - -NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX, - nsIFrame* aParent, - nsIFrame*& aContinuingFrame) -{ - nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent); - PrepareContinuingFrame(aCX, aParent, cf); - aContinuingFrame = cf; - return NS_OK; -} - -NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsReflowCommand& aReflowCommand, - ReflowStatus& aStatus) -{ + // Set return status aStatus = frComplete; - - if (aReflowCommand.GetTarget() == this) { - // XXX for now, just do a complete reflow mapped (it'll kinda - // work, but it's slow) - - nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager); - PRBool reflowMappedOK = ReflowMappedChildren(aCX, state); - if (!reflowMappedOK) { - aStatus = frNotComplete; - } - } else { - // XXX not yet implemented - NS_ABORT(); - // XXX work to compute initial state goes *HERE* - aDesiredRect.x = 0; - aDesiredRect.y = 0; - aDesiredRect.width = 0; - aDesiredRect.height = 0; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; } +#ifdef NS_DEBUG + // Verify that the line layout code pulled everything up when it + // indicates a complete reflow. + if (frComplete == aStatus) { + nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow; + while (nsnull != nextBlock) { + NS_ASSERTION((nsnull == nextBlock->mLines) && + (nextBlock->mChildCount == 0) && + (nsnull == nextBlock->mFirstChild), + "bad completion status"); + nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow; + } - mCurrentState = nsnull; - return NS_OK; +#if XXX + // We better not be in the same parent frame as our prev-in-flow. + // If we are it means that we were continued instead of pulling up + // children. + if (nsnull != mPrevInFlow) { + nsIFrame* ourParent = mGeometricParent; + nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent; + NS_ASSERTION(ourParent != prevParent, "bad continuation"); + } +#endif + } +#endif + + // Now that reflow has finished, remove the cached pointer + shell->RemoveCachedData(this); + NS_RELEASE(shell); + +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); + PostReflowCheck(aStatus); +#endif +#ifdef NOISY_REFLOW + ListTag(stdout); + printf(": After:\n"); + List(stdout, 1); +#endif + return rv; } -PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ContentAppended(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentInserted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentReplaced(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::ContentDeleted(nsIPresShell* aShell, + nsIPresContext* aPresContext, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInParent) +{ + nsresult rv = NS_OK; + return rv; +} + +NS_METHOD +nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext, + nsReflowMetrics& aMetrics) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +NS_METHOD +nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsSize* aMaxElementSize, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + aStatus = frComplete; + nsBlockReflowState state; + rv = InitializeState(aPresContext, aSpaceManager, aMaxSize, + aMaxElementSize, state); + if (NS_OK == rv) { + nsRect desiredRect; + rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus); + } + return rv; +} + +NS_METHOD +nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, + nsISpaceManager* aSpaceManager, + const nsSize& aMaxSize, + nsRect& aDesiredRect, + nsReflowCommand& aReflowCommand, + nsIFrame::ReflowStatus& aStatus) +{ + nsresult rv = NS_OK; + return rv; +} + +//---------------------------------------------------------------------- + +PRBool +nsBlockFrame::AddFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + + if (nsnull != state) { + // Get the frame associated with the space manager, and get its + // nsIAnchoredItems interface + nsIFrame* frame = state->mSpaceManager->GetFrame(); + nsIAnchoredItems* anchoredItems = nsnull; + + frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); + NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); + if (nsnull != anchoredItems) { + anchoredItems->AddAnchoredItem(aFloater, + nsIAnchoredItems::anHTMLFloater, + this); + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + return PR_TRUE; + } + } + + return PR_FALSE; +} + +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder) +{ + nsIPresShell* shell = aPresContext->GetShell(); + nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this); + NS_RELEASE(shell); + if (nsnull != state) { + PlaceFloater(aPresContext, aFloater, aPlaceholder, *state); + } +} + +PRBool +nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) { do { nsIFrame* parent; - aFrame->GetGeometricParent(parent); - // See if there are any non-zero sized child frames that precede aFrame - // in the child list + // See if there are any non-zero sized child frames that precede + // aFrame in the child list nsIFrame* child; - parent->FirstChild(child); while ((nsnull != child) && (aFrame != child)) { nsSize size; @@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // We found a non-zero sized child frame that precedes aFrame return PR_FALSE; } - child->GetNextSibling(child); } @@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame) // Walk up one level and check that its parent is left-most as well aFrame = parent; } while (aFrame != this); - return PR_TRUE; } -PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) -{ - // Get the frame associated with the space manager, and get its nsIAnchoredItems - // interface - nsIFrame* frame = mCurrentState->spaceManager->GetFrame(); - nsIAnchoredItems* anchoredItems = nsnull; - - frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems); - NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface"); - - if (nsnull != anchoredItems) { - anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this); - PlaceFloater(aCX, aFloater, aPlaceholder); - return PR_TRUE; - } - - return PR_FALSE; -} - -// XXX The size of the floater needs to be taken into consideration if we're -// computing a maximum element size -void nsBlockFrame::PlaceFloater(nsIPresContext* aCX, - nsIFrame* aFloater, - PlaceholderFrame* aPlaceholder) +void +nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext, + nsIFrame* aFloater, + PlaceholderFrame* aPlaceholder, + nsBlockReflowState& aState) { // If the floater is the left-most non-zero size child frame then insert // it before the current line; otherwise add it to the below-current-line // todo list and we'll handle it when we flush out the line if (IsLeftMostChild(aPlaceholder)) { - // Get the type of floater - nsIStyleContextPtr styleContext; + nsISpaceManager* sm = aState.mSpaceManager; - aFloater->GetStyleContext(aCX, styleContext.AssignRef()); + // Get the type of floater + nsIStyleContextPtr styleContext; + aFloater->GetStyleContext(aPresContext, styleContext.AssignRef()); nsStyleDisplay* floaterDisplay = (nsStyleDisplay*) styleContext->GetData(kStyleDisplaySID); - if (!mCurrentState->isInline) { - // Get the current band for this line - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); - } - - // Commit some space in the space manager - nsRect region; - + // Commit some space in the space manager and adjust our current + // band of available space. + nsRect region; aFloater->GetRect(region); - region.y = mCurrentState->currentBand->availSpace.y; - + region.y = aState.mY; if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) { - region.x = mCurrentState->currentBand->availSpace.x; + region.x = aState.mX; } else { - NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type"); - region.x = mCurrentState->currentBand->availSpace.XMost() - region.width; + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, + "bad float type"); + region.x = aState.mCurrentBand.availSpace.XMost() - region.width; } // XXX Don't forget the floater's margins... - mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0); - mCurrentState->spaceManager->AddRectRegion(region, aFloater); + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, aFloater); // Set the origin of the floater in world coordinates nscoord worldX, worldY; - - mCurrentState->spaceManager->GetTranslation(worldX, worldY); + sm->GetTranslation(worldX, worldY); aFloater->MoveTo(region.x + worldX, region.y + worldY); - mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0); + sm->Translate(-aState.mBorderPadding.left, 0); - // Update the band of available space to reflect space taken up by the floater - GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin); + // Update the band of available space to reflect space taken up by + // the floater + GetAvailableSpace(aState, aState.mY); + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); + } } else { // Add the floater to our to-do list - mCurrentState->floaterToDo.AppendElement(aFloater); + aState.mPendingFloaters.AppendElement(aFloater); } } -NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell, - nsIPresContext* aPresContext, - nsIContent* aContainer) +void +nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState, + nscoord aY) { - // Get the last-in-flow - nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow(); - PRInt32 kidIndex = flow->NextChildOffset(); - PRInt32 startIndex = kidIndex; + NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters"); -#if 0 - nsIFrame* kidFrame = nsnull; - nsIFrame* prevKidFrame; - - flow->LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (nsnull == kid) { - break; - } + nsISpaceManager* sm = aState.mSpaceManager; + nsBlockBandData* bd = &aState.mCurrentBand; - // Resolve style for the kid - nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this); - nsStyleDisplay* kidDisplay = (nsStyleDisplay*) - kidSC->GetData(kStyleDisplaySID); + // XXX Factor this code with PlaceFloater()... + PRInt32 numFloaters = aState.mPendingFloaters.Count(); + for (PRInt32 i = 0; i < numFloaters; i++) { + nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i]; + nsRect region; - // Is it a floater? - if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this); + // Get the band of available space + // XXX This is inefficient to do this inside the loop... + GetAvailableSpace(aState, aY); + + // Get the type of floater + nsIStyleContextPtr styleContext; + floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef()); + nsStyleDisplay* sd = (nsStyleDisplay*) + styleContext->GetData(kStyleDisplaySID); + + floater->GetRect(region); + region.y = bd->availSpace.y; + if (NS_STYLE_FLOAT_LEFT == sd->mFloats) { + region.x = bd->availSpace.x; } else { - // Create initial frame for the child - nsIContentDelegate* kidDel; - nsresult fr; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - // Pseudo block frames do not contain other block elements - // unless the block element would be the first child. - if (IsPseudoFrame()) { - // Update last content offset now that we are done drawing - // children from our parent. - SetLastContentOffset(prevKidFrame); - - // It better be true that we are not being asked to flow a - // block element as our first child. That means the body - // decided it needed a pseudo-frame when it shouldn't have. - NS_ASSERTION(nsnull != mFirstChild, "bad body"); - - return NS_OK; - } - // FALL THROUGH (and create frame) - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aPresContext); - kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this); - NS_RELEASE(kidDel); - break; - - default: - fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this); - break; - } + NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type"); + region.x = bd->availSpace.XMost() - region.width; } - kidFrame->SetStyleContext(aCX,kidSC); - // Link child frame into the list of children - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - nsIFrame* nextSibling; + // XXX Don't forget the floater's margins... + sm->Translate(aState.mBorderPadding.left, 0); + sm->AddRectRegion(region, floater); - prevKidFrame->GetNextSibling(nextSibling); - NS_ASSERTION(nsnull == nextSibling, "bad append"); -#endif - prevKidFrame->SetNextSibling(kidFrame); - } else { - NS_ASSERTION(nsnull == mFirstChild, "bad create"); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - kidIndex++; - mChildCount++; + // Set the origin of the floater in world coordinates + nscoord worldX, worldY; + sm->GetTranslation(worldX, worldY); + floater->MoveTo(region.x + worldX, region.y + worldY); + sm->Translate(-aState.mBorderPadding.left, 0); } - SetLastContentOffset(prevKidFrame); -#endif - // If this is a pseudo-frame then our parent will generate the - // reflow command. Otherwise, if the container is us then we should - // generate the reflow command because we were directly called. - if (!IsPseudoFrame() && (aContainer == mContent)) { - nsReflowCommand* rc = - new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended, - startIndex); - aShell->AppendReflowCommand(rc); + aState.mPendingFloaters.Clear(); + + // Pass on updated available space to the current line + if (nsnull != aState.mCurrentLine) { + nsLineLayout& lineLayout = *aState.mCurrentLine; + lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace); } - return NS_OK; } -PRIntn nsBlockFrame::GetSkipSides() const +//---------------------------------------------------------------------- + +nsLineData* +nsBlockFrame::GetFirstLine() +{ + return mLines; +} + +PRIntn +nsBlockFrame::GetSkipSides() const { PRIntn skip = 0; if (nsnull != mPrevInFlow) { @@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const } return skip; } - -nsHTMLFrameType nsBlockFrame::GetFrameType() const -{ - return eHTMLFrame_Block; -} - -NS_METHOD nsBlockFrame::ListTag(FILE* out) const -{ - if ((nsnull != mGeometricParent) && IsPseudoFrame()) { - fprintf(out, "*block<"); - nsIAtom* atom = mContent->GetTag(); - if (nsnull != atom) { - nsAutoString tmp; - atom->ToString(tmp); - fputs(tmp, out); - } - fprintf(out, ">(%d)@%p", mIndexInParent, this); - } else { - nsHTMLContainerFrame::ListTag(out); - } - return NS_OK; -} - -#ifdef NS_DEBUG -void nsBlockFrame::DumpFlow() const -{ -#ifdef NOISY_FLOW - nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow; - while (flow != 0) { - printf(" %p: [%d,%d,%c]\n", - flow, flow->mFirstContentOffset, flow->mLastContentOffset, - (flow->mLastContentIsComplete ? 'T' : 'F')); - flow = (nsBlockFrame*) flow->mNextInFlow; - } -#endif -} -#endif diff --git a/layout/html/base/src/nsHRPart.cpp b/layout/html/base/src/nsHRPart.cpp index f1852035c969..ab7a6b9ffb05 100644 --- a/layout/html/base/src/nsHRPart.cpp +++ b/layout/html/base/src/nsHRPart.cpp @@ -73,9 +73,9 @@ public: PRInt32 aIndexInParent, nsIFrame* aParentFrame); - NS_METHOD Paint(nsIPresContext& aPresContext, - nsIRenderingContext& aRenderingContext, - const nsRect& aDirtyRect); + NS_IMETHOD Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); protected: virtual ~HRuleFrame(); diff --git a/layout/html/base/src/nsHTMLContainerFrame.h b/layout/html/base/src/nsHTMLContainerFrame.h index 9364e3f870b9..0d2d6879e04f 100644 --- a/layout/html/base/src/nsHTMLContainerFrame.h +++ b/layout/html/base/src/nsHTMLContainerFrame.h @@ -42,14 +42,6 @@ public: nsIFrame** aFrame, PRInt32& aCursor); -#if 0 - virtual ReflowStatus IncrementalReflow(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsSize& aMaxSize, - nsISpaceManager* aSpaceManager, - nsReflowCommand& aReflowCommand); -#endif - protected: virtual ~nsHTMLContainerFrame(); @@ -59,32 +51,6 @@ protected: const nsString& aBase, const nsString& aURLSpec, const nsString& aTargetSpec); - -#if 0 - virtual ReflowStatus ReflowAppended(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsSize& aMaxSize, - nsISpaceManager* aSpaceManager, - nsReflowCommand& aReflowCommand); - - virtual ReflowStatus ReflowInserted(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsSize& aMaxSize, - nsISpaceManager* aSpaceManager, - nsReflowCommand& aReflowCommand); - - virtual ReflowStatus ReflowDeleted(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsSize& aMaxSize, - nsISpaceManager* aSpaceManager, - nsReflowCommand& aReflowCommand); - - virtual ReflowStatus ReflowChanged(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsSize& aMaxSize, - nsISpaceManager* aSpaceManager, - nsReflowCommand& aReflowCommand); -#endif }; #endif /* nsHTMLContainerFrame_h___ */ diff --git a/layout/html/base/src/nsHTMLIIDs.cpp b/layout/html/base/src/nsHTMLIIDs.cpp index 64f39328accc..22bbf94f8607 100644 --- a/layout/html/base/src/nsHTMLIIDs.cpp +++ b/layout/html/base/src/nsHTMLIIDs.cpp @@ -17,7 +17,7 @@ */ #include "nsHTMLIIDs.h" #include "nsIHTMLContent.h" -#include "nsIHTMLFrameType.h" +#include "nsBlockFrame.h" const nsIID kIHTMLContentIID = NS_IHTMLCONTENT_IID; -const nsIID kIHTMLFrameTypeIID = NS_IHTMLFRAMETYPE_IID; +const nsIID kBlockFrameCID = NS_BLOCKFRAME_CID; diff --git a/layout/html/base/src/nsHTMLIIDs.h b/layout/html/base/src/nsHTMLIIDs.h index 8a4e3e694caf..d6853c8410da 100644 --- a/layout/html/base/src/nsHTMLIIDs.h +++ b/layout/html/base/src/nsHTMLIIDs.h @@ -21,6 +21,6 @@ #include "nsISupports.h" extern const nsIID kIHTMLContentIID; -extern const nsIID kIHTMLFrameTypeIID; +extern const nsIID kBlockFrameCID; #endif /* nsHTMLIIDs_h___ */ diff --git a/layout/html/base/src/nsInlineFrame.cpp b/layout/html/base/src/nsInlineFrame.cpp index 4864674f0487..9cd92de6cf59 100644 --- a/layout/html/base/src/nsInlineFrame.cpp +++ b/layout/html/base/src/nsInlineFrame.cpp @@ -592,42 +592,6 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, // FALLTHROUGH case NS_STYLE_DISPLAY_INLINE: - // XXX temporary hack to make plain BR's in inlines "work" - // get style for break-before-after; get break-type (line, page, etc.) - { - nsIAtom* tag = kid->GetTag(); - if (nsHTMLAtoms::br == tag) { - // Set break-after flag so we stop mapping children (we - // will end up being continued if there are more children) - breakAfter = PR_TRUE; - - // Get cached state for containing block frame - // XXX how about QueryInterface(kIHTMLBlockFrameIID)? DOH! - nsBlockReflowState* state = nsnull; - nsIFrame* parent = mGeometricParent; - while (nsnull != parent) { - nsIHTMLFrameType* ft; - nsresult status = - parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft); - if (NS_OK == status) { - nsHTMLFrameType type = ft->GetFrameType(); - if (eHTMLFrame_Block == type) { - break; - } - } - parent->GetGeometricParent(parent); - } - if (nsnull != parent) { - nsIPresShell* shell = aPresContext->GetShell(); - state = (nsBlockReflowState*) shell->GetCachedData(parent); - // XXX Of course this won't work if the inline span is nested - // in another inline span! - state->breakAfterChild = PR_TRUE; - NS_RELEASE(shell); - } - } - NS_IF_RELEASE(tag); - } kidDel = kid->GetDelegate(aPresContext); kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this); NS_RELEASE(kidDel); diff --git a/layout/html/base/src/nsLineLayout.cpp b/layout/html/base/src/nsLineLayout.cpp index e2371682c2e1..d3ec7ec27864 100644 --- a/layout/html/base/src/nsLineLayout.cpp +++ b/layout/html/base/src/nsLineLayout.cpp @@ -841,10 +841,10 @@ nsLineLayout::CreateFrameFor(nsIContent* aKid) nsIFrame* kidFrame; if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) { AbsoluteFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock); - kidFrame->SetStyleContext(kidSC); + kidFrame->SetStyleContext(mPresContext, kidSC); } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { PlaceholderFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock); - kidFrame->SetStyleContext(kidSC); + kidFrame->SetStyleContext(mPresContext, kidSC); } else if (nsnull == mKidPrevInFlow) { // Create initial frame for the child @@ -882,7 +882,7 @@ nsLineLayout::CreateFrameFor(nsIContent* aKid) nsFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock); break; } - kidFrame->SetStyleContext(kidSC); + kidFrame->SetStyleContext(mPresContext, kidSC); } else { // Since kid has a prev-in-flow, use that to create the next // frame. diff --git a/layout/html/base/src/nsListItemFrame.cpp b/layout/html/base/src/nsListItemFrame.cpp index 78e8b5e6336e..ee6971db0dde 100644 --- a/layout/html/base/src/nsListItemFrame.cpp +++ b/layout/html/base/src/nsListItemFrame.cpp @@ -197,7 +197,7 @@ PRInt32 BulletFrame::GetListItemOrdinal(nsIPresContext* aCX, if (eHTMLUnit_Integer == value.GetUnit()) { ordinal = value.GetIntValue(); if (nsnull != state) { - state->nextListOrdinal = ordinal + 1; + state->mNextListOrdinal = ordinal + 1; } goto done; } @@ -205,7 +205,7 @@ PRInt32 BulletFrame::GetListItemOrdinal(nsIPresContext* aCX, // Get ordinal from block reflow state if (nsnull != state) { - ordinal = state->nextListOrdinal; + ordinal = state->mNextListOrdinal; if (ordinal < 0) { // This is the first list item and the list container doesn't // have a "start" attribute. Get the starting ordinal value @@ -219,7 +219,7 @@ PRInt32 BulletFrame::GetListItemOrdinal(nsIPresContext* aCX, break; } } - state->nextListOrdinal = ordinal + 1; + state->mNextListOrdinal = ordinal + 1; } done: @@ -518,26 +518,23 @@ nsListItemFrame::GetListContainerReflowState(nsIPresContext* aCX) nsBlockReflowState* state = nsnull; nsIFrame* parent = mGeometricParent; while (nsnull != parent) { - nsIHTMLFrameType* ft; - nsresult status = parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft); + void* ft; + nsresult status = parent->QueryInterface(kBlockFrameCID, &ft); if (NS_OK == status) { - nsHTMLFrameType type = ft->GetFrameType(); - if (eHTMLFrame_Block == type) { - // The parent is a block. See if its content object is a list - // container. Only UL, OL, MENU or DIR can be list containers. - // XXX need something more flexible, say style? - nsIContent* parentContent; + // The parent is a block. See if its content object is a list + // container. Only UL, OL, MENU or DIR can be list containers. + // XXX need something more flexible, say style? + nsIContent* parentContent; - parent->GetContent(parentContent); - nsIAtom* tag = parentContent->GetTag(); - NS_RELEASE(parentContent); - if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || - (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { - NS_RELEASE(tag); - break; - } + parent->GetContent(parentContent); + nsIAtom* tag = parentContent->GetTag(); + NS_RELEASE(parentContent); + if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) || + (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) { NS_RELEASE(tag); + break; } + NS_RELEASE(tag); } parent->GetGeometricParent(parent); } @@ -549,6 +546,29 @@ nsListItemFrame::GetListContainerReflowState(nsIPresContext* aCX) return state; } +void +nsListItemFrame::InsertBullet(nsIFrame* aBullet) +{ + mFirstChild = aBullet; + mChildCount++; + if (nsnull == mLines) { + mLines = new nsLineData(); + mLines->mFirstChild = aBullet; + mLines->mChildCount = 1; + mLines->mFirstContentOffset = 0; + mLines->mLastContentOffset = 0; + mLines->mLastContentIsComplete = PR_TRUE; + } + else { + mLines->mChildCount++; + mLines->mFirstChild = aBullet; + } + mLines->mHasBullet = PR_TRUE; +#ifdef NS_DEBUG + mLines->Verify(); +#endif +} + /** * The basic approach here is pretty simple: let our base class do all * the hard work, and after it's done, get the bullet placed. We only @@ -579,8 +599,7 @@ NS_METHOD nsListItemFrame::ResizeReflow(nsIPresContext* aCX, // Inside bullets get placed on the list immediately so that // the regular reflow logic can place them. bullet = CreateBullet(aCX); - mFirstChild = bullet; - mChildCount++; + InsertBullet(bullet); } else { // We already have a first child. It's the bullet (check?) // so we don't need to do anything here @@ -593,6 +612,9 @@ NS_METHOD nsListItemFrame::ResizeReflow(nsIPresContext* aCX, // Pull bullet off list (we'll put it back later) bullet = mFirstChild; bullet->GetNextSibling(mFirstChild); + mLines->mFirstChild = mFirstChild; + mLines->mChildCount--; + mLines->mHasBullet = PR_FALSE; mChildCount--; } } @@ -600,9 +622,9 @@ NS_METHOD nsListItemFrame::ResizeReflow(nsIPresContext* aCX, // Let base class do things first nsBlockReflowState state; - SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager); - state.firstChildIsInsideBullet = insideBullet; - DoResizeReflow(aCX, state, aDesiredRect, aStatus); + InitializeState(aCX, aSpaceManager, aMaxSize, aMaxElementSize, state); + state.mFirstChildIsInsideBullet = insideBullet; + DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus); // Now place the bullet and put it at the head of the list of children if (!insideBullet && (nsnull != bullet)) { @@ -612,19 +634,12 @@ NS_METHOD nsListItemFrame::ResizeReflow(nsIPresContext* aCX, // information for the first line. PlaceOutsideBullet(bullet, aCX); bullet->SetNextSibling(mFirstChild); - mFirstChild = bullet; - mChildCount++; - if (nsnull == mLines) { - mLines = new PRInt32[1]; - mLines[0] = 1; - mNumLines = 1; - } else { - mLines[0]++; - } + InsertBullet(bullet); } return NS_OK; } +#if XXX // XXX we may need to grow to accomodate the bullet NS_METHOD nsListItemFrame::IncrementalReflow(nsIPresContext* aCX, nsISpaceManager* aSpaceManager, @@ -634,8 +649,10 @@ NS_METHOD nsListItemFrame::IncrementalReflow(nsIPresContext* aCX, ReflowStatus& aStatus) { aStatus = frComplete; + // XXX return NS_OK; } +#endif NS_METHOD nsListItemFrame::CreateContinuingFrame(nsIPresContext* aCX, nsIFrame* aParent, diff --git a/layout/html/base/src/nsListItemFrame.h b/layout/html/base/src/nsListItemFrame.h index 0a0c2c26573c..66ed62e576fb 100644 --- a/layout/html/base/src/nsListItemFrame.h +++ b/layout/html/base/src/nsListItemFrame.h @@ -28,20 +28,13 @@ public: PRInt32 aIndexInParent, nsIFrame* aParent); + // nsIFrame NS_IMETHOD ResizeReflow(nsIPresContext* aCX, nsISpaceManager* aSpaceManager, const nsSize& aMaxSize, nsRect& aDesiredRect, nsSize* aMaxElementSize, ReflowStatus& aStatus); - - NS_IMETHOD IncrementalReflow(nsIPresContext* aCX, - nsISpaceManager* aSpaceManager, - const nsSize& aMaxSize, - nsRect& aDesiredRect, - nsReflowCommand& aReflowCommand, - ReflowStatus& aStatus); - NS_IMETHOD CreateContinuingFrame(nsIPresContext* aCX, nsIFrame* aParent, nsIFrame*& aContinuingFrame); @@ -62,6 +55,8 @@ protected: nsIFrame* CreateBullet(nsIPresContext* aCX); + void InsertBullet(nsIFrame* aBullet); + virtual void PaintChildren(nsIPresContext& aCX, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect); diff --git a/layout/html/base/src/nsSpacerPart.cpp b/layout/html/base/src/nsSpacerPart.cpp index 280293f20441..4ef630a0ad28 100644 --- a/layout/html/base/src/nsSpacerPart.cpp +++ b/layout/html/base/src/nsSpacerPart.cpp @@ -91,9 +91,9 @@ SpacerFrame::ResizeReflow(nsIPresContext* aPresContext, { // Get cached state for containing block frame nsBlockReflowState* state = nsnull; +#if XXX nsIFrame* parent = mGeometricParent; while (nsnull != parent) { - nsIHTMLFrameType* ft; nsresult status = parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft); if (NS_OK == status) { nsHTMLFrameType type = ft->GetFrameType(); @@ -108,6 +108,7 @@ SpacerFrame::ResizeReflow(nsIPresContext* aPresContext, state = (nsBlockReflowState*) shell->GetCachedData(parent); NS_RELEASE(shell); } +#endif // By default, we have no area aDesiredSize.width = 0; @@ -170,8 +171,10 @@ SpacerFrame::ResizeReflow(nsIPresContext* aPresContext, case TYPE_LINE: if (0 != width) { +#if XXX state->breakBeforeChild = PR_TRUE; state->breakAfterChild = PR_TRUE; +#endif aDesiredSize.height = nscoord(width * p2t); aDesiredSize.ascent = aDesiredSize.height; } diff --git a/layout/html/base/src/nsTextContent.cpp b/layout/html/base/src/nsTextContent.cpp index efe14b4bcc89..9985b3977bc5 100644 --- a/layout/html/base/src/nsTextContent.cpp +++ b/layout/html/base/src/nsTextContent.cpp @@ -34,6 +34,7 @@ #include "nsITimer.h" #include "nsBlockFrame.h" #include "prtime.h" +#include "nsVoidArray.h" #include "prprf.h" #include "nsIDOMText.h" @@ -151,7 +152,7 @@ protected: nsSize* aMaxElementSize, nsStyleFont& aFont, PRInt32 aStartingOffset, - nsBlockReflowState* aState); + nsLineLayout* aLineState); ReflowStatus ReflowNormal(nsIPresContext* aCX, nsReflowMetrics& aDesiredSize, @@ -160,7 +161,7 @@ protected: nsStyleFont& aFont, nsStyleText& aTextStyle, PRInt32 aStartingOffset, - nsBlockReflowState* aState); + nsLineLayout* aLineState); public: PRInt32 mContentOffset; @@ -665,23 +666,13 @@ NS_METHOD TextFrame::ResizeReflow(nsIPresContext* aCX, } // Get cached state for containing block frame - nsBlockReflowState* state = nsnull; - nsIFrame* parent = mGeometricParent; - while (nsnull != parent) { - nsIHTMLFrameType* ft; - nsresult status = parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft); - if (NS_OK == status) { - nsHTMLFrameType type = ft->GetFrameType(); - if (eHTMLFrame_Block == type) { - break; - } + nsLineLayout* lineLayoutState = nsnull; + nsBlockReflowState* state = nsBlockFrame::FindBlockReflowState(aCX, this); + if (nsnull != state) { + lineLayoutState = state->mCurrentLine; + if (nsnull != lineLayoutState) { + lineLayoutState->mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_AWARE; } - parent->GetGeometricParent(parent); - } - if (nsnull != parent) { - nsIPresShell* shell = aCX->GetShell(); - state = (nsBlockReflowState*) shell->GetCachedData(parent); - NS_RELEASE(shell); } nsStyleFont* font = @@ -703,13 +694,14 @@ NS_METHOD TextFrame::ResizeReflow(nsIPresContext* aCX, if (NS_STYLE_WHITESPACE_PRE == text->mWhiteSpace) { // Use a specialized routine for pre-formatted text aStatus = ReflowPre(aCX, aDesiredSize, aMaxSize, - aMaxElementSize, *font, startingOffset, state); + aMaxElementSize, *font, startingOffset, + lineLayoutState); } else { // Use normal wrapping routine for non-pre text (this includes // text that is not wrapping) aStatus = ReflowNormal(aCX, aDesiredSize, aMaxSize, aMaxElementSize, *font, *text, - startingOffset, state); + startingOffset, lineLayoutState); } #ifdef NOISY @@ -731,7 +723,7 @@ TextFrame::ReflowNormal(nsIPresContext* aCX, nsStyleFont& aFont, nsStyleText& aTextStyle, PRInt32 aStartingOffset, - nsBlockReflowState* aState) + nsLineLayout* aLineState) { Text* txt = (Text*) mContent; const PRUnichar* cp = txt->mText + aStartingOffset; @@ -749,8 +741,8 @@ TextFrame::ReflowNormal(nsIPresContext* aCX, // Set whitespace skip flag PRBool skipWhitespace = PR_FALSE; - if (nsnull != aState) { - if (!aState->allowLeadingWhitespace) { + if (nsnull != aLineState) { + if (aLineState->mSkipLeadingWhiteSpace) { skipWhitespace = PR_TRUE; mFlags |= TEXT_SKIP_LEADING_WS; } @@ -769,6 +761,7 @@ TextFrame::ReflowNormal(nsIPresContext* aCX, nscoord maxWidth = aMaxSize.width; nscoord maxWordWidth = 0; const PRUnichar* lastWordEnd = cpStart; + const PRUnichar* lastWordStart = cpStart; PRBool hasMultibyte = PR_FALSE; PRBool endsInWhitespace = PR_FALSE; while (cp < end) { @@ -798,6 +791,7 @@ TextFrame::ReflowNormal(nsIPresContext* aCX, hasMultibyte = PR_TRUE; } const PRUnichar* wordStart = cp - 1; + lastWordStart = wordStart; while (cp < end) { ch = *cp; if (ch >= 256) { @@ -840,13 +834,14 @@ TextFrame::ReflowNormal(nsIPresContext* aCX, mFlags |= TEXT_HAS_MULTIBYTE; } - if (nsnull != aState) { + if (nsnull != aLineState) { if (0 == x) { // Since we collapsed into nothingness (all our whitespace - // is ignored) leave the aState->allowLeadingWhitespace + // is ignored) leave the aState->mSkipLeadingWhiteSpace // flag alone since it doesn't want leading whitespace - } else { - aState->allowLeadingWhitespace = !endsInWhitespace; + } + else { + aLineState->mSkipLeadingWhiteSpace = endsInWhitespace; } } @@ -889,7 +884,7 @@ TextFrame::ReflowPre(nsIPresContext* aCX, nsSize* aMaxElementSize, nsStyleFont& aFont, PRInt32 aStartingOffset, - nsBlockReflowState* aState) + nsLineLayout* aLineState) { Text* txt = (Text*) mContent; const PRUnichar* cp = txt->mText + aStartingOffset; @@ -903,16 +898,16 @@ TextFrame::ReflowPre(nsIPresContext* aCX, PRBool hasMultibyte = PR_FALSE; PRUint16 tabs = 0; PRIntn col = 0; - if (nsnull != aState) { - col = aState->column; + if (nsnull != aLineState) { + col = aLineState->mColumn; } mColumn = (PRUint16) col; nscoord spaceWidth = widths[' ']; while (cp < end) { PRUnichar ch = *cp++; if (ch == '\n') { - if (nsnull != aState) { - aState->breakAfterChild = PR_TRUE; + if (nsnull != aLineState) { + aLineState->mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_BREAK_AFTER; } break; } @@ -937,8 +932,8 @@ TextFrame::ReflowPre(nsIPresContext* aCX, } col++; } - if (nsnull != aState) { - aState->column = col; + if (nsnull != aLineState) { + aLineState->mColumn = col; } if (hasMultibyte) { mFlags |= TEXT_HAS_MULTIBYTE;