From e1b0af4e1dd35698e933d8bef75b3567cd75fd2b Mon Sep 17 00:00:00 2001 From: "karnaze%netscape.com" Date: Wed, 17 Jul 2002 01:48:56 +0000 Subject: [PATCH] bug 153785 - Added function to undo splitting floaters when the line they are on gets moved to the next page. Cached the break type of the last floater that gets split in the block reflow state so that the frame afterh the floater's continuation can combine that break type with its own. a=asa, sr=kin, r=alexsavulov git-svn-id: svn://10.0.0.236/trunk@125355 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/generic/nsBlockFrame.cpp | 194 ++++++++++++++---- mozilla/layout/generic/nsBlockFrame.h | 11 +- mozilla/layout/generic/nsBlockReflowState.cpp | 21 +- mozilla/layout/generic/nsBlockReflowState.h | 2 + mozilla/layout/html/base/src/nsBlockFrame.cpp | 194 ++++++++++++++---- mozilla/layout/html/base/src/nsBlockFrame.h | 11 +- .../html/base/src/nsBlockReflowState.cpp | 21 +- .../layout/html/base/src/nsBlockReflowState.h | 2 + 8 files changed, 354 insertions(+), 102 deletions(-) diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index 8968b8bbe9a..a48726b334b 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -953,29 +953,43 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, aStatus = state.mReflowStatus; - // If there are incomplete floaters then they will go in the 1st line of the overflow lines. + // Put continued floaters at the beginning of the first overflow line. If the first line + // is a block then create a new line as the first line and put the floaters there. If there + // are no overflow lines, then create one and put the floaters in it. if (state.mOverflowFloaters.NotEmpty()) { - aStatus = NS_FRAME_NOT_COMPLETE; nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); if (overflowLines) { - // put the floaters at the beginning of the line line_iterator firstLine = overflowLines->begin(); - nsIFrame* firstFrame = firstLine->mFirstChild; - firstLine->mFirstChild = state.mOverflowFloaters.FirstChild(); - PRInt32 numOverflowFloaters = state.mOverflowFloaters.GetLength(); - state.mOverflowFloaters.LastChild()->SetNextSibling(firstFrame); - firstLine->SetChildCount(firstLine->GetChildCount() + numOverflowFloaters); + if (firstLine->IsBlock()) { // floaters go on a new line before 1st overflow line + nsLineBox* newLine = state.NewLineBox(state.mOverflowFloaters.FirstChild(), + state.mOverflowFloaters.GetLength(), PR_FALSE); + firstLine = mLines.before_insert(firstLine, newLine); + } + else { // floaters go on 1st overflow line + nsIFrame* firstFrame = firstLine->mFirstChild; + firstLine->mFirstChild = state.mOverflowFloaters.FirstChild(); + PRInt32 numOverflowFloaters = state.mOverflowFloaters.GetLength(); + // hook up the last placeholder with the original frames + nsPlaceholderFrame* lastPlaceholder = + (nsPlaceholderFrame*)state.mOverflowFloaters.LastChild(); + lastPlaceholder->SetNextSibling(firstFrame); + NS_ASSERTION(firstFrame != lastPlaceholder, "trying to set next sibling to self"); + firstLine->SetChildCount(firstLine->GetChildCount() + numOverflowFloaters); + } } - else { - // there aren't any overflow lines, so create a line, put the floaters in it, and then push. + else { + // Create a line, put the floaters in it, and then push. nsLineBox* newLine = state.NewLineBox(state.mOverflowFloaters.FirstChild(), state.mOverflowFloaters.GetLength(), PR_FALSE); if (!newLine) return NS_ERROR_OUT_OF_MEMORY; + state.mOverflowFloaters.SetFrames(nsnull); mLines.push_back(newLine); nsLineList::iterator nextToLastLine = ----end_lines(); PushLines(state, nextToLastLine); } + aStatus = NS_FRAME_NOT_COMPLETE; + state.mOverflowFloaters.SetFrames(nsnull); } if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { @@ -3112,6 +3126,63 @@ nsBlockFrame::GetTopBlockChild() return nsnull; } +// If placeholders/floaters split during reflowing a line, but that line will +// be put on the next page, then put the placeholders/floaters back the way +// they were before the line was reflowed. +static void +UndoPlaceholders(nsBlockReflowState& aState, + nsIFrame* aLastPlaceholder) +{ + nsIFrame* undoPlaceholder = nsnull; + if (aLastPlaceholder) { + aLastPlaceholder->GetNextSibling(&undoPlaceholder); + aLastPlaceholder->SetNextSibling(nsnull); + } + else { + undoPlaceholder = aState.mOverflowFloaters.FirstChild(); + aState.mOverflowFloaters.SetFrames(nsnull); + } + // remove the next in flows of the placeholders that need to be removed + for (nsIFrame* placeholder = undoPlaceholder; placeholder; ) { + nsSplittableFrame::RemoveFromFlow(placeholder); + nsIFrame* savePlaceholder = placeholder; + placeholder->GetNextSibling(&placeholder); + savePlaceholder->Destroy(aState.mPresContext); + } +} + +// Combine aNewBreakType with aOrigBreakType, but limit the break types +// to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT. When there is a
right +// after a floater and the floater splits, then the
's break type is combined +// with the break type of the frame right after the floaters next-in-flow. +static PRUint8 +CombineBreakType(PRUint8 aOrigBreakType, + PRUint8 aNewBreakType) +{ + PRUint8 breakType = aOrigBreakType; + switch(breakType) { + case NS_STYLE_CLEAR_LEFT: + if ((NS_STYLE_CLEAR_RIGHT == aNewBreakType) || + (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) { + breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT; + } + break; + case NS_STYLE_CLEAR_RIGHT: + if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) || + (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) { + breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT; + } + break; + case NS_STYLE_CLEAR_NONE: + if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) || + (NS_STYLE_CLEAR_RIGHT == aNewBreakType) || + (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) { + breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT; + } + } + return breakType; +} + nsresult nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, line_iterator aLine, @@ -3146,8 +3217,15 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, applyTopMargin = ShouldApplyTopMargin(aState, aLine); } - // Clear past floaters before the block if the clear style is not none PRUint8 breakType = display->mBreakType; + // If a floater split and its prev-in-flow was followed by a
, then combine + // the
's break type with the block's break type (the block will be the very + // next frame after the split floater). + if (NS_STYLE_CLEAR_NONE != aState.mFloaterBreakType) { + breakType = ::CombineBreakType(breakType, aState.mFloaterBreakType); + aState.mFloaterBreakType = NS_STYLE_CLEAR_NONE; + } + // Clear past floaters before the block if the clear style is not none aLine->SetBreakType(breakType); if (NS_STYLE_CLEAR_NONE != breakType) { PRBool alsoApplyTopMargin = aState.ClearPastFloaters(breakType); @@ -3219,6 +3297,9 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } } + // keep track of the last overflow floater in case we need to undo any new additions + nsIFrame* lastPlaceholder = aState.mOverflowFloaters.LastChild(); + // Reflow the block into the available space nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE; nsMargin computedOffsets; @@ -3226,6 +3307,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, applyTopMargin, aState.mPrevBottomMargin, aState.IsAdjacentWithTop(), computedOffsets, frameReflowStatus); + if (brc.BlockShouldInvalidateItself() && !mRect.IsEmpty()) { Invalidate(aState.mPresContext, mRect); } @@ -3245,6 +3327,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) { // None of the child block fits. + ::UndoPlaceholders(aState, lastPlaceholder); PushLines(aState, aLine.prev()); *aKeepReflowGoing = PR_FALSE; aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; @@ -3420,6 +3503,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, else { // Push the line that didn't fit and any lines that follow it // to our next-in-flow. + ::UndoPlaceholders(aState, lastPlaceholder); PushLines(aState, aLine.prev()); aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; } @@ -3533,22 +3617,8 @@ nsBlockFrame::PushTruncatedPlaceholderLine(nsBlockReflowState& aState, nsIFrame* aLastPlaceholder, PRBool& aKeepReflowGoing) { - nsIFrame* undoPlaceholder = nsnull; - if (aLastPlaceholder) { - aLastPlaceholder->GetNextSibling(&undoPlaceholder); - aLastPlaceholder->SetNextSibling(nsnull); - } - else { - undoPlaceholder = aState.mOverflowFloaters.FirstChild(); - aState.mOverflowFloaters.SetFrames(nsnull); - } - // remove the next in flows of the placeholders that need to be removed - for (nsIFrame* placeholder = undoPlaceholder; placeholder; ) { - nsSplittableFrame::RemoveFromFlow(placeholder); - nsIFrame* savePlaceholder = placeholder; - placeholder->GetNextSibling(&placeholder); - savePlaceholder->Destroy(aState.mPresContext); - } + ::UndoPlaceholders(aState, aLastPlaceholder); + line_iterator prevLine = aLine; --prevLine; PushLines(aState, prevLine); @@ -3616,7 +3686,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, aLineLayout.SetFirstLetterStyleOK(PR_TRUE); } - // keep track of the last overflow floater in case we need to undo and push the line + // keep track of the last overflow floater in case we need to undo any new additions nsIFrame* lastPlaceholder = aState.mOverflowFloaters.LastChild(); // Reflow the frames that are already on the line first @@ -3707,7 +3777,9 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // If we are propagating out a break-before status then there is // no point in placing the line. if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { - rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth); + if (PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth)) { + ::UndoPlaceholders(aState, lastPlaceholder); // undo since we pushed the current line + } } } *aLineReflowStatus = lineReflowStatus; @@ -3792,14 +3864,16 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // block or we are an inline. This makes a total of 10 cases // (fortunately, there is some overlap). aLine->SetBreakType(NS_STYLE_CLEAR_NONE); - if (NS_INLINE_IS_BREAK(frameReflowStatus)) { + if (NS_INLINE_IS_BREAK(frameReflowStatus) || + (NS_STYLE_CLEAR_NONE != aState.mFloaterBreakType)) { // Always abort the line reflow (because a line break is the // minimal amount of break we do). *aLineReflowStatus = LINE_REFLOW_STOP; // XXX what should aLine's break-type be set to in all these cases? PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus); - NS_ASSERTION(breakType != NS_STYLE_CLEAR_NONE, "bad break type"); + NS_ASSERTION((NS_STYLE_CLEAR_NONE != breakType) || + (NS_STYLE_CLEAR_NONE != aState.mFloaterBreakType), "bad break type"); NS_ASSERTION(NS_STYLE_CLEAR_PAGE != breakType, "no page breaks yet"); if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) { @@ -3828,6 +3902,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, } } else { + // If a floater split and its prev-in-flow was followed by a
, then combine + // the
's break type with the inline's break type (the inline will be the very + // next frame after the split floater). + if (NS_STYLE_CLEAR_NONE != aState.mFloaterBreakType) { + breakType = ::CombineBreakType(breakType, aState.mFloaterBreakType); + aState.mFloaterBreakType = NS_STYLE_CLEAR_NONE; + } // Break-after cases if (breakType == NS_STYLE_CLEAR_LINE) { if (!aLineLayout.GetLineEndsInBR()) { @@ -4061,15 +4142,13 @@ nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, return PR_FALSE; } -nsresult +PRBool nsBlockFrame::PlaceLine(nsBlockReflowState& aState, - nsLineLayout& aLineLayout, - line_iterator aLine, - PRBool* aKeepReflowGoing, - PRBool aUpdateMaximumWidth) + nsLineLayout& aLineLayout, + line_iterator aLine, + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { - nsresult rv = NS_OK; - // Trim extra white-space from the line before placing the frames aLineLayout.TrimTrailingWhiteSpace(); @@ -4242,7 +4321,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; *aKeepReflowGoing = PR_FALSE; } - return rv; + return PR_TRUE; } aState.mY = newY; @@ -4376,7 +4455,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, break; } - return rv; + return PR_FALSE; } // Compute the line's max-element-size by adding into the raw value @@ -4484,7 +4563,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, } void -nsBlockFrame::PushLines(nsBlockReflowState& aState, +nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineList::iterator aLineBefore) { nsLineList::iterator overBegin(aLineBefore.next()); @@ -5405,6 +5484,39 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, #ifdef NOISY_FLOATER printf("end ReflowFloater %p, sized to %d,%d\n", floater, metrics.width, metrics.height); #endif + + // If the placeholder was continued and its first-in-flow was followed by a + //
, then cache the
's break type in aState.mFloaterBreakType so that + // the next frame after the placeholder can combine that break type with its own + nsIFrame* prevPlaceholder = nsnull; + aPlaceholder->GetPrevInFlow(&prevPlaceholder); + if (prevPlaceholder) { + // the break occurs only after the last continued placeholder + PRBool lastPlaceholder = PR_TRUE; + nsIFrame* next; + aPlaceholder->GetNextSibling(&next); + if (next) { + nsCOMPtr nextType; + next->GetFrameType(getter_AddRefs(nextType)); + if (nsLayoutAtoms::placeholderFrame == nextType) { + lastPlaceholder = PR_FALSE; + } + } + if (lastPlaceholder) { + // get the containing block of prevPlaceholder which is our prev-in-flow + if (mPrevInFlow) { + // get the break type of the last line in mPrevInFlow + line_iterator endLine = --((nsBlockFrame*)mPrevInFlow)->end_lines(); + PRUint8 breakType = endLine->GetBreakType(); + if ((NS_STYLE_CLEAR_LEFT == breakType) || + (NS_STYLE_CLEAR_RIGHT == breakType) || + (NS_STYLE_CLEAR_LEFT_AND_RIGHT == breakType)) { + aState.mFloaterBreakType = breakType; + } + } + else NS_ASSERTION(PR_FALSE, "no prev in flow"); + } + } return NS_OK; } diff --git a/mozilla/layout/generic/nsBlockFrame.h b/mozilla/layout/generic/nsBlockFrame.h index 318b095d81c..1ecf8b23b4e 100644 --- a/mozilla/layout/generic/nsBlockFrame.h +++ b/mozilla/layout/generic/nsBlockFrame.h @@ -333,11 +333,12 @@ protected: PRBool* aKeepReflowGoing, PRBool aDamageDirtyArea = PR_FALSE); - nsresult PlaceLine(nsBlockReflowState& aState, - nsLineLayout& aLineLayout, - line_iterator aLine, - PRBool* aKeepReflowGoing, - PRBool aUpdateMaximumWidth); + // Return PR_TRUE if aLine gets pushed. + PRBool PlaceLine(nsBlockReflowState& aState, + nsLineLayout& aLineLayout, + line_iterator aLine, + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth); /** * Mark |aLine| dirty, and, if necessary because of possible diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp index 0f10fcf420b..847a6eefe12 100644 --- a/mozilla/layout/generic/nsBlockReflowState.cpp +++ b/mozilla/layout/generic/nsBlockReflowState.cpp @@ -66,7 +66,8 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mLastFloaterY(0), mPrevBottomMargin(), mLineNumber(0), - mFlags(0) + mFlags(0), + mFloaterBreakType(NS_STYLE_CLEAR_NONE) { const nsMargin& borderPadding = BorderPadding(); @@ -974,6 +975,13 @@ nsBlockReflowState::FlowAndPlaceFloater(nsFloaterCache* aFloaterCache, } } + // If the floater is continued, it will get the same x value as its prev-in-flow + nsRect prevInFlowRect(0,0,0,0); + nsIFrame* prevInFlow; + floater->GetPrevInFlow(&prevInFlow); + if (prevInFlow) { + prevInFlow->GetRect(prevInFlowRect); + } // Assign an x and y coordinate to the floater. Note that the x,y // coordinates are computed relative to the translation in the // spacemanager which means that the impacted region will be @@ -982,7 +990,7 @@ nsBlockReflowState::FlowAndPlaceFloater(nsFloaterCache* aFloaterCache, PRBool isLeftFloater; if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) { isLeftFloater = PR_TRUE; - region.x = mAvailSpaceRect.x; + region.x = (prevInFlow) ? prevInFlowRect.x : mAvailSpaceRect.x; } else { isLeftFloater = PR_FALSE; @@ -990,9 +998,7 @@ nsBlockReflowState::FlowAndPlaceFloater(nsFloaterCache* aFloaterCache, nsIFrame* prevInFlow; floater->GetPrevInFlow(&prevInFlow); if (prevInFlow) { - nsRect rect; - prevInFlow->GetRect(rect); - region.x = rect.x; + region.x = prevInFlowRect.x; } else if (!keepFloaterOnSameLine) { region.x = mAvailSpaceRect.XMost() - region.width; @@ -1022,6 +1028,11 @@ nsBlockReflowState::FlowAndPlaceFloater(nsFloaterCache* aFloaterCache, // Place the floater in the space manager if (okToAddRectRegion) { + // if the floater split, then take up all of the vertical height + if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && + (NS_UNCONSTRAINEDSIZE != mContentArea.height)) { + region.height += PR_MAX(region.height, mContentArea.height); + } #ifdef DEBUG nsresult rv = #endif diff --git a/mozilla/layout/generic/nsBlockReflowState.h b/mozilla/layout/generic/nsBlockReflowState.h index c82f058c2e5..694b4230f9d 100644 --- a/mozilla/layout/generic/nsBlockReflowState.h +++ b/mozilla/layout/generic/nsBlockReflowState.h @@ -256,6 +256,8 @@ public: #define BRS_LASTFLAG BRS_DAMAGECONSTRAINED PRInt16 mFlags; + + PRUint8 mFloaterBreakType; void SetFlag(PRUint32 aFlag, PRBool aValue) { diff --git a/mozilla/layout/html/base/src/nsBlockFrame.cpp b/mozilla/layout/html/base/src/nsBlockFrame.cpp index 8968b8bbe9a..a48726b334b 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.cpp +++ b/mozilla/layout/html/base/src/nsBlockFrame.cpp @@ -953,29 +953,43 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, aStatus = state.mReflowStatus; - // If there are incomplete floaters then they will go in the 1st line of the overflow lines. + // Put continued floaters at the beginning of the first overflow line. If the first line + // is a block then create a new line as the first line and put the floaters there. If there + // are no overflow lines, then create one and put the floaters in it. if (state.mOverflowFloaters.NotEmpty()) { - aStatus = NS_FRAME_NOT_COMPLETE; nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); if (overflowLines) { - // put the floaters at the beginning of the line line_iterator firstLine = overflowLines->begin(); - nsIFrame* firstFrame = firstLine->mFirstChild; - firstLine->mFirstChild = state.mOverflowFloaters.FirstChild(); - PRInt32 numOverflowFloaters = state.mOverflowFloaters.GetLength(); - state.mOverflowFloaters.LastChild()->SetNextSibling(firstFrame); - firstLine->SetChildCount(firstLine->GetChildCount() + numOverflowFloaters); + if (firstLine->IsBlock()) { // floaters go on a new line before 1st overflow line + nsLineBox* newLine = state.NewLineBox(state.mOverflowFloaters.FirstChild(), + state.mOverflowFloaters.GetLength(), PR_FALSE); + firstLine = mLines.before_insert(firstLine, newLine); + } + else { // floaters go on 1st overflow line + nsIFrame* firstFrame = firstLine->mFirstChild; + firstLine->mFirstChild = state.mOverflowFloaters.FirstChild(); + PRInt32 numOverflowFloaters = state.mOverflowFloaters.GetLength(); + // hook up the last placeholder with the original frames + nsPlaceholderFrame* lastPlaceholder = + (nsPlaceholderFrame*)state.mOverflowFloaters.LastChild(); + lastPlaceholder->SetNextSibling(firstFrame); + NS_ASSERTION(firstFrame != lastPlaceholder, "trying to set next sibling to self"); + firstLine->SetChildCount(firstLine->GetChildCount() + numOverflowFloaters); + } } - else { - // there aren't any overflow lines, so create a line, put the floaters in it, and then push. + else { + // Create a line, put the floaters in it, and then push. nsLineBox* newLine = state.NewLineBox(state.mOverflowFloaters.FirstChild(), state.mOverflowFloaters.GetLength(), PR_FALSE); if (!newLine) return NS_ERROR_OUT_OF_MEMORY; + state.mOverflowFloaters.SetFrames(nsnull); mLines.push_back(newLine); nsLineList::iterator nextToLastLine = ----end_lines(); PushLines(state, nextToLastLine); } + aStatus = NS_FRAME_NOT_COMPLETE; + state.mOverflowFloaters.SetFrames(nsnull); } if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { @@ -3112,6 +3126,63 @@ nsBlockFrame::GetTopBlockChild() return nsnull; } +// If placeholders/floaters split during reflowing a line, but that line will +// be put on the next page, then put the placeholders/floaters back the way +// they were before the line was reflowed. +static void +UndoPlaceholders(nsBlockReflowState& aState, + nsIFrame* aLastPlaceholder) +{ + nsIFrame* undoPlaceholder = nsnull; + if (aLastPlaceholder) { + aLastPlaceholder->GetNextSibling(&undoPlaceholder); + aLastPlaceholder->SetNextSibling(nsnull); + } + else { + undoPlaceholder = aState.mOverflowFloaters.FirstChild(); + aState.mOverflowFloaters.SetFrames(nsnull); + } + // remove the next in flows of the placeholders that need to be removed + for (nsIFrame* placeholder = undoPlaceholder; placeholder; ) { + nsSplittableFrame::RemoveFromFlow(placeholder); + nsIFrame* savePlaceholder = placeholder; + placeholder->GetNextSibling(&placeholder); + savePlaceholder->Destroy(aState.mPresContext); + } +} + +// Combine aNewBreakType with aOrigBreakType, but limit the break types +// to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT. When there is a
right +// after a floater and the floater splits, then the
's break type is combined +// with the break type of the frame right after the floaters next-in-flow. +static PRUint8 +CombineBreakType(PRUint8 aOrigBreakType, + PRUint8 aNewBreakType) +{ + PRUint8 breakType = aOrigBreakType; + switch(breakType) { + case NS_STYLE_CLEAR_LEFT: + if ((NS_STYLE_CLEAR_RIGHT == aNewBreakType) || + (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) { + breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT; + } + break; + case NS_STYLE_CLEAR_RIGHT: + if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) || + (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) { + breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT; + } + break; + case NS_STYLE_CLEAR_NONE: + if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) || + (NS_STYLE_CLEAR_RIGHT == aNewBreakType) || + (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) { + breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT; + } + } + return breakType; +} + nsresult nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, line_iterator aLine, @@ -3146,8 +3217,15 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, applyTopMargin = ShouldApplyTopMargin(aState, aLine); } - // Clear past floaters before the block if the clear style is not none PRUint8 breakType = display->mBreakType; + // If a floater split and its prev-in-flow was followed by a
, then combine + // the
's break type with the block's break type (the block will be the very + // next frame after the split floater). + if (NS_STYLE_CLEAR_NONE != aState.mFloaterBreakType) { + breakType = ::CombineBreakType(breakType, aState.mFloaterBreakType); + aState.mFloaterBreakType = NS_STYLE_CLEAR_NONE; + } + // Clear past floaters before the block if the clear style is not none aLine->SetBreakType(breakType); if (NS_STYLE_CLEAR_NONE != breakType) { PRBool alsoApplyTopMargin = aState.ClearPastFloaters(breakType); @@ -3219,6 +3297,9 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } } + // keep track of the last overflow floater in case we need to undo any new additions + nsIFrame* lastPlaceholder = aState.mOverflowFloaters.LastChild(); + // Reflow the block into the available space nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE; nsMargin computedOffsets; @@ -3226,6 +3307,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, applyTopMargin, aState.mPrevBottomMargin, aState.IsAdjacentWithTop(), computedOffsets, frameReflowStatus); + if (brc.BlockShouldInvalidateItself() && !mRect.IsEmpty()) { Invalidate(aState.mPresContext, mRect); } @@ -3245,6 +3327,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) { // None of the child block fits. + ::UndoPlaceholders(aState, lastPlaceholder); PushLines(aState, aLine.prev()); *aKeepReflowGoing = PR_FALSE; aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; @@ -3420,6 +3503,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, else { // Push the line that didn't fit and any lines that follow it // to our next-in-flow. + ::UndoPlaceholders(aState, lastPlaceholder); PushLines(aState, aLine.prev()); aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; } @@ -3533,22 +3617,8 @@ nsBlockFrame::PushTruncatedPlaceholderLine(nsBlockReflowState& aState, nsIFrame* aLastPlaceholder, PRBool& aKeepReflowGoing) { - nsIFrame* undoPlaceholder = nsnull; - if (aLastPlaceholder) { - aLastPlaceholder->GetNextSibling(&undoPlaceholder); - aLastPlaceholder->SetNextSibling(nsnull); - } - else { - undoPlaceholder = aState.mOverflowFloaters.FirstChild(); - aState.mOverflowFloaters.SetFrames(nsnull); - } - // remove the next in flows of the placeholders that need to be removed - for (nsIFrame* placeholder = undoPlaceholder; placeholder; ) { - nsSplittableFrame::RemoveFromFlow(placeholder); - nsIFrame* savePlaceholder = placeholder; - placeholder->GetNextSibling(&placeholder); - savePlaceholder->Destroy(aState.mPresContext); - } + ::UndoPlaceholders(aState, aLastPlaceholder); + line_iterator prevLine = aLine; --prevLine; PushLines(aState, prevLine); @@ -3616,7 +3686,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, aLineLayout.SetFirstLetterStyleOK(PR_TRUE); } - // keep track of the last overflow floater in case we need to undo and push the line + // keep track of the last overflow floater in case we need to undo any new additions nsIFrame* lastPlaceholder = aState.mOverflowFloaters.LastChild(); // Reflow the frames that are already on the line first @@ -3707,7 +3777,9 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // If we are propagating out a break-before status then there is // no point in placing the line. if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { - rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth); + if (PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth)) { + ::UndoPlaceholders(aState, lastPlaceholder); // undo since we pushed the current line + } } } *aLineReflowStatus = lineReflowStatus; @@ -3792,14 +3864,16 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // block or we are an inline. This makes a total of 10 cases // (fortunately, there is some overlap). aLine->SetBreakType(NS_STYLE_CLEAR_NONE); - if (NS_INLINE_IS_BREAK(frameReflowStatus)) { + if (NS_INLINE_IS_BREAK(frameReflowStatus) || + (NS_STYLE_CLEAR_NONE != aState.mFloaterBreakType)) { // Always abort the line reflow (because a line break is the // minimal amount of break we do). *aLineReflowStatus = LINE_REFLOW_STOP; // XXX what should aLine's break-type be set to in all these cases? PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus); - NS_ASSERTION(breakType != NS_STYLE_CLEAR_NONE, "bad break type"); + NS_ASSERTION((NS_STYLE_CLEAR_NONE != breakType) || + (NS_STYLE_CLEAR_NONE != aState.mFloaterBreakType), "bad break type"); NS_ASSERTION(NS_STYLE_CLEAR_PAGE != breakType, "no page breaks yet"); if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) { @@ -3828,6 +3902,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, } } else { + // If a floater split and its prev-in-flow was followed by a
, then combine + // the
's break type with the inline's break type (the inline will be the very + // next frame after the split floater). + if (NS_STYLE_CLEAR_NONE != aState.mFloaterBreakType) { + breakType = ::CombineBreakType(breakType, aState.mFloaterBreakType); + aState.mFloaterBreakType = NS_STYLE_CLEAR_NONE; + } // Break-after cases if (breakType == NS_STYLE_CLEAR_LINE) { if (!aLineLayout.GetLineEndsInBR()) { @@ -4061,15 +4142,13 @@ nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, return PR_FALSE; } -nsresult +PRBool nsBlockFrame::PlaceLine(nsBlockReflowState& aState, - nsLineLayout& aLineLayout, - line_iterator aLine, - PRBool* aKeepReflowGoing, - PRBool aUpdateMaximumWidth) + nsLineLayout& aLineLayout, + line_iterator aLine, + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { - nsresult rv = NS_OK; - // Trim extra white-space from the line before placing the frames aLineLayout.TrimTrailingWhiteSpace(); @@ -4242,7 +4321,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; *aKeepReflowGoing = PR_FALSE; } - return rv; + return PR_TRUE; } aState.mY = newY; @@ -4376,7 +4455,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, break; } - return rv; + return PR_FALSE; } // Compute the line's max-element-size by adding into the raw value @@ -4484,7 +4563,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, } void -nsBlockFrame::PushLines(nsBlockReflowState& aState, +nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineList::iterator aLineBefore) { nsLineList::iterator overBegin(aLineBefore.next()); @@ -5405,6 +5484,39 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, #ifdef NOISY_FLOATER printf("end ReflowFloater %p, sized to %d,%d\n", floater, metrics.width, metrics.height); #endif + + // If the placeholder was continued and its first-in-flow was followed by a + //
, then cache the
's break type in aState.mFloaterBreakType so that + // the next frame after the placeholder can combine that break type with its own + nsIFrame* prevPlaceholder = nsnull; + aPlaceholder->GetPrevInFlow(&prevPlaceholder); + if (prevPlaceholder) { + // the break occurs only after the last continued placeholder + PRBool lastPlaceholder = PR_TRUE; + nsIFrame* next; + aPlaceholder->GetNextSibling(&next); + if (next) { + nsCOMPtr nextType; + next->GetFrameType(getter_AddRefs(nextType)); + if (nsLayoutAtoms::placeholderFrame == nextType) { + lastPlaceholder = PR_FALSE; + } + } + if (lastPlaceholder) { + // get the containing block of prevPlaceholder which is our prev-in-flow + if (mPrevInFlow) { + // get the break type of the last line in mPrevInFlow + line_iterator endLine = --((nsBlockFrame*)mPrevInFlow)->end_lines(); + PRUint8 breakType = endLine->GetBreakType(); + if ((NS_STYLE_CLEAR_LEFT == breakType) || + (NS_STYLE_CLEAR_RIGHT == breakType) || + (NS_STYLE_CLEAR_LEFT_AND_RIGHT == breakType)) { + aState.mFloaterBreakType = breakType; + } + } + else NS_ASSERTION(PR_FALSE, "no prev in flow"); + } + } return NS_OK; } diff --git a/mozilla/layout/html/base/src/nsBlockFrame.h b/mozilla/layout/html/base/src/nsBlockFrame.h index 318b095d81c..1ecf8b23b4e 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.h +++ b/mozilla/layout/html/base/src/nsBlockFrame.h @@ -333,11 +333,12 @@ protected: PRBool* aKeepReflowGoing, PRBool aDamageDirtyArea = PR_FALSE); - nsresult PlaceLine(nsBlockReflowState& aState, - nsLineLayout& aLineLayout, - line_iterator aLine, - PRBool* aKeepReflowGoing, - PRBool aUpdateMaximumWidth); + // Return PR_TRUE if aLine gets pushed. + PRBool PlaceLine(nsBlockReflowState& aState, + nsLineLayout& aLineLayout, + line_iterator aLine, + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth); /** * Mark |aLine| dirty, and, if necessary because of possible diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.cpp b/mozilla/layout/html/base/src/nsBlockReflowState.cpp index 0f10fcf420b..847a6eefe12 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowState.cpp @@ -66,7 +66,8 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mLastFloaterY(0), mPrevBottomMargin(), mLineNumber(0), - mFlags(0) + mFlags(0), + mFloaterBreakType(NS_STYLE_CLEAR_NONE) { const nsMargin& borderPadding = BorderPadding(); @@ -974,6 +975,13 @@ nsBlockReflowState::FlowAndPlaceFloater(nsFloaterCache* aFloaterCache, } } + // If the floater is continued, it will get the same x value as its prev-in-flow + nsRect prevInFlowRect(0,0,0,0); + nsIFrame* prevInFlow; + floater->GetPrevInFlow(&prevInFlow); + if (prevInFlow) { + prevInFlow->GetRect(prevInFlowRect); + } // Assign an x and y coordinate to the floater. Note that the x,y // coordinates are computed relative to the translation in the // spacemanager which means that the impacted region will be @@ -982,7 +990,7 @@ nsBlockReflowState::FlowAndPlaceFloater(nsFloaterCache* aFloaterCache, PRBool isLeftFloater; if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) { isLeftFloater = PR_TRUE; - region.x = mAvailSpaceRect.x; + region.x = (prevInFlow) ? prevInFlowRect.x : mAvailSpaceRect.x; } else { isLeftFloater = PR_FALSE; @@ -990,9 +998,7 @@ nsBlockReflowState::FlowAndPlaceFloater(nsFloaterCache* aFloaterCache, nsIFrame* prevInFlow; floater->GetPrevInFlow(&prevInFlow); if (prevInFlow) { - nsRect rect; - prevInFlow->GetRect(rect); - region.x = rect.x; + region.x = prevInFlowRect.x; } else if (!keepFloaterOnSameLine) { region.x = mAvailSpaceRect.XMost() - region.width; @@ -1022,6 +1028,11 @@ nsBlockReflowState::FlowAndPlaceFloater(nsFloaterCache* aFloaterCache, // Place the floater in the space manager if (okToAddRectRegion) { + // if the floater split, then take up all of the vertical height + if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && + (NS_UNCONSTRAINEDSIZE != mContentArea.height)) { + region.height += PR_MAX(region.height, mContentArea.height); + } #ifdef DEBUG nsresult rv = #endif diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.h b/mozilla/layout/html/base/src/nsBlockReflowState.h index c82f058c2e5..694b4230f9d 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.h +++ b/mozilla/layout/html/base/src/nsBlockReflowState.h @@ -256,6 +256,8 @@ public: #define BRS_LASTFLAG BRS_DAMAGECONSTRAINED PRInt16 mFlags; + + PRUint8 mFloaterBreakType; void SetFlag(PRUint32 aFlag, PRBool aValue) {