Bug 1336708: Don't reuse cached flex-item reflow measurements if the item's computed height has changed

This commit is contained in:
janekptacijarabaci 2018-04-24 20:21:41 +02:00 • committed by Roy Tam
commit 54d4d7e14a
2 changed files with 39 additions and 23 deletions

View file

@ -1761,31 +1761,50 @@ nsFlexContainerFrame::
/** /**
* A cached result for a measuring reflow. * A cached result for a measuring reflow.
* *
* Right now we only need to cache the available size, the height and the * Right now we only need to cache the available size and the computed height
* ascent. This can be extended later if needed. * for checking that the reflow input is valid, and the height and the ascent
* to be used. This can be extended later if needed.
* *
* The assumption here is that a given flex item measurement won't change until * The assumption here is that a given flex item measurement won't change until
* either the available size changes, or the flex container intrinsic size is * either the available size or computed height changes, or the flex container
* marked as dirty (due to a style or DOM change). * intrinsic size is marked as dirty (due to a style or DOM change).
*
* In particular the computed height may change between measuring reflows due to
* how the mIsFlexContainerMeasuringReflow flag affects size computation (see
* bug 1336708).
* *
* Caching it prevents us from doing exponential reflows in cases of deeply * Caching it prevents us from doing exponential reflows in cases of deeply
* nested flex and scroll frames. * nested flex and scroll frames.
* *
* We store them in the frame property table for simplicity. * We store them in the frame property table for simplicity.
*/ */
struct nsFlexContainerFrame::CachedMeasuringReflowResult class nsFlexContainerFrame::CachedMeasuringReflowResult
{ {
LogicalSize mAvailableSize; // Members that are part of the cache key:
const LogicalSize mAvailableSize;
const nscoord mComputedHeight;
// Members that are part of the cache value:
const nscoord mHeight; const nscoord mHeight;
const nscoord mAscent; const nscoord mAscent;
CachedMeasuringReflowResult(const LogicalSize& aAvailableSize, public:
nscoord aHeight, CachedMeasuringReflowResult(const ReflowInput& aReflowInput,
nscoord aAscent) const ReflowOutput& aDesiredSize)
: mAvailableSize(aAvailableSize) : mAvailableSize(aReflowInput.AvailableSize())
, mHeight(aHeight) , mComputedHeight(aReflowInput.ComputedHeight())
, mAscent(aAscent) , mHeight(aDesiredSize.Height())
, mAscent(aDesiredSize.BlockStartAscent())
{} {}
bool IsValidFor(const ReflowInput& aReflowInput) const {
return mAvailableSize == aReflowInput.AvailableSize() &&
mComputedHeight == aReflowInput.ComputedHeight();
}
nscoord Height() const { return mHeight; }
nscoord Ascent() const { return mAscent; }
}; };
NS_DECLARE_FRAME_PROPERTY_DELETABLE(CachedFlexMeasuringReflow, NS_DECLARE_FRAME_PROPERTY_DELETABLE(CachedFlexMeasuringReflow,
@ -1797,10 +1816,9 @@ nsFlexContainerFrame::MeasureAscentAndHeightForFlexItem(
nsPresContext* aPresContext, nsPresContext* aPresContext,
ReflowInput& aChildReflowInput) ReflowInput& aChildReflowInput)
{ {
const auto availableSize = aChildReflowInput.AvailableSize();
const FrameProperties props = aItem.Frame()->Properties(); const FrameProperties props = aItem.Frame()->Properties();
if (const auto* cachedResult = props.Get(CachedFlexMeasuringReflow())) { if (const auto* cachedResult = props.Get(CachedFlexMeasuringReflow())) {
if (cachedResult->mAvailableSize == availableSize) { if (cachedResult->IsValidFor(aChildReflowInput)) {
return *cachedResult; return *cachedResult;
} }
} }
@ -1827,9 +1845,7 @@ nsFlexContainerFrame::MeasureAscentAndHeightForFlexItem(
childDesiredSize, &aChildReflowInput, 0, 0, flags); childDesiredSize, &aChildReflowInput, 0, 0, flags);
auto result = auto result =
new CachedMeasuringReflowResult(availableSize, new CachedMeasuringReflowResult(aChildReflowInput, childDesiredSize);
childDesiredSize.Height(),
childDesiredSize.BlockStartAscent());
props.Set(CachedFlexMeasuringReflow(), result); props.Set(CachedFlexMeasuringReflow(), result);
return *result; return *result;
@ -1874,11 +1890,11 @@ nsFlexContainerFrame::
MeasureAscentAndHeightForFlexItem(aFlexItem, aPresContext, MeasureAscentAndHeightForFlexItem(aFlexItem, aPresContext,
childRIForMeasuringHeight); childRIForMeasuringHeight);
aFlexItem.SetAscent(reflowResult.mAscent); aFlexItem.SetAscent(reflowResult.Ascent());
// Subtract border/padding in vertical axis, to get _just_ // Subtract border/padding in vertical axis, to get _just_
// the effective computed value of the "height" property. // the effective computed value of the "height" property.
nscoord childDesiredHeight = reflowResult.mHeight - nscoord childDesiredHeight = reflowResult.Height() -
childRIForMeasuringHeight.ComputedPhysicalBorderPadding().TopBottom(); childRIForMeasuringHeight.ComputedPhysicalBorderPadding().TopBottom();
return std::max(0, childDesiredHeight); return std::max(0, childDesiredHeight);
@ -4049,7 +4065,7 @@ nsFlexContainerFrame::SizeItemInCrossAxis(
// so we don't bother with making aAxisTracker pick the cross-axis component // so we don't bother with making aAxisTracker pick the cross-axis component
// for us.) // for us.)
nscoord crossAxisBorderPadding = aItem.GetBorderPadding().TopBottom(); nscoord crossAxisBorderPadding = aItem.GetBorderPadding().TopBottom();
if (reflowResult.mHeight < crossAxisBorderPadding) { if (reflowResult.Height() < crossAxisBorderPadding) {
// Child's requested size isn't large enough for its border/padding! // Child's requested size isn't large enough for its border/padding!
// This is OK for the trivial nsFrame::Reflow() impl, but other frame // This is OK for the trivial nsFrame::Reflow() impl, but other frame
// classes should know better. So, if we get here, the child had better be // classes should know better. So, if we get here, the child had better be
@ -4062,10 +4078,10 @@ nsFlexContainerFrame::SizeItemInCrossAxis(
aItem.SetCrossSize(0); aItem.SetCrossSize(0);
} else { } else {
// (normal case) // (normal case)
aItem.SetCrossSize(reflowResult.mHeight - crossAxisBorderPadding); aItem.SetCrossSize(reflowResult.Height() - crossAxisBorderPadding);
} }
aItem.SetAscent(reflowResult.mAscent); aItem.SetAscent(reflowResult.Ascent());
} }
void void

View file

@ -56,7 +56,7 @@ public:
class FlexLine; class FlexLine;
class FlexboxAxisTracker; class FlexboxAxisTracker;
struct StrutInfo; struct StrutInfo;
struct CachedMeasuringReflowResult; class CachedMeasuringReflowResult;
// nsIFrame overrides // nsIFrame overrides
void Init(nsIContent* aContent, void Init(nsIContent* aContent,