From 76e1c016a32cb8cee6f5a0a0012fe512570da1eb Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Thu, 17 Aug 2017 20:56:20 +0200 Subject: CSS - Grid - transferred min-size contribution of percentage size grid item with an intrinsic ratio --- layout/base/nsLayoutUtils.cpp | 151 ++++++++++++++++++--- layout/base/nsLayoutUtils.h | 5 + layout/generic/nsGridContainerFrame.cpp | 68 ++++++++-- ...to-min-sizing-min-content-min-size-004-ref.html | 2 +- ...d-auto-min-sizing-transferred-size-004-ref.html | 2 +- ...ontent-min-sizing-transferred-size-004-ref.html | 2 +- 6 files changed, 199 insertions(+), 31 deletions(-) (limited to 'layout') diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 789c91f50..c8c91b251 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -4637,6 +4637,63 @@ GetPercentBSize(const nsStyleCoord& aStyle, return true; } +// Return true if aStyle can be resolved to a definite value and if so +// return that value in aResult. +static bool +GetDefiniteSize(const nsStyleCoord& aStyle, + nsIFrame* aFrame, + bool aIsInlineAxis, + const Maybe& aPercentageBasis, + nscoord* aResult) +{ + switch (aStyle.GetUnit()) { + case eStyleUnit_Coord: + *aResult = aStyle.GetCoordValue(); + return true; + case eStyleUnit_Percent: { + if (aPercentageBasis.isNothing()) { + return false; + } + auto wm = aFrame->GetWritingMode(); + nscoord pb = aIsInlineAxis ? aPercentageBasis.value().ISize(wm) + : aPercentageBasis.value().BSize(wm); + if (pb != NS_UNCONSTRAINEDSIZE) { + nscoord p = NSToCoordFloorClamped(pb * aStyle.GetPercentValue()); + *aResult = std::max(nscoord(0), p); + return true; + } + return false; + } + case eStyleUnit_Calc: { + nsStyleCoord::Calc* calc = aStyle.GetCalcValue(); + if (calc->mPercent != 0.0f) { + if (aPercentageBasis.isNothing()) { + return false; + } + auto wm = aFrame->GetWritingMode(); + nscoord pb = aIsInlineAxis ? aPercentageBasis.value().ISize(wm) + : aPercentageBasis.value().BSize(wm); + if (pb == NS_UNCONSTRAINEDSIZE) { + // XXXmats given that we're calculating an intrinsic size here, + // maybe we should back-compute the calc-size using AddPercents? + return false; + } + *aResult = std::max(0, calc->mLength + + NSToCoordFloorClamped(pb * calc->mPercent)); + } else { + *aResult = std::max(0, calc->mLength); + } + return true; + } + default: + return false; + } +} + +// +// NOTE: this function will be replaced by GetDefiniteSizeTakenByBoxSizing (bug 1363918). +// Please do not add new uses of this function. +// // Get the amount of vertical space taken out of aFrame's content area due to // its borders and paddings given the box-sizing value in aBoxSizing. We don't // get aBoxSizing from the frame because some callers want to compute this for @@ -4681,6 +4738,54 @@ GetBSizeTakenByBoxSizing(StyleBoxSizing aBoxSizing, return bSizeTakenByBoxSizing; } +// Get the amount of space taken out of aFrame's content area due to its +// borders and paddings given the box-sizing value in aBoxSizing. We don't +// get aBoxSizing from the frame because some callers want to compute this for +// specific box-sizing values. +// aIsInlineAxis is true if we're computing for aFrame's inline axis. +// aIgnorePadding is true if padding should be ignored. +static nscoord +GetDefiniteSizeTakenByBoxSizing(StyleBoxSizing aBoxSizing, + nsIFrame* aFrame, + bool aIsInlineAxis, + bool aIgnorePadding, + const Maybe& aPercentageBasis) +{ + nscoord sizeTakenByBoxSizing = 0; + if (MOZ_UNLIKELY(aBoxSizing == StyleBoxSizing::Border)) { + const bool isHorizontalAxis = + aIsInlineAxis == !aFrame->GetWritingMode().IsVertical(); + const nsStyleBorder* styleBorder = aFrame->StyleBorder(); + sizeTakenByBoxSizing = + isHorizontalAxis ? styleBorder->GetComputedBorder().LeftRight() + : styleBorder->GetComputedBorder().TopBottom(); + if (!aIgnorePadding) { + const nsStyleSides& stylePadding = aFrame->StylePadding()->mPadding; + const nsStyleCoord& pStart = + stylePadding.Get(isHorizontalAxis ? eSideLeft : eSideTop); + const nsStyleCoord& pEnd = + stylePadding.Get(isHorizontalAxis ? eSideRight : eSideBottom); + nscoord pad; + // XXXbz Calling GetPercentBSize on padding values looks bogus, since + // percent padding is always a percentage of the inline-size of the + // containing block. We should perhaps just treat non-absolute paddings + // here as 0 instead, except that in some cases the width may in fact be + // known. See bug 1231059. + if (GetDefiniteSize(pStart, aFrame, aIsInlineAxis, aPercentageBasis, &pad) || + (aPercentageBasis.isNothing() && + GetPercentBSize(pStart, aFrame, isHorizontalAxis, pad))) { + sizeTakenByBoxSizing += pad; + } + if (GetDefiniteSize(pEnd, aFrame, aIsInlineAxis, aPercentageBasis, &pad) || + (aPercentageBasis.isNothing() && + GetPercentBSize(pEnd, aFrame, isHorizontalAxis, pad))) { + sizeTakenByBoxSizing += pad; + } + } + } + return sizeTakenByBoxSizing; +} + // Handles only -moz-max-content and -moz-min-content, and // -moz-fit-content for min-width and max-width, since the others // (-moz-fit-content for width, and -moz-available) have no effect on @@ -4940,17 +5045,21 @@ AddStateBitToAncestors(nsIFrame* aFrame, nsFrameState aBit) } /* static */ nscoord -nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis, - nsRenderingContext* aRenderingContext, - nsIFrame* aFrame, - IntrinsicISizeType aType, - uint32_t aFlags, - nscoord aMarginBoxMinSizeClamp) +nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis, + nsRenderingContext* aRenderingContext, + nsIFrame* aFrame, + IntrinsicISizeType aType, + const Maybe& aPercentageBasis, + uint32_t aFlags, + nscoord aMarginBoxMinSizeClamp) { NS_PRECONDITION(aFrame, "null frame"); NS_PRECONDITION(aFrame->GetParent(), "IntrinsicForAxis called on frame not in tree"); NS_PRECONDITION(aType == MIN_ISIZE || aType == PREF_ISIZE, "bad type"); + MOZ_ASSERT(aFrame->GetParent()->Type() != LayoutFrameType::GridContainer || + aPercentageBasis.isSome(), + "grid layout should always pass a percentage basis"); const bool horizontalAxis = MOZ_LIKELY(aAxis == eAxisHorizontal); #ifdef DEBUG_INTRINSIC_WIDTH @@ -5014,6 +5123,7 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis, PhysicalAxis ourInlineAxis = aFrame->GetWritingMode().PhysicalAxis(eLogicalAxisInline); + const bool isInlineAxis = aAxis == ourInlineAxis; // If we have a specified width (or a specified 'min-width' greater // than the specified 'max-width', which works out to the same thing), // don't even bother getting the frame's intrinsic width, because in @@ -5043,7 +5153,7 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis, // constraint is the max-content contribution which we shouldn't clamp. aMarginBoxMinSizeClamp = NS_MAXSIZE; } - if (MOZ_UNLIKELY(aAxis != ourInlineAxis)) { + if (MOZ_UNLIKELY(!isInlineAxis)) { IntrinsicSize intrinsicSize = aFrame->GetIntrinsicSize(); const nsStyleCoord intrinsicBCoord = horizontalAxis ? intrinsicSize.width : intrinsicSize.height; @@ -5105,21 +5215,23 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis, NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE); nscoord bSizeTakenByBoxSizing = - GetBSizeTakenByBoxSizing(boxSizing, aFrame, horizontalAxis, - aFlags & IGNORE_PADDING); - + GetDefiniteSizeTakenByBoxSizing(boxSizing, aFrame, !isInlineAxis, + aFlags & IGNORE_PADDING, + aPercentageBasis); // NOTE: This is only the minContentSize if we've been passed MIN_INTRINSIC_ISIZE // (which is fine, because this should only be used inside a check for that flag). nscoord minContentSize = result; nscoord h; - if (GetAbsoluteCoord(styleBSize, h) || - GetPercentBSize(styleBSize, aFrame, horizontalAxis, h)) { + if (GetDefiniteSize(styleBSize, aFrame, !isInlineAxis, aPercentageBasis, &h) || + (aPercentageBasis.isNothing() && + GetPercentBSize(styleBSize, aFrame, horizontalAxis, h))) { h = std::max(0, h - bSizeTakenByBoxSizing); result = NSCoordMulDiv(h, ratioISize, ratioBSize); } - if (GetAbsoluteCoord(styleMaxBSize, h) || - GetPercentBSize(styleMaxBSize, aFrame, horizontalAxis, h)) { + if (GetDefiniteSize(styleMaxBSize, aFrame, !isInlineAxis, aPercentageBasis, &h) || + (aPercentageBasis.isNothing() && + GetPercentBSize(styleMaxBSize, aFrame, horizontalAxis, h))) { h = std::max(0, h - bSizeTakenByBoxSizing); nscoord maxISize = NSCoordMulDiv(h, ratioISize, ratioBSize); if (maxISize < result) { @@ -5130,8 +5242,9 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis, } } - if (GetAbsoluteCoord(styleMinBSize, h) || - GetPercentBSize(styleMinBSize, aFrame, horizontalAxis, h)) { + if (GetDefiniteSize(styleMinBSize, aFrame, !isInlineAxis, aPercentageBasis, &h) || + (aPercentageBasis.isNothing() && + GetPercentBSize(styleMinBSize, aFrame, horizontalAxis, h))) { h = std::max(0, h - bSizeTakenByBoxSizing); nscoord minISize = NSCoordMulDiv(h, ratioISize, ratioBSize); if (minISize > result) { @@ -5158,8 +5271,8 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis, } nsIFrame::IntrinsicISizeOffsetData offsets = - MOZ_LIKELY(aAxis == ourInlineAxis) ? aFrame->IntrinsicISizeOffsets() - : aFrame->IntrinsicBSizeOffsets(); + MOZ_LIKELY(isInlineAxis) ? aFrame->IntrinsicISizeOffsets() + : aFrame->IntrinsicBSizeOffsets(); nscoord contentBoxSize = result; result = AddIntrinsicSizeOffset(aRenderingContext, aFrame, offsets, aType, boxSizing, result, min, styleISize, @@ -5196,7 +5309,7 @@ nsLayoutUtils::IntrinsicForContainer(nsRenderingContext* aRenderingContext, // We want the size aFrame will contribute to its parent's inline-size. PhysicalAxis axis = aFrame->GetParent()->GetWritingMode().PhysicalAxis(eLogicalAxisInline); - return IntrinsicForAxis(axis, aRenderingContext, aFrame, aType, aFlags); + return IntrinsicForAxis(axis, aRenderingContext, aFrame, aType, Nothing(), aFlags); } /* static */ nscoord diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index d9580a3df..97fc410b0 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -1378,6 +1378,10 @@ public: * width, its 'width', 'min-width', and 'max-width' properties (or 'height' * variations if that's what matches aAxis) and its padding, border and margin * in the corresponding dimension. + * @param aPercentageBasis an optional percentage basis (in aFrame's WM). + * Pass NS_UNCONSTRAINEDSIZE if the basis is indefinite in either/both axes. + * If you pass Nothing() a percentage basis will be calculated from aFrame's + * ancestors' computed size in the relevant axis, if needed. * @param aMarginBoxMinSizeClamp make the result fit within this margin-box * size by reducing the *content size* (flooring at zero). This is used for: * https://drafts.csswg.org/css-grid/#min-size-auto @@ -1396,6 +1400,7 @@ public: nsRenderingContext* aRenderingContext, nsIFrame* aFrame, IntrinsicISizeType aType, + const mozilla::Maybe& aPercentageBasis = mozilla::Nothing(), uint32_t aFlags = 0, nscoord aMarginBoxMinSizeClamp = NS_MAXSIZE); /** diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index f3e28acc4..8f117b5ab 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -2092,6 +2092,18 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput LogicalSize& aContentBox, SizingConstraint aConstraint); + /** + * Return the percentage basis for a grid item in its writing-mode. + * If aAxis is eLogicalAxisInline then we return NS_UNCONSTRAINEDSIZE in + * both axes since we know all track sizes are indefinite at this point + * (we calculate column sizes before row sizes). Otherwise, assert that + * column sizes are known and calculate the size for aGridItem.mArea.mCols + * and use NS_UNCONSTRAINEDSIZE in the other axis. + * @param aAxis the axis we're currently calculating track sizes for + */ + LogicalSize PercentageBasisFor(LogicalAxis aAxis, + const GridItemInfo& aGridItem) const; + /** * Return the containing block for a grid item occupying aArea. */ @@ -3734,18 +3746,20 @@ MeasuringReflow(nsIFrame* aChild, * the child's margin-box) in aAxis. */ static nscoord -ContentContribution(const GridItemInfo& aGridItem, - const GridReflowInput& aState, - nsRenderingContext* aRC, - WritingMode aCBWM, - LogicalAxis aAxis, - IntrinsicISizeType aConstraint, - nscoord aMinSizeClamp = NS_MAXSIZE, - uint32_t aFlags = 0) +ContentContribution(const GridItemInfo& aGridItem, + const GridReflowInput& aState, + nsRenderingContext* aRC, + WritingMode aCBWM, + LogicalAxis aAxis, + const Maybe& aPercentageBasis, + IntrinsicISizeType aConstraint, + nscoord aMinSizeClamp = NS_MAXSIZE, + uint32_t aFlags = 0) { nsIFrame* child = aGridItem.mFrame; PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis)); nscoord size = nsLayoutUtils::IntrinsicForAxis(axis, aRC, child, aConstraint, + aPercentageBasis, aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED | nsLayoutUtils::ADD_PERCENTS, aMinSizeClamp); @@ -3815,6 +3829,10 @@ struct CachedIntrinsicSizes Maybe mMinSize; Maybe mMinContentContribution; Maybe mMaxContentContribution; + + // The item's percentage basis for intrinsic sizing purposes. + Maybe mPercentageBasis; + // "if the grid item spans only grid tracks that have a fixed max track // sizing function, its automatic minimum size in that dimension is // further clamped to less than or equal to the size necessary to fit its @@ -3835,7 +3853,11 @@ MinContentContribution(const GridItemInfo& aGridItem, if (aCache->mMinContentContribution.isSome()) { return aCache->mMinContentContribution.value(); } + if (aCache->mPercentageBasis.isNothing()) { + aCache->mPercentageBasis.emplace(aState.PercentageBasisFor(aAxis, aGridItem)); + } nscoord s = ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, + aCache->mPercentageBasis, nsLayoutUtils::MIN_ISIZE, aCache->mMinSizeClamp); aCache->mMinContentContribution.emplace(s); @@ -3853,7 +3875,11 @@ MaxContentContribution(const GridItemInfo& aGridItem, if (aCache->mMaxContentContribution.isSome()) { return aCache->mMaxContentContribution.value(); } + if (aCache->mPercentageBasis.isNothing()) { + aCache->mPercentageBasis.emplace(aState.PercentageBasisFor(aAxis, aGridItem)); + } nscoord s = ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, + aCache->mPercentageBasis, nsLayoutUtils::PREF_ISIZE, aCache->mMinSizeClamp); aCache->mMaxContentContribution.emplace(s); @@ -3907,7 +3933,11 @@ MinSize(const GridItemInfo& aGridItem, child->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)) { // Now calculate the "content size" part and return whichever is smaller. MOZ_ASSERT(unit != eStyleUnit_Enumerated || sz == NS_UNCONSTRAINEDSIZE); + if (aCache->mPercentageBasis.isNothing()) { + aCache->mPercentageBasis.emplace(aState.PercentageBasisFor(aAxis, aGridItem)); + } sz = std::min(sz, ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, + aCache->mPercentageBasis, nsLayoutUtils::MIN_ISIZE, aCache->mMinSizeClamp, nsLayoutUtils::MIN_INTRINSIC_ISIZE)); @@ -4721,7 +4751,8 @@ nsGridContainerFrame::Tracks::FindUsedFlexFraction( const GridItemInfo& item = aGridItems[iter.GridItemIndex()]; if (item.mState[mAxis] & ItemState::eIsFlexing) { // XXX optimize: bug 1194446 - nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis, + auto pb = Some(aState.PercentageBasisFor(mAxis, item)); + nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis, pb, nsLayoutUtils::PREF_ISIZE); if (spaceToFill <= 0) { continue; @@ -5052,6 +5083,25 @@ nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos( } } +LogicalSize +nsGridContainerFrame::GridReflowInput::PercentageBasisFor( + LogicalAxis aAxis, + const GridItemInfo& aGridItem) const +{ + auto wm = aGridItem.mFrame->GetWritingMode(); + if (aAxis == eLogicalAxisInline) { + return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + } + // Note: for now, we only resolve transferred percentages to row sizing. + // We may need to adjust these assertions once we implement bug 1300366. + MOZ_ASSERT(mCols.mCanResolveLineRangeSize); + MOZ_ASSERT(!mRows.mCanResolveLineRangeSize); + nscoord colSize = aGridItem.mArea.mCols.ToLength(mCols.mSizes); + nscoord rowSize = NS_UNCONSTRAINEDSIZE; + return !wm.IsOrthogonalTo(mWM) ? + LogicalSize(wm, colSize, rowSize) : LogicalSize(wm, rowSize, colSize); +} + LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockFor(const GridArea& aArea) const { diff --git a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004-ref.html b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004-ref.html index fc74cd214..caef8b031 100644 --- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004-ref.html +++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004-ref.html @@ -36,7 +36,7 @@ var rowtest = [ "min-width:80%; max-height:20px", "min-width:50%", "margin-left: 50px; width:50%" ]; var results = [ -"0/2px", "0/2px", "0/4px", "0/2px", "0/2px", "0/2px", "24px/2px", "20px/2px", "20px/2px", "24px/2px", "24px/52px" +"0/2px", "0/2px", "0/4px", "0/2px", "0/2px", "0/2px", "12px/2px", "20px/2px", "20px/2px", "24px/2px", "24px/52px" ]; var item_height = [ "0", "0", "0", "0", "0", "0", "12px", "20px", "20px", "24px", "312px" diff --git a/layout/reftests/css-grid/grid-auto-min-sizing-transferred-size-004-ref.html b/layout/reftests/css-grid/grid-auto-min-sizing-transferred-size-004-ref.html index c5d8a68ff..36a2d4920 100644 --- a/layout/reftests/css-grid/grid-auto-min-sizing-transferred-size-004-ref.html +++ b/layout/reftests/css-grid/grid-auto-min-sizing-transferred-size-004-ref.html @@ -36,7 +36,7 @@ var rowtest = [ "min-width:80%; max-height:20px", "min-width:50%", "margin-left: 50px; width:50%" ]; var results = [ -"0/2px", "0/2px", "0/4px", "0/2px", "0/2px", "0/2px", "24px/2px", "20px/2px", "20px/2px", "24px/2px", "24px/52px" +"0/2px", "0/2px", "0/4px", "0/2px", "0/2px", "0/2px", "12px/2px", "20px/2px", "20px/2px", "24px/2px", "24px/52px" ]; var item_height = [ "0", "0", "0", "0", "0", "0", "12px", "20px", "20px", "24px", "312px" diff --git a/layout/reftests/css-grid/grid-min-content-min-sizing-transferred-size-004-ref.html b/layout/reftests/css-grid/grid-min-content-min-sizing-transferred-size-004-ref.html index 5fa60b3b5..04d047b83 100644 --- a/layout/reftests/css-grid/grid-min-content-min-sizing-transferred-size-004-ref.html +++ b/layout/reftests/css-grid/grid-min-content-min-sizing-transferred-size-004-ref.html @@ -36,7 +36,7 @@ var rowtest = [ "min-width:80%; max-height:20px", "min-width:50%", "margin-left: 50px; width:50%" ]; var results = [ -"0/2px", "0/2px", "0/4px", "0/2px", "0/2px", "0/2px", "24px/2px", "20px/2px", "20px/2px", "24px/2px", "24px/52px" +"0/2px", "0/2px", "0/4px", "0/2px", "0/2px", "0/2px", "12px/2px", "20px/2px", "20px/2px", "24px/2px", "312px/52px" ]; var item_height = [ "0", "0", "0", "0", "0", "0", "12px", "20px", "20px", "24px", "312px" -- cgit v1.2.3