Don't propagate the float's reflow status through places that expect an inline's reflow status, to avoid the assertion for which bug 563584 was filed. This separates pushing of floats from pushing of lines, and allows first-in-flow floats to be pushed to the next page without their associated line. (Bug 563584, patch 17) r=roc

This commit is contained in:
L. David Baron 2010-08-05 21:59:19 -07:00
parent f3f9dbebba
commit 867d31744c
5 changed files with 71 additions and 87 deletions

View File

@ -1029,7 +1029,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
if (!NS_FRAME_IS_FULLY_COMPLETE(state.mReflowStatus)) {
if (GetOverflowLines()) {
if (GetOverflowLines() || GetFloatContinuations()) {
state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
@ -3536,10 +3536,9 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
}
--aLine;
if (LINE_REFLOW_TRUNCATED == lineReflowStatus) {
// Push the line with the truncated float
PushTruncatedPlaceholderLine(aState, aLine, *aKeepReflowGoing);
}
NS_ASSERTION(lineReflowStatus != LINE_REFLOW_TRUNCATED,
"ReflowInlineFrame should never determine that a line "
"needs to go to the next page/column");
}
}
@ -3634,9 +3633,9 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
aFloatAvailableSpace = aState.GetFloatAvailableSpace();
} else {
// There's nowhere to retry placing the line. Just treat it as if
// we placed the float but it was truncated so we need this line
// to go to the next page/column.
// There's nowhere to retry placing the line, so we want to push
// it to the next page/column where its contents can fit not
// next to a float.
lineReflowStatus = LINE_REFLOW_TRUNCATED;
// Push the line that didn't fit
PushTruncatedPlaceholderLine(aState, aLine, *aKeepReflowGoing);
@ -3820,12 +3819,6 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
}
}
}
else if (NS_FRAME_IS_TRUNCATED(frameReflowStatus) &&
nsGkAtoms::placeholderFrame == aFrame->GetType()) {
// if the frame is a placeholder and was complete but truncated (and not at the top
// of page), the entire line will be pushed to give it another chance to not truncate.
*aLineReflowStatus = LINE_REFLOW_TRUNCATED;
}
if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
// Create a continuation for the incomplete frame. Note that the
@ -3833,13 +3826,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
nsIAtom* frameType = aFrame->GetType();
PRBool madeContinuation;
if (nsGkAtoms::placeholderFrame == frameType) {
nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(aFrame);
rv = SplitFloat(aState, placeholder->GetOutOfFlowFrame(), frameReflowStatus);
}
else {
rv = CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
}
rv = CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
NS_ENSURE_SUCCESS(rv, rv);
// Remember that the line has wrapped
@ -3915,10 +3902,8 @@ nsBlockFrame::SplitFloat(nsBlockReflowState& aState,
if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aFloatStatus))
aFloat->GetNextInFlow()->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
// Float continuations can only trigger overflow
NS_FRAME_SET_OVERFLOW_INCOMPLETE(aFloatStatus);
// Make sure the containing block knows about the float's status
NS_MergeReflowStatusInto(&aState.mReflowStatus, aFloatStatus);
// The containing block is now overflow-incomplete.
NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
if (aFloat->GetStyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) {
aState.mFloatManager->SetSplitLeftFloatAcrossBreak();
@ -5755,12 +5740,6 @@ nsBlockFrame::ReflowFloatContinuations(nsBlockReflowState& aState,
// Reflow
nsReflowStatus fStatus = NS_FRAME_COMPLETE;
aState.FlowAndPlaceFloat(f, fStatus);
if (!NS_FRAME_IS_FULLY_COMPLETE(fStatus)) {
rv = SplitFloat(aState, f, fStatus);
NS_ENSURE_SUCCESS(rv, rv);
NS_FRAME_SET_OVERFLOW_INCOMPLETE(fStatus);
}
NS_MergeReflowStatusInto(&aStatus, fStatus);
// Invalidate if there was a position or size change
nsRect rect = f->GetRect();

View File

@ -97,7 +97,9 @@ class nsIntervalSet;
* FloatContinuationProperty frame property that points to
* an nsFrameList. This list contains continuations for
* floats whose prev-in-flow is in the block's regular float
* list.
* list and first-in-flows of floats that did not fit, but
* whose placeholders are in the block or one of its
* prev-in-flows.
* -- In all these frame lists, if there are two frames for
* the same content appearing in the list, then the frames
* appear with the prev-in-flow before the next-in-flow.

View File

@ -571,6 +571,7 @@ nsBlockReflowState::AddFloat(nsLineLayout* aLineLayout,
mBlock->mFloats.AppendFrame(mBlock, aFloat);
}
// FIXME: Remove aReflowStatus parameter!
aReflowStatus = NS_FRAME_COMPLETE;
// Because we are in the middle of reflowing a placeholder frame
@ -596,12 +597,9 @@ nsBlockReflowState::AddFloat(nsLineLayout* aLineLayout,
(aLineLayout->LineIsEmpty() ||
mBlock->ComputeFloatWidth(*this, floatAvailableSpace, aFloat)
<= aAvailableWidth)) {
nsFloatManager::SavedState floatManagerState;
mFloatManager->PushState(&floatManagerState);
// And then place it
placed = FlowAndPlaceFloat(aFloat, aReflowStatus);
if (placed && !NS_FRAME_IS_TRUNCATED(aReflowStatus)) {
if (placed) {
// Pass on updated available space to the current inline reflow engine
nsFlowAreaRect floatAvailSpace = GetFloatAvailableSpace(mY);
nsRect availSpace(nsPoint(floatAvailSpace.mRect.x + BorderPadding().left,
@ -611,24 +609,6 @@ nsBlockReflowState::AddFloat(nsLineLayout* aLineLayout,
// Record this float in the current-line list
mCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat));
}
else {
if (placed) {
mFloatManager->PopState(&floatManagerState);
} else {
mFloatManager->AssertStateMatches(&floatManagerState);
}
if (IsAdjacentWithTop()) {
// Pushing the line to the next page won't give us any more space;
// therefore, we break.
NS_ASSERTION(aLineLayout->LineIsBreakable(),
"We can't get here unless forceFit is false");
aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
} else {
// Make sure we propagate the truncated status; this signals the
// block to push the line to the next page.
aReflowStatus |= NS_FRAME_TRUNCATED;
}
}
}
else {
// Always claim to be placed; we don't know whether we fit yet, so we
@ -683,6 +663,7 @@ FloatMarginWidth(const nsHTMLReflowState& aCBReflowState,
PRBool
nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat,
// FIXME: remove aReflowStatus
nsReflowStatus& aReflowStatus)
{
aReflowStatus = NS_FRAME_COMPLETE;
@ -740,6 +721,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat,
for (;;) {
if (floatAvailableSpace.mRect.height <= 0) {
// No space, nowhere to put anything.
PushFloatPastBreak(aFloat);
return PR_FALSE;
}
@ -845,22 +827,30 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat,
// where to break.
nsMargin floatMargin; // computed margin
PRBool pushedDown = mY != saveY;
nsReflowStatus reflowStatus;
mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat,
floatMargin, pushedDown, aReflowStatus);
floatMargin, pushedDown, reflowStatus);
if (aFloat->GetPrevInFlow())
floatMargin.top = 0;
if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus))
if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus))
floatMargin.bottom = 0;
// In the case that we're in columns and not splitting floats, we need
// to check here that the float's height fit, and if it didn't, bail.
// (This code is only for DISABLE_FLOAT_BREAKING_IN_COLUMNS .)
if (mContentArea.height != NS_UNCONSTRAINEDSIZE &&
adjustedAvailableSpace.height == NS_UNCONSTRAINEDSIZE &&
(!mReflowState.mFlags.mIsTopOfPage || !IsAdjacentWithTop() ||
pushedDown) &&
aFloat->GetSize().height + floatMargin.TopBottom() >
mContentArea.height - floatY) {
//
// Likewise, if none of the float fit, and it needs to be pushed in
// its entirety to the next page (NS_FRAME_IS_TRUNCATED), we need to
// do the same.
if ((mContentArea.height != NS_UNCONSTRAINEDSIZE &&
adjustedAvailableSpace.height == NS_UNCONSTRAINEDSIZE &&
(!mReflowState.mFlags.mIsTopOfPage || !IsAdjacentWithTop() ||
pushedDown) &&
aFloat->GetSize().height + floatMargin.TopBottom() >
mContentArea.height - floatY) ||
NS_FRAME_IS_TRUNCATED(reflowStatus)) {
PushFloatPastBreak(aFloat);
return PR_FALSE;
}
@ -891,7 +881,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat,
// calculate region
nsRect region = nsFloatManager::CalculateRegionFor(aFloat, floatMargin);
// if the float split, then take up all of the vertical height
if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) &&
if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus) &&
(NS_UNCONSTRAINEDSIZE != mContentArea.height)) {
region.height = NS_MAX(region.height, mContentArea.height - floatY);
}
@ -916,6 +906,10 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat,
mFloatManager->IncludeInDamage(top, bottom);
}
if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) {
mBlock->SplitFloat(*this, aFloat, reflowStatus);
}
#ifdef NOISY_FLOATMANAGER
nscoord tx, ty;
mFloatManager->GetTranslation(tx, ty);
@ -938,6 +932,32 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat,
return PR_TRUE;
}
void
nsBlockReflowState::PushFloatPastBreak(nsIFrame *aFloat)
{
// This ensures that we:
// * don't try to place later but smaller floats (which CSS says
// must have their tops below the top of this float)
// * don't waste much time trying to reflow this float again until
// after the break
if (aFloat->GetStyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) {
mFloatManager->SetPushedLeftFloatPastBreak();
} else {
NS_ABORT_IF_FALSE(aFloat->GetStyleDisplay()->mFloats ==
NS_STYLE_FLOAT_RIGHT,
"unexpected float value");
mFloatManager->SetPushedRightFloatPastBreak();
}
// Put the float on the float continuations list, even though it
// isn't actually a continuation.
nsresult rv = mBlock->StealFrame(mPresContext, aFloat);
NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame should succeed");
AppendFloatContinuation(aFloat);
NS_FRAME_SET_OVERFLOW_INCOMPLETE(mReflowStatus);
}
/**
* Place below-current-line floats.
*/
@ -959,22 +979,11 @@ nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList)
nsReflowStatus reflowStatus;
PRBool placed = FlowAndPlaceFloat(fc->mFloat, reflowStatus);
if (!placed || NS_FRAME_IS_TRUNCATED(reflowStatus)) {
if (!placed) {
// return before processing all of the floats, since the line will be pushed.
// FIXME: This seems like it should be handled elsewhere...
return PR_FALSE;
}
else if (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus)) {
// Create a continuation for the incomplete float
nsresult rv = mBlock->SplitFloat(*this, fc->mFloat, reflowStatus);
if (NS_FAILED(rv))
return PR_FALSE;
} else {
// XXX We could deal with truncated frames better by breaking before
// the associated placeholder
NS_WARN_IF_FALSE(!NS_FRAME_IS_TRUNCATED(reflowStatus),
"This situation currently leads to data not printing");
// Float is complete.
}
}
fc = fc->Next();
}

View File

@ -108,10 +108,15 @@ public:
nsIFrame* aFloat,
nscoord aAvailableWidth,
nsReflowStatus& aReflowStatus);
private:
PRBool CanPlaceFloat(nscoord aFloatWidth,
const nsFlowAreaRect& aFloatAvailableSpace);
public:
PRBool FlowAndPlaceFloat(nsIFrame* aFloat,
nsReflowStatus& aReflowStatus);
private:
void PushFloatPastBreak(nsIFrame* aFloat);
public:
PRBool PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aFloats);
// Returns the first coordinate >= aY that clears the

View File

@ -754,17 +754,6 @@ nsInlineFrame::ReflowInlineFrame(nsPresContext* aPresContext,
// Create a next-in-flow if needed.
if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
if (nsGkAtoms::placeholderFrame == aFrame->GetType()) {
nsBlockReflowState* blockRS = lineLayout->mBlockRS;
nsPlaceholderFrame* placeholder =
static_cast<nsPlaceholderFrame*>(aFrame);
rv = blockRS->mBlock->SplitFloat(*blockRS,
placeholder->GetOutOfFlowFrame(),
aStatus);
// Allow the parent to continue reflowing.
aStatus = NS_FRAME_COMPLETE;
return rv;
}
nsIFrame* newFrame;
rv = CreateNextInFlow(aPresContext, aFrame, newFrame);
if (NS_FAILED(rv)) {