Issue #1230 - Part 1: Fix Back-computing percentages for intrinsic sizing in Layout CSS-Grid

List of relevant patches applied:

1398537 part 2 - [css-multicol] Implement percentages for 'column-gap' (Gecko part).

1434478 part 1 - [css-grid] Stop back-computing percentage grid gaps when the percentage basis is indefinite. Treat them as zero sized instead.

1434478 part 2 - Stop back-computing percentage padding/margin when the percentage basis is indefinite. Treat them as zero sized instead.

1434478 part 3 - Remove IntrinsicISizeOffsetData::hPctPadding/hPctMargin members since they are now unused.

1434478 part 4 - Factor out constants like NS_UNCONSTRAINEDSIZE so they can be used in headers without needing nsIFrame.h (idempotent patch).

1434478 part 5 - Create nsLayoutUtils::ResolveToLength for resolving CSS <length-percentage> (idempotent patch).

1434478 part 6 - Propagate a percentage basis to nsIFrame::IntrinsicISizeOffsets for resolving padding/margin.

This is needed only for CSS Grid since in other cases we're only using IntrinsicISizeOffsets in the inline-axis and the percentage basis is always indefinite for *intrinsic sizing*. When calculating the intrinsic size of grid items in the grid container's block axis however, we do have a definite size for the grid area in the inline-axis and it should be used per: https://drafts.csswg.org/css-grid/#algo-overview "2. Next, the track sizing algorithm resolves the sizes of the grid rows, using the grid column sizes calculated in the previous step." (Percentage padding/margin for grid items is always resolved against the grid area's inline-size nowadays.)
This commit is contained in:
Gaming4JC 2019-09-28 23:36:05 -04:00 committed by Roy Tam
commit e4c397f0d9
16 changed files with 240 additions and 319 deletions

View file

@ -8,8 +8,8 @@
#include "nsGridContainerFrame.h"
#include <algorithm> // for std::stable_sort
#include <limits>
#include <stdlib.h> // for div()
#include "mozilla/CSSAlignUtils.h"
#include "mozilla/dom/GridBinding.h"
#include "mozilla/Function.h"
@ -127,43 +127,6 @@ ResolveToDefiniteSize(const nsStyleCoord& aCoord, nscoord aPercentBasis)
nsRuleNode::ComputeCoordPercentCalc(aCoord, aPercentBasis));
}
static bool
GetPercentSizeParts(const nsStyleCoord& aCoord, nscoord* aLength, float* aPercent)
{
switch (aCoord.GetUnit()) {
case eStyleUnit_Percent:
*aLength = 0;
*aPercent = aCoord.GetPercentValue();
return true;
case eStyleUnit_Calc: {
nsStyleCoord::Calc* calc = aCoord.GetCalcValue();
*aLength = calc->mLength;
*aPercent = calc->mPercent;
return true;
}
default:
return false;
}
}
static void
ResolvePercentSizeParts(const nsStyleCoord& aCoord, nscoord aPercentBasis,
nscoord* aLength, float* aPercent)
{
MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
if (aPercentBasis != NS_UNCONSTRAINEDSIZE) {
*aLength = std::max(nscoord(0),
nsRuleNode::ComputeCoordPercentCalc(aCoord,
aPercentBasis));
*aPercent = 0.0f;
return;
}
if (!GetPercentSizeParts(aCoord, aLength, aPercent)) {
*aLength = aCoord.ToLength();
*aPercent = 0.0f;
}
}
// Synthesize a baseline from a border box. For an alphabetical baseline
// this is the end edge of the border box. For a central baseline it's
// the center of the border box.
@ -1172,7 +1135,7 @@ struct nsGridContainerFrame::TrackSizingFunctions
return 1;
}
nscoord repeatTrackSize = 0;
// Note that the repeat() track size is included in |sum| in this loop.
// Note that one repeat() track size is included in |sum| in this loop.
nscoord sum = 0;
const nscoord percentBasis = aSize;
for (uint32_t i = 0; i < numTracks; ++i) {
@ -1189,54 +1152,31 @@ struct nsGridContainerFrame::TrackSizingFunctions
}
nscoord trackSize = ::ResolveToDefiniteSize(*coord, percentBasis);
if (i == mRepeatAutoStart) {
if (percentBasis != NS_UNCONSTRAINEDSIZE) {
// Use a minimum 1px for the repeat() track-size.
if (trackSize < AppUnitsPerCSSPixel()) {
trackSize = AppUnitsPerCSSPixel();
}
// Use a minimum 1px for the repeat() track-size.
if (trackSize < AppUnitsPerCSSPixel()) {
trackSize = AppUnitsPerCSSPixel();
}
repeatTrackSize = trackSize;
}
sum += trackSize;
}
nscoord gridGap;
float percentSum = 0.0f;
float gridGapPercent;
ResolvePercentSizeParts(aGridGap, percentBasis, &gridGap, &gridGapPercent);
nscoord gridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aSize);
if (numTracks > 1) {
// Add grid-gaps for all the tracks including the repeat() track.
sum += gridGap * (numTracks - 1);
percentSum = gridGapPercent * (numTracks - 1);
}
// Calculate the max number of tracks that fits without overflow.
nscoord available = maxFill != NS_UNCONSTRAINEDSIZE ? maxFill : aMinSize;
nscoord size = nsLayoutUtils::AddPercents(sum, percentSum);
if (available - size < 0) {
nscoord spaceToFill = available - sum;
if (spaceToFill <= 0) {
// "if any number of repetitions would overflow, then 1 repetition"
return 1;
}
uint32_t numRepeatTracks = 1;
bool exactFit = false;
while (true) {
sum += gridGap + repeatTrackSize;
percentSum += gridGapPercent;
nscoord newSize = nsLayoutUtils::AddPercents(sum, percentSum);
if (newSize <= size) {
// Adding more repeat-tracks won't make forward progress.
return numRepeatTracks;
}
size = newSize;
nscoord remaining = available - size;
exactFit = remaining == 0;
if (remaining >= 0) {
++numRepeatTracks;
}
if (remaining <= 0) {
break;
}
}
if (!exactFit && maxFill == NS_UNCONSTRAINEDSIZE) {
// Calculate the max number of tracks that fits without overflow.
div_t q = div(spaceToFill, repeatTrackSize + gridGap);
// The +1 here is for the one repeat track we already accounted for above.
uint32_t numRepeatTracks = q.quot + 1;
if (q.rem != 0 && maxFill == NS_UNCONSTRAINEDSIZE) {
// "Otherwise, if the grid container has a definite min size in
// the relevant axis, the number of repetitions is the largest possible
// positive integer that fulfills that minimum requirement."
@ -1882,13 +1822,6 @@ struct nsGridContainerFrame::Tracks
WritingMode aWM,
const LogicalSize& aContainerSize);
/**
* Return the intrinsic size by back-computing percentages as:
* IntrinsicSize = SumOfCoordSizes / (1 - SumOfPercentages).
*/
nscoord BackComputedIntrinsicSize(const TrackSizingFunctions& aFunctions,
const nsStyleCoord& aGridGap) const;
nscoord GridLineEdge(uint32_t aLine, GridLineSide aSide) const
{
if (MOZ_UNLIKELY(mSizes.IsEmpty())) {
@ -2198,11 +2131,10 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput
}
/**
* Calculate our track sizes. If the given aContentBox block-axis size is
* unconstrained, it is assigned to the resulting intrinsic block-axis size.
* Calculate our track sizes.
*/
void CalculateTrackSizes(const Grid& aGrid,
LogicalSize& aContentBox,
const LogicalSize& aContentBox,
SizingConstraint aConstraint);
/**
@ -2688,7 +2620,7 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::Grid
void
nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
const Grid& aGrid,
LogicalSize& aContentBox,
const LogicalSize& aContentBox,
SizingConstraint aConstraint)
{
mCols.Initialize(mColFunctions, mGridStyle->mGridColumnGap,
@ -2706,12 +2638,6 @@ nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
mRows.CalculateSizes(*this, mGridItems, mRowFunctions,
aContentBox.BSize(mWM), &GridArea::mRows,
aConstraint);
if (aContentBox.BSize(mWM) == NS_AUTOHEIGHT) {
aContentBox.BSize(mWM) =
mRows.BackComputedIntrinsicSize(mRowFunctions, mGridStyle->mGridRowGap);
mRows.mGridGap =
::ResolveToDefiniteSize(mGridStyle->mGridRowGap, aContentBox.BSize(mWM));
}
}
/**
@ -3813,7 +3739,7 @@ nsGridContainerFrame::Tracks::Initialize(
aFunctions.MinSizingFor(i),
aFunctions.MaxSizingFor(i));
}
mGridGap = ::ResolveToDefiniteSize(aGridGap, aContentBoxSize);
mGridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aContentBoxSize);
mContentBoxSize = aContentBoxSize;
}
@ -3894,8 +3820,7 @@ ContentContribution(const GridItemInfo& aGridItem,
PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
nscoord size = nsLayoutUtils::IntrinsicForAxis(axis, aRC, child, aConstraint,
aPercentageBasis,
aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED |
nsLayoutUtils::ADD_PERCENTS,
aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED,
aMinSizeClamp);
if (size == NS_INTRINSIC_WIDTH_UNKNOWN) {
// We need to reflow the child to find its BSize contribution.
@ -3932,15 +3857,7 @@ ContentContribution(const GridItemInfo& aGridItem,
LogicalSize availableSize(childWM, availISize, availBSize);
size = ::MeasuringReflow(child, aState.mReflowInput, aRC, availableSize,
cbSize, iMinSizeClamp, bMinSizeClamp);
nsIFrame::IntrinsicISizeOffsetData offsets = child->IntrinsicBSizeOffsets();
size += offsets.hMargin;
auto percent = offsets.hPctMargin;
if (availBSize == NS_UNCONSTRAINEDSIZE) {
// We always want to add in percent padding too, unless we already did so
// using a resolved column size above.
percent += offsets.hPctPadding;
}
size = nsLayoutUtils::AddPercents(size, percent);
size += child->GetLogicalUsedMargin(childWM).BStartEnd(childWM);
nscoord overflow = size - aMinSizeClamp;
if (MOZ_UNLIKELY(overflow > 0)) {
nscoord contentSize = child->ContentBSize(childWM);
@ -4045,6 +3962,10 @@ MinSize(const GridItemInfo& aGridItem,
return s;
}
if (aCache->mPercentageBasis.isNothing()) {
aCache->mPercentageBasis.emplace(aState.PercentageBasisFor(aAxis, aGridItem));
}
// https://drafts.csswg.org/css-grid/#min-size-auto
// This calculates the min-content contribution from either a definite
// min-width (or min-height depending on aAxis), or the "specified /
@ -4058,7 +3979,8 @@ MinSize(const GridItemInfo& aGridItem,
"baseline offset should be zero when not baseline-aligned");
nscoord sz = aGridItem.mBaselineOffset[aAxis] +
nsLayoutUtils::MinSizeContributionForAxis(axis, aRC, child,
nsLayoutUtils::MIN_ISIZE);
nsLayoutUtils::MIN_ISIZE,
*aCache->mPercentageBasis);
const nsStyleCoord& style = axis == eAxisHorizontal ? stylePos->mMinWidth
: stylePos->mMinHeight;
auto unit = style.GetUnit();
@ -4067,9 +3989,6 @@ 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,
@ -5097,36 +5016,6 @@ nsGridContainerFrame::Tracks::AlignJustifyContent(
MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
}
nscoord
nsGridContainerFrame::Tracks::BackComputedIntrinsicSize(
const TrackSizingFunctions& aFunctions,
const nsStyleCoord& aGridGap) const
{
// Sum up the current sizes (where percentage tracks were treated as 'auto')
// in 'size'.
nscoord size = 0;
for (size_t i = 0, len = mSizes.Length(); i < len; ++i) {
size += mSizes[i].mBase;
}
// Add grid-gap contributions to 'size' and calculate a 'percent' sum.
float percent = 0.0f;
size_t numTracks = mSizes.Length();
if (numTracks > 1) {
const size_t gridGapCount = numTracks - 1;
nscoord gridGapLength;
float gridGapPercent;
if (::GetPercentSizeParts(aGridGap, &gridGapLength, &gridGapPercent)) {
percent = gridGapCount * gridGapPercent;
} else {
gridGapLength = aGridGap.ToLength();
}
size += gridGapCount * gridGapLength;
}
return std::max(0, nsLayoutUtils::AddPercents(size, percent));
}
void
nsGridContainerFrame::LineRange::ToPositionAndLength(
const nsTArray<TrackSize>& aTrackSizes, nscoord* aPos, nscoord* aLength) const
@ -6292,7 +6181,7 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
LogicalSize computedSize(wm, computedISize, computedBSize);
nscoord consumedBSize = 0;
nscoord bSize;
nscoord bSize = 0;
if (!prevInFlow) {
Grid grid;
grid.PlaceGridItems(gridReflowInput, aReflowInput.ComputedMinSize(),
@ -6300,7 +6189,12 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
gridReflowInput.CalculateTrackSizes(grid, computedSize,
SizingConstraint::eNoConstraint);
bSize = computedSize.BSize(wm);
// Note: we can't use GridLineEdge here since we haven't calculated
// the rows' mPosition yet (happens in AlignJustifyContent below).
for (const auto& sz : gridReflowInput.mRows.mSizes) {
bSize += sz.mBase;
}
bSize += gridReflowInput.mRows.SumOfGridGaps();
} else {
consumedBSize = GetConsumedBSize();
gridReflowInput.InitializeForContinuation(this, consumedBSize);
@ -6699,8 +6593,14 @@ nsGridContainerFrame::IntrinsicISize(nsRenderingContext* aRenderingContext,
state.mCols.CalculateSizes(state, state.mGridItems, state.mColFunctions,
NS_UNCONSTRAINEDSIZE, &GridArea::mCols,
constraint);
return state.mCols.BackComputedIntrinsicSize(state.mColFunctions,
state.mGridStyle->mGridColumnGap);
state.mCols.mGridGap =
nsLayoutUtils::ResolveGapToLength(state.mGridStyle->mGridColumnGap,
NS_UNCONSTRAINEDSIZE);
nscoord length = 0;
for (const TrackSize& sz : state.mCols.mSizes) {
length += sz.mBase;
}
return length + state.mCols.SumOfGridGaps();
}
nscoord