Bug 538194 patch 2 - Redo block reflow below floats when the height of a block that does not intersect floats pushes it into the way of other floats. r=roc

This adds an additional retry loop in block reflow that we can only
trigger when reflowing a block formatting context (replacedBlock
non-null).  It can retry in two different ways, either with a narrower
width but at the same vertical position (when
ReplacedBlockFitsInAvailSpace is still true) or at a new vertical
position (which is treated as a form of clearance).

Fortunately we don't have to worry about margins collapsing *through*
such a boundary since we're dealing with a new block formatting context.

Note that Chromium passes all of the new bfc-displace-* tests, although
it moves the block formating context down unnecessarily in
bfc-shrink-1.html (which we do neither before nor after the patch),
though agrees with the width we have after the patch (but not before the
patch).
This commit is contained in:
L. David Baron 2015-08-02 21:03:09 -07:00
parent 88915cde1f
commit 1e265de04a
18 changed files with 664 additions and 25 deletions

View File

@ -3261,14 +3261,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
}
}
aLine->SetLineIsImpactedByFloat(false);
// Here aState.mBCoord is the block-start border-edge of the block.
// Compute the available space for the block
nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
#ifdef REALLY_NOISY_REFLOW
printf("setting line %p isImpacted to %s\n",
aLine.get(), floatAvailableSpace.mHasFloats?"true":"false");
#endif
aLine->SetLineIsImpactedByFloat(floatAvailableSpace.mHasFloats);
WritingMode wm = aState.mReflowState.GetWritingMode();
LogicalRect availSpace(wm);
aState.ComputeBlockAvailSpace(frame, display, floatAvailableSpace,
@ -3304,14 +3301,13 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
}
// Now put the block-dir coordinate back to the start of the
// block-start-margin + clearance, and flow the block.
// block-start-margin + clearance.
aState.mBCoord -= bStartMargin;
availSpace.BStart(wm) -= bStartMargin;
if (NS_UNCONSTRAINEDSIZE != availSpace.BSize(wm)) {
availSpace.BSize(wm) += bStartMargin;
}
// Reflow the block into the available space
// construct the html reflow state for the block. ReflowBlock
// will initialize it
nsHTMLReflowState
@ -3320,17 +3316,120 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance();
nsFloatManager::SavedState floatManagerState;
if (mayNeedRetry) {
blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
aState.mFloatManager->PushState(&floatManagerState);
} else if (!applyBStartMargin) {
blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance;
}
nsReflowStatus frameReflowStatus;
do {
if (floatAvailableSpace.mHasFloats) {
// Set if floatAvailableSpace.mHasFloats is true for any
// iteration of the loop.
aLine->SetLineIsImpactedByFloat(true);
}
nsReflowStatus frameReflowStatus = NS_FRAME_COMPLETE;
brc.ReflowBlock(availSpace, applyBStartMargin, aState.mPrevBEndMargin,
clearance, aState.IsAdjacentWithTop(),
aLine.get(), blockHtmlRS, frameReflowStatus, aState);
// We might need to store into mDiscoveredClearance later if it's
// currently null; we want to overwrite any writes that
// brc.ReflowBlock() below does, so we need to remember now
// whether it's empty.
const bool shouldStoreClearance =
aState.mReflowState.mDiscoveredClearance &&
!*aState.mReflowState.mDiscoveredClearance;
// Reflow the block into the available space
if (mayNeedRetry || replacedBlock) {
aState.mFloatManager->PushState(&floatManagerState);
}
if (mayNeedRetry) {
blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
} else if (!applyBStartMargin) {
blockHtmlRS.mDiscoveredClearance =
aState.mReflowState.mDiscoveredClearance;
}
frameReflowStatus = NS_FRAME_COMPLETE;
brc.ReflowBlock(availSpace, applyBStartMargin, aState.mPrevBEndMargin,
clearance, aState.IsAdjacentWithTop(),
aLine.get(), blockHtmlRS, frameReflowStatus, aState);
// Now the block has a height. Using that height, get the
// available space again and call ComputeBlockAvailSpace again.
// If ComputeBlockAvailSpace gives a different result, we need to
// reflow again.
if (!replacedBlock) {
break;
}
LogicalRect oldFloatAvailableSpaceRect(floatAvailableSpace.mRect);
floatAvailableSpace = aState.GetFloatAvailableSpaceForBSize(
aState.mBCoord + bStartMargin,
brc.GetMetrics().Height(),
&floatManagerState);
NS_ASSERTION(floatAvailableSpace.mRect.BStart(wm) ==
oldFloatAvailableSpaceRect.BStart(wm),
"yikes");
// Restore the height to the position of the next band.
floatAvailableSpace.mRect.BSize(wm) =
oldFloatAvailableSpaceRect.BSize(wm);
if (!AvailableSpaceShrunk(wm, oldFloatAvailableSpaceRect,
floatAvailableSpace.mRect)) {
break;
}
bool advanced = false;
if (!aState.ReplacedBlockFitsInAvailSpace(replacedBlock,
floatAvailableSpace)) {
// Advance to the next band.
nscoord newBCoord = aState.mBCoord;
if (aState.AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) {
advanced = true;
}
// ClearFloats might be able to advance us further once we're there.
aState.mBCoord =
aState.ClearFloats(newBCoord, NS_STYLE_CLEAR_NONE, replacedBlock);
// Start over with a new available space rect at the new height.
floatAvailableSpace =
aState.GetFloatAvailableSpaceWithState(aState.mBCoord,
&floatManagerState);
}
LogicalRect oldAvailSpace(availSpace);
aState.ComputeBlockAvailSpace(frame, display, floatAvailableSpace,
replacedBlock != nullptr, availSpace);
if (!advanced && availSpace.IsEqualEdges(oldAvailSpace)) {
break;
}
// We need another reflow.
aState.mFloatManager->PopState(&floatManagerState);
if (!treatWithClearance && !applyBStartMargin &&
aState.mReflowState.mDiscoveredClearance) {
// We set shouldStoreClearance above to record only the first
// frame that requires clearance.
if (shouldStoreClearance) {
*aState.mReflowState.mDiscoveredClearance = frame;
}
aState.mPrevChild = frame;
// Exactly what we do now is flexible since we'll definitely be
// reflowed.
return;
}
if (advanced) {
// We're pushing down the border-box, so we don't apply margin anymore.
// This should never cause us to move up since the call to
// GetFloatAvailableSpaceForBSize above included the margin.
applyBStartMargin = false;
bStartMargin = 0;
treatWithClearance = true; // avoid hitting test above
clearance = 0;
}
blockHtmlRS.~nsHTMLReflowState();
new (&blockHtmlRS) nsHTMLReflowState(aState.mPresContext,
aState.mReflowState, frame,
availSpace.Size(wm).ConvertTo(
frame->GetWritingMode(), wm));
} while (true);
if (mayNeedRetry && clearanceFrame) {
aState.mFloatManager->PopState(&floatManagerState);

View File

@ -257,6 +257,10 @@ nsBlockReflowContext::ReflowBlock(const LogicalRect& aSpace,
aFrameRS.AvailableBSize() -= mBStartMargin.get() + aClearance;
}
}
} else {
// nsBlockFrame::ReflowBlock might call us multiple times with
// *different* values of aApplyBStartMargin.
mBStartMargin.Zero();
}
nscoord tI = 0, tB = 0;

View File

@ -46,14 +46,14 @@ asserts(2) skip-if(!cocoaWidget) HTTP(..) == 10209-3.html 10209-3-ref.html # Ass
== 25888-2r.html 25888-2r-ref.html
== 25888-3l.html 25888-3l-ref.html
== 25888-3r.html 25888-3r-ref.html
fails == 25888-1l-block.html 25888-1l-ref.html # Bug 25888
fails != 25888-1l-block.html 25888-1l-notref.html # Bug 25888
fails == 25888-1r-block.html 25888-1r-ref.html # Bug 25888
fails != 25888-1r-block.html 25888-1r-notref.html # Bug 25888
fails == 25888-2l-block.html 25888-2l-ref.html # Bug 25888
fails == 25888-2r-block.html 25888-2r-ref.html # Bug 25888
fails == 25888-3l-block.html 25888-3l-ref.html # Bug 25888
fails == 25888-3r-block.html 25888-3r-ref.html # Bug 25888
== 25888-1l-block.html 25888-1l-ref.html
!= 25888-1l-block.html 25888-1l-notref.html
== 25888-1r-block.html 25888-1r-ref.html
!= 25888-1r-block.html 25888-1r-notref.html
== 25888-2l-block.html 25888-2l-ref.html
== 25888-2r-block.html 25888-2r-ref.html
== 25888-3l-block.html 25888-3l-ref.html
== 25888-3r-block.html 25888-3r-ref.html
skip-if(B2G||Mulet) == 28811-1a.html 28811-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
fuzzy-if(gtkWidget,6,26200) == 28811-1b.html 28811-1-ref.html # Bug 1128229
skip-if(B2G||Mulet) == 28811-2a.html 28811-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop

View File

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
float: left;
width: 250px;
margin-top: 7px;
height: 13px; /* fits exactly */
background: fuchsia;
}
</style>
<div class="contain">
<div class="float" style="width: 100px"></div>
<div class="bfc"></div>
<div class="float" style="width: 200px"></div>
</div>

View File

@ -0,0 +1,37 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#floats">
<meta name="assert" content="The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself.">
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
overflow: hidden;
width: 250px;
margin-top: 7px;
height: 13px; /* fits exactly */
margin-bottom: 20px;
background: fuchsia;
}
</style>
<div class="contain">
<div class="float" style="width: 100px"></div>
<div class="float" style="width: 200px"></div>
<div class="bfc"></div>
</div>

View File

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
float: left;
clear: left;
width: 250px;
height: 14px; /* one pixel too tall to fit next to first float */
background: fuchsia;
}
</style>
<div class="contain">
<div class="float" style="width: 100px"></div>
<div class="float" style="width: 200px"></div>
<div class="bfc"></div>
</div>

View File

@ -0,0 +1,36 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#floats">
<meta name="assert" content="The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself.">
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
overflow: hidden;
width: 250px;
margin-top: 7px;
height: 14px; /* one pixel too tall to fit next to first float */
margin-bottom: 20px;
background: fuchsia;
}
</style>
<div class="contain">
<div class="float" style="width: 100px"></div>
<div class="float" style="width: 200px"></div>
<div class="bfc"></div>
</div>

View File

@ -0,0 +1,40 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.block {
height: 10px;
background: aqua;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
float: left;
width: 250px;
margin-top: 7px;
height: 13px; /* fits exactly */
background: fuchsia;
}
</style>
<div class="contain">
<div class="block"></div>
<div class="float" style="width: 100px"></div>
<div class="bfc"></div>
<div class="float" style="width: 200px"></div>
</div>

View File

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#floats">
<meta name="assert" content="The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself.">
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.block {
height: 10px;
background: aqua;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
overflow: hidden;
width: 250px;
margin-top: 7px;
height: 13px; /* fits exactly */
margin-bottom: 20px;
background: fuchsia;
}
</style>
<div class="contain">
<div class="block"></div>
<div class="float" style="width: 100px"></div>
<div class="float" style="width: 200px"></div>
<div class="bfc"></div>
</div>

View File

@ -0,0 +1,40 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.block {
height: 10px;
background: aqua;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
float: left;
clear: left;
width: 250px;
height: 14px; /* one pixel too tall to fit next to first float */
background: fuchsia;
}
</style>
<div class="contain">
<div class="block"></div>
<div class="float" style="width: 100px"></div>
<div class="float" style="width: 200px"></div>
<div class="bfc"></div>
</div>

View File

@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#floats">
<meta name="assert" content="The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself.">
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.block {
height: 10px;
background: aqua;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
overflow: hidden;
width: 250px;
margin-top: 7px;
height: 14px; /* one pixel too tall to fit next to first float */
margin-bottom: 20px;
background: fuchsia;
}
</style>
<div class="contain">
<div class="block"></div>
<div class="float" style="width: 100px"></div>
<div class="float" style="width: 200px"></div>
<div class="bfc"></div>
</div>

View File

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
float: left;
width: 250px;
height: 20px; /* fits exactly */
background: fuchsia;
}
</style>
<div class="contain">
<div class="float" style="width: 100px; margin-top: 7px;"></div>
<div class="bfc" style="margin-top: 7px"></div>
<div class="float" style="width: 200px"></div>
</div>

View File

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#floats">
<meta name="assert" content="The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself.">
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.margin {
margin-top: 3px;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
overflow: hidden;
width: 250px;
margin-top: 7px; /* collapses */
height: 20px; /* fits exactly */
margin-bottom: 20px;
background: fuchsia;
}
</style>
<div class="contain">
<div class="margin">
<div class="float" style="width: 100px"></div>
<div class="float" style="width: 200px"></div>
<div class="bfc"></div>
</div>
</div>

View File

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
float: left;
clear: left;
width: 250px;
height: 21px; /* one pixel too tall to fit next to first float */
background: fuchsia;
}
</style>
<div class="contain">
<div class="float" style="width: 100px; margin-top: 3px"></div>
<div class="float" style="width: 200px"></div>
<div class="bfc"></div>
</div>

View File

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#floats">
<meta name="assert" content="The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself.">
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.margin {
margin-top: 3px;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
overflow: hidden;
width: 250px;
margin-top: 7px; /* does not collapse, due to clearance */
height: 21px; /* fits exactly */
margin-bottom: 20px;
background: fuchsia;
}
</style>
<div class="contain">
<div class="margin">
<div class="float" style="width: 100px"></div>
<div class="float" style="width: 200px"></div>
<div class="bfc"></div>
</div>
</div>

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#floats">
<meta name="assert" content="The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself.">
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
float: right;
width: 200px;
height: 50px;
background: fuchsia;
}
</style>
<div class="contain">
<div class="bfc"></div>
<div class="float" style="width: 100px"></div>
<div class="float" style="width: 200px"></div>
</div>

View File

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<title>Test of block formatting context displacement by floats</title>
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#floats">
<meta name="assert" content="The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself.">
<style>
.contain {
border: medium solid;
width: 400px;
height: 400px;
background: yellow;
}
.float {
float: left;
clear: left;
height: 20px;
background: blue;
}
.bfc {
overflow: hidden;
height: 50px;
background: fuchsia;
}
</style>
<div class="contain">
<div class="float" style="width: 100px"></div>
<div class="float" style="width: 200px"></div>
<div class="bfc"></div>
</div>

View File

@ -37,6 +37,14 @@ fails == 345369-2.html 345369-2-ref.html
== float-in-rtl-4c.html float-in-rtl-4-ref.html
== float-in-rtl-4d.html float-in-rtl-4-ref.html
== bfc-displace-1a.html bfc-displace-1a-ref.html
== bfc-displace-1b.html bfc-displace-1b-ref.html
== bfc-displace-2a.html bfc-displace-2a-ref.html
== bfc-displace-2b.html bfc-displace-2b-ref.html
== bfc-displace-3a.html bfc-displace-3a-ref.html
== bfc-displace-3b.html bfc-displace-3b-ref.html
== bfc-shrink-1.html bfc-shrink-1-ref.html
# Testcases that involve vertical writing mode.
#
# XXX The default-preferences setting here can be removed after the