From 0fe4f86e06899264ac00334a44f7f87d8ad49c11 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 22 Dec 2016 18:30:38 +1100 Subject: [PATCH] Bug 1322843 part 2 - Conditionally keep some floats in InlinePrefISizeData::ForceBreak. r=dbaron This patch makes ForceBreak() partially clear floats according to the break type of the coming block. MozReview-Commit-ID: 71Gl9lBoTJ5 --HG-- extra : rebase_source : 5ca01565f607241df0c63a7cd64c35ac7ff7648f --- layout/generic/nsBlockFrame.cpp | 7 ++++- layout/generic/nsFrame.cpp | 56 ++++++++++++++++++++++++++++++--- layout/generic/nsIFrame.h | 20 +++++++++++- 3 files changed, 76 insertions(+), 7 deletions(-) diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 78809fe8b65f..fd23f8b7f517 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -838,9 +838,14 @@ nsBlockFrame::GetPrefISize(nsRenderingContext *aRenderingContext) AutoNoisyIndenter lineindent(gNoisyIntrinsic); #endif if (line->IsBlock()) { + StyleClear breakType; if (!data.mLineIsEmpty || BlockCanIntersectFloats(line->mFirstChild)) { - data.ForceBreak(); + breakType = StyleClear::Both; + } else { + breakType = line->mFirstChild-> + StyleDisplay()->PhysicalBreakType(data.mLineContainerWM); } + data.ForceBreak(breakType); data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, line->mFirstChild, nsLayoutUtils::PREF_ISIZE); data.ForceBreak(); diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index f78511d66aff..78ac40a6108b 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4512,9 +4512,17 @@ nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth) } void -nsIFrame::InlinePrefISizeData::ForceBreak() +nsIFrame::InlinePrefISizeData::ForceBreak(StyleClear aBreakType) { - if (mFloats.Length() != 0) { + MOZ_ASSERT(aBreakType == StyleClear::None || + aBreakType == StyleClear::Both || + aBreakType == StyleClear::Left || + aBreakType == StyleClear::Right, + "Must be a physical break type"); + + // If this force break is not clearing any float, we can leave all the + // floats to the next force break. + if (mFloats.Length() != 0 && aBreakType != StyleClear::None) { // preferred widths accumulated for floats that have already // been cleared past nscoord floats_done = 0, @@ -4522,11 +4530,12 @@ nsIFrame::InlinePrefISizeData::ForceBreak() // been cleared past floats_cur_left = 0, floats_cur_right = 0; + const WritingMode wm = mLineContainerWM; for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) { const FloatInfo& floatInfo = mFloats[i]; const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay(); - StyleClear breakType = floatDisp->PhysicalBreakType(mLineContainerWM); + StyleClear breakType = floatDisp->PhysicalBreakType(wm); if (breakType == StyleClear::Left || breakType == StyleClear::Right || breakType == StyleClear::Both) { @@ -4543,7 +4552,7 @@ nsIFrame::InlinePrefISizeData::ForceBreak() } } - StyleFloat floatStyle = floatDisp->PhysicalFloats(mLineContainerWM); + StyleFloat floatStyle = floatDisp->PhysicalFloats(wm); nscoord& floats_cur = floatStyle == StyleFloat::Left ? floats_cur_left : floats_cur_right; nscoord floatWidth = floatInfo.Width(); @@ -4560,7 +4569,44 @@ nsIFrame::InlinePrefISizeData::ForceBreak() mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, floats_done); - mFloats.Clear(); + if (aBreakType == StyleClear::Both) { + mFloats.Clear(); + } else { + // If the break type does not clear all floats, it means there may + // be some floats whose isize should contribute to the intrinsic + // isize of the next line. The code here scans the current mFloats + // and keeps floats which are not cleared by this break. Note that + // floats may be cleared directly or indirectly. See below. + nsTArray newFloats; + MOZ_ASSERT(aBreakType == StyleClear::Left || + aBreakType == StyleClear::Right, + "Other values should have been handled in other branches"); + StyleFloat clearFloatType = + aBreakType == StyleClear::Left ? StyleFloat::Left : StyleFloat::Right; + // Iterate the array in reverse so that we can stop when there are + // no longer any floats we need to keep. See below. + for (FloatInfo& floatInfo : Reversed(mFloats)) { + const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay(); + if (floatDisp->PhysicalFloats(wm) != clearFloatType) { + newFloats.AppendElement(floatInfo); + } else { + // This is a float on the side that this break directly clears + // which means we're not keeping it in mFloats. However, if + // this float clears floats on the opposite side (via a value + // of either 'both' or one of 'left'/'right'), any remaining + // (earlier) floats on that side would be indirectly cleared + // as well. Thus, we should break out of this loop and stop + // considering earlier floats to be kept in mFloats. + StyleClear floatBreakType = floatDisp->PhysicalBreakType(wm); + if (floatBreakType != aBreakType && + floatBreakType != StyleClear::None) { + break; + } + } + } + newFloats.Reverse(); + mFloats = Move(newFloats); + } } mCurrentLine = diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 383e684f42bc..7d1efbaae12c 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -2020,11 +2020,29 @@ public: }; struct InlinePrefISizeData : public InlineIntrinsicISizeData { + typedef mozilla::StyleClear StyleClear; + InlinePrefISizeData() : mLineIsEmpty(true) {} - void ForceBreak(); + /** + * Finish the current line and start a new line. + * + * @param aBreakType controls whether isize of floats are considered + * and what floats are kept for the next line: + * * |None| skips handling floats, which means no floats are + * removed, and isizes of floats are not considered either. + * * |Both| takes floats into consideration when computing isize + * of the current line, and removes all floats after that. + * * |Left| and |Right| do the same as |Both| except that they only + * remove floats on the given side, and any floats on the other + * side that are prior to a float on the given side that has a + * 'clear' property that clears them. + * All other values of StyleClear must be converted to the four + * physical values above for this function. + */ + void ForceBreak(StyleClear aBreakType = StyleClear::Both); // The default implementation for nsIFrame::AddInlinePrefISize. void DefaultAddInlinePrefISize(nscoord aISize);