--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -3,19 +3,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* rendering object for CSS "display: grid | inline-grid" */
#include "nsGridContainerFrame.h"
-#include <algorithm> // for std::stable_sort
#include <functional>
#include <limits>
+#include <stdlib.h> // for div()
#include "gfxContext.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/CSSAlignUtils.h"
#include "mozilla/CSSOrderAwareFrameIterator.h"
#include "mozilla/dom/GridBinding.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/Maybe.h"
@@ -119,52 +119,16 @@ ResolveToDefiniteSize(const nsStyleCoord
{
MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
if (::IsPercentOfIndefiniteSize(aCoord, aPercentBasis)) {
return nscoord(0);
}
return std::max(nscoord(0), aCoord.ComputeCoordPercentCalc(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),
- aCoord.ComputeCoordPercentCalc(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.
// https://drafts.csswg.org/css-align-3/#synthesize-baselines
// For a 'first baseline' the measure is from the border-box start edge and
// for a 'last baseline' the measure is from the border-box end edge.
static nscoord
SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup,
@@ -925,81 +889,58 @@ struct nsGridContainerFrame::TrackSizing
const uint32_t numTracks = mMinSizingFunctions.Length();
MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track");
nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize;
if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) {
// "Otherwise, the specified track list repeats only once."
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) {
// "treating each track as its max track sizing function if that is
// definite or as its minimum track sizing function otherwise"
// https://drafts.csswg.org/css-grid/#valdef-repeat-auto-fill
const auto& maxCoord = mMaxSizingFunctions[i];
const auto* coord = &maxCoord;
if (!coord->IsCoordPercentCalcUnit()) {
coord = &mMinSizingFunctions[i];
if (!coord->IsCoordPercentCalcUnit()) {
return 1;
}
}
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."
++numRepeatTracks; // one more to ensure the grid is at least min-size
}
// Clamp the number of repeat tracks so that the last line <= kMaxLine.
// (note that |numTracks| already includes one repeat() track)
const uint32_t maxRepeatTracks = nsStyleGridLine::kMaxLine - numTracks;
@@ -1634,23 +1575,16 @@ struct nsGridContainerFrame::Tracks
/**
* Apply 'align/justify-content', whichever is relevant for this axis.
* https://drafts.csswg.org/css-align-3/#propdef-align-content
*/
void AlignJustifyContent(const nsStylePosition* aStyle,
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())) {
// https://drafts.csswg.org/css-grid/#grid-definition
// "... the explicit grid still contains one grid line in each axis."
MOZ_ASSERT(aLine == 0, "We should only resolve line 1 in an empty grid");
return nscoord(0);
}
@@ -1944,21 +1878,20 @@ struct MOZ_STACK_CLASS nsGridContainerFr
// Copy in the computed grid info state bit
if (mSharedGridData->mGenerateComputedGridInfo) {
aGridContainerFrame->AddStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
}
}
/**
- * 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);
/**
* 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
@@ -2434,17 +2367,17 @@ struct MOZ_STACK_CLASS nsGridContainerFr
*/
uint32_t mExplicitGridOffsetCol;
uint32_t mExplicitGridOffsetRow;
};
void
nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
const Grid& aGrid,
- LogicalSize& aContentBox,
+ const LogicalSize& aContentBox,
SizingConstraint aConstraint)
{
mCols.Initialize(mColFunctions, mGridStyle->mGridColumnGap,
aGrid.mGridColEnd, aContentBox.ISize(mWM));
mRows.Initialize(mRowFunctions, mGridStyle->mGridRowGap,
aGrid.mGridRowEnd, aContentBox.BSize(mWM));
mCols.CalculateSizes(*this, mGridItems, mColFunctions,
@@ -2452,22 +2385,16 @@ nsGridContainerFrame::GridReflowInput::C
aConstraint);
mCols.AlignJustifyContent(mGridStyle, mWM, aContentBox);
// Column positions and sizes are now final.
mCols.mCanResolveLineRangeSize = true;
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));
- }
}
/**
* (XXX share this utility function with nsFlexContainerFrame at some point)
*
* Helper for BuildDisplayList, to implement this special-case for grid
* items from the spec:
* The painting order of grid items is exactly the same as inline blocks,
@@ -3559,17 +3486,17 @@ nsGridContainerFrame::Tracks::Initialize
aFunctions.NumExplicitTracks());
mSizes.SetLength(aNumTracks);
PodZero(mSizes.Elements(), mSizes.Length());
for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
mStateUnion |= mSizes[i].Initialize(aContentBoxSize,
aFunctions.MinSizingFor(i),
aFunctions.MaxSizingFor(i));
}
- mGridGap = ::ResolveToDefiniteSize(aGridGap, aContentBoxSize);
+ mGridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aContentBoxSize);
mContentBoxSize = aContentBoxSize;
}
/**
* Reflow aChild in the given aAvailableSize.
*/
static nscoord
MeasuringReflow(nsIFrame* aChild,
@@ -4829,46 +4756,16 @@ nsGridContainerFrame::Tracks::AlignJusti
roundingError -= 1;
spacing += 1;
}
pos += sz.mBase + spacing;
}
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
{
MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
"expected a definite LineRange");
MOZ_ASSERT(mStart < mEnd);
nscoord startPos = aTrackSizes[mStart].mPosition;
@@ -6046,25 +5943,30 @@ nsGridContainerFrame::Reflow(nsPresConte
RemoveStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
}
const nscoord computedBSize = aReflowInput.ComputedBSize();
const nscoord computedISize = aReflowInput.ComputedISize();
const WritingMode& wm = gridReflowInput.mWM;
LogicalSize computedSize(wm, computedISize, computedBSize);
nscoord consumedBSize = 0;
- nscoord bSize;
+ nscoord bSize = 0;
if (!prevInFlow) {
Grid grid;
grid.PlaceGridItems(gridReflowInput, aReflowInput.ComputedMinSize(),
computedSize, aReflowInput.ComputedMaxSize());
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 = ConsumedBSize(wm);
gridReflowInput.InitializeForContinuation(this, consumedBSize);
const uint32_t numRows = gridReflowInput.mRows.mSizes.Length();
bSize = gridReflowInput.mRows.GridLineEdge(numRows,
GridLineSide::eAfterGridGap);
}
if (computedBSize == NS_AUTOHEIGHT) {
@@ -6477,18 +6379,24 @@ nsGridContainerFrame::IntrinsicISize(gfx
}
state.mCols.Initialize(state.mColFunctions, state.mGridStyle->mGridColumnGap,
grid.mGridColEnd, NS_UNCONSTRAINEDSIZE);
auto constraint = aType == nsLayoutUtils::MIN_ISIZE ?
SizingConstraint::eMinContent : SizingConstraint::eMaxContent;
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
nsGridContainerFrame::GetMinISize(gfxContext* aRC)
{
DISPLAY_MIN_WIDTH(this, mCachedMinISize);
if (mCachedMinISize == NS_INTRINSIC_WIDTH_UNKNOWN) {
mCachedMinISize = IntrinsicISize(aRC, nsLayoutUtils::MIN_ISIZE);