Bug 1599159 - Break the child block frame reflow loop if there is a break-before. r=emilio

This patch is based on Mats Palmgren's idea bug 1599159 comment 0.

When a child block frame reports inline-break-before status after being
reflowed, its size in the returned reflow output shouldn't matter because the
parent is expected to push it to the next page/column [1].

Here is the detail of the debug assertion if the workaround in fieldset frame is
removed. In nsBlockReflowContext::ReflowBlock(), it sets mReflowOutput::BSize()
to 0xdeadbeef, which is a negative number in 32-bits integer. Without the early
break of the loop in nsBlockFrame::ReflowBlockFrame(), then later in the loop we
are going to pass the negative block-size to
BlockReflowState::GetFloatAvailableSpaceForBSize(), which triggers a `aBSize>=0`
assertion further down in the callstack in nsFloatManager::GetFlowArea().

layout/reftests/pagination/fieldset-00G.html can trigger the above assertion
after removing the workaround in fieldset frame.

I don't expect this patch will change the behavior even if there are some float
elements interacting with this block. Without the early breaking out of the
loop, we still end up in [1].

[1] https://searchfox.org/mozilla-central/rev/ace2c59e6c56b2dcba25af1aa8903a5e7f9a5857/layout/generic/nsBlockFrame.cpp#4026-4035

Differential Revision: https://phabricator.services.mozilla.com/D148815
This commit is contained in:
Ting-Yu Lin 2022-06-11 05:53:36 +00:00
Родитель d2534adbc1
Коммит f8731e5b3c
2 изменённых файлов: 6 добавлений и 5 удалений

Просмотреть файл

@ -481,14 +481,11 @@ void nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
// Propagate break-before from the legend to the fieldset.
if (legend->StyleDisplay()->BreakBefore() ||
aStatus.IsInlineBreakBefore()) {
// XXX(mats) setting a desired size shouldn't be necessary: bug 1599159.
aDesiredSize.SetSize(wm, LogicalSize(wm));
aStatus.SetInlineLineBreakBeforeAndReset();
return;
}
// Honor break-inside:avoid by breaking before instead.
if (MOZ_UNLIKELY(avoidBreakInside) && !aStatus.IsFullyComplete()) {
aDesiredSize.SetSize(wm, LogicalSize(wm));
aStatus.SetInlineLineBreakBeforeAndReset();
return;
}
@ -650,7 +647,6 @@ void nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
!aReflowInput.mFlags.mIsTopOfPage &&
availSize.BSize(wm) != NS_UNCONSTRAINEDSIZE) {
if (status.IsInlineBreakBefore() || !status.IsFullyComplete()) {
aDesiredSize.SetSize(wm, LogicalSize(wm));
aStatus.SetInlineLineBreakBeforeAndReset();
return;
}
@ -775,7 +771,6 @@ void nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
border.BEnd(wm) > 0 && aReflowInput.AvailableBSize() > border.BEnd(wm)) {
// Our end border doesn't fit but it should fit in the next column/page.
if (MOZ_UNLIKELY(avoidBreakInside)) {
aDesiredSize.SetSize(wm, LogicalSize(wm));
aStatus.SetInlineLineBreakBeforeAndReset();
return;
} else {

Просмотреть файл

@ -3907,6 +3907,12 @@ void nsBlockFrame::ReflowBlockFrame(BlockReflowState& aState,
clearance, aState.IsAdjacentWithTop(), aLine.get(),
*childReflowInput, frameReflowStatus, aState);
if (frameReflowStatus.IsInlineBreakBefore()) {
// No need to retry this loop if there is a break opportunity before the
// child block.
break;
}
// 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