summaryrefslogtreecommitdiffstats
path: root/layout/generic
diff options
context:
space:
mode:
Diffstat (limited to 'layout/generic')
-rw-r--r--layout/generic/nsGridContainerFrame.cpp661
1 files changed, 383 insertions, 278 deletions
diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp
index 3a2d5ad1d..59f58f268 100644
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -198,7 +198,7 @@ struct nsGridContainerFrame::TrackSize
eMaxContentMinSizing = 0x4,
eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing,
eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing,
- // 0x8 is unused, feel free to take it!
+ eModified = 0x8,
eAutoMaxSizing = 0x10,
eMinContentMaxSizing = 0x20,
eMaxContentMaxSizing = 0x40,
@@ -211,6 +211,7 @@ struct nsGridContainerFrame::TrackSize
eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2,
eBreakBefore = 0x800,
eFitContent = 0x1000,
+ eInfinitelyGrowable = 0x2000,
};
StateBits Initialize(nscoord aPercentageBasis,
@@ -856,6 +857,8 @@ struct nsGridContainerFrame::GridItemInfo
// Return true if we should apply Automatic Minimum Size to this item.
// https://drafts.csswg.org/css-grid/#min-size-auto
+ // @note the caller should also check that the item spans at least one track
+ // that has a min track sizing function that is 'auto' before applying it.
bool ShouldApplyAutoMinSize(WritingMode aContainerWM,
LogicalAxis aContainerAxis,
nscoord aPercentageBasis) const
@@ -915,6 +918,12 @@ nsGridContainerFrame::GridItemInfo::Dump() const
if (state & ItemState::eIsFlexing) {
printf("flexing ");
}
+ if (state & ItemState::eApplyAutoMinSize) {
+ printf("auto-min-size ");
+ }
+ if (state & ItemState::eClampMarginBoxMinSize) {
+ printf("clamp ");
+ }
if (state & ItemState::eFirstBaseline) {
printf("first baseline %s-alignment ",
(state & ItemState::eSelfBaseline) ? "self" : "content");
@@ -1091,6 +1100,7 @@ private:
const nsTArray<nsString>& mRepeatAutoLineNameListBefore;
const nsTArray<nsString>& mRepeatAutoLineNameListAfter;
// The index of the repeat(auto-fill/fit) track, or zero if there is none.
+ // Relative to mExplicitGridOffset (repeat tracks are explicit by definition).
const uint32_t mRepeatAutoStart;
// The (hypothetical) index of the last such repeat() track.
const uint32_t mRepeatAutoEnd;
@@ -1101,6 +1111,7 @@ private:
// generates one track (making mRepeatEndDelta == 0).
const uint32_t mTemplateLinesEnd;
// True if there is a specified repeat(auto-fill/fit) track.
+ // Indexed relative to mExplicitGridOffset + mRepeatAutoStart.
const bool mHasRepeatAuto;
};
@@ -1340,15 +1351,9 @@ struct nsGridContainerFrame::Tracks
nscoord aContentBoxSize);
/**
- * Return true if aRange spans at least one track with an intrinsic sizing
- * function and does not span any tracks with a <flex> max-sizing function.
- * @param aRange the span of tracks to check
- * @param aState will be set to the union of the state bits of all the spanned
- * tracks, unless a flex track is found - then it only contains
- * the union of the tracks up to and including the flex track.
+ * Return the union of the state bits for the tracks in aRange.
*/
- bool HasIntrinsicButNoFlexSizingInRange(const LineRange& aRange,
- TrackSize::StateBits* aState) const;
+ TrackSize::StateBits StateBitsForRange(const LineRange& aRange) const;
// Some data we collect for aligning baseline-aligned items.
struct ItemBaselineData
@@ -1383,6 +1388,62 @@ struct nsGridContainerFrame::Tracks
*/
void AlignBaselineSubtree(const GridItemInfo& aGridItem) const;
+ enum class TrackSizingPhase
+ {
+ eIntrinsicMinimums,
+ eContentBasedMinimums,
+ eMaxContentMinimums,
+ eIntrinsicMaximums,
+ eMaxContentMaximums,
+ };
+
+ // Some data we collect on each item for Step 2 of the Track Sizing Algorithm
+ // in ResolveIntrinsicSize below.
+ struct Step2ItemData final
+ {
+ uint32_t mSpan;
+ TrackSize::StateBits mState;
+ LineRange mLineRange;
+ nscoord mMinSize;
+ nscoord mMinContentContribution;
+ nscoord mMaxContentContribution;
+ nsIFrame* mFrame;
+ static bool IsSpanLessThan(const Step2ItemData& a, const Step2ItemData& b)
+ {
+ return a.mSpan < b.mSpan;
+ }
+
+ template<TrackSizingPhase phase>
+ nscoord SizeContributionForPhase() const
+ {
+ switch (phase) {
+ case TrackSizingPhase::eIntrinsicMinimums:
+ case TrackSizingPhase::eIntrinsicMaximums:
+ return mMinSize;
+ case TrackSizingPhase::eContentBasedMinimums:
+ return mMinContentContribution;
+ case TrackSizingPhase::eMaxContentMinimums:
+ case TrackSizingPhase::eMaxContentMaximums:
+ return mMaxContentContribution;
+ }
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
+ }
+ };
+
+ using FitContentClamper =
+ function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
+
+ // Helper method for ResolveIntrinsicSize.
+ template<TrackSizingPhase phase>
+ bool GrowSizeForSpanningItems(nsTArray<Step2ItemData>::iterator aIter,
+ const nsTArray<Step2ItemData>::iterator aEnd,
+ nsTArray<uint32_t>& aTracks,
+ nsTArray<TrackSize>& aPlan,
+ nsTArray<TrackSize>& aItemPlan,
+ TrackSize::StateBits aSelector,
+ const FitContentClamper& aClamper = nullptr,
+ bool aNeedInfinitelyGrowableFlag = false);
+
/**
* Resolve Intrinsic Track Sizes.
* http://dev.w3.org/csswg/css-grid/#algo-content
@@ -1405,66 +1466,117 @@ struct nsGridContainerFrame::Tracks
SizingConstraint aConstraint,
const LineRange& aRange,
const GridItemInfo& aGridItem);
+
+ // Helper method that returns the track size to use in §11.5.1.2
+ // https://drafts.csswg.org/css-grid/#extra-space
+ template<TrackSizingPhase phase> static
+ nscoord StartSizeInDistribution(const TrackSize& aSize)
+ {
+ switch (phase) {
+ case TrackSizingPhase::eIntrinsicMinimums:
+ case TrackSizingPhase::eContentBasedMinimums:
+ case TrackSizingPhase::eMaxContentMinimums:
+ return aSize.mBase;
+ case TrackSizingPhase::eIntrinsicMaximums:
+ case TrackSizingPhase::eMaxContentMaximums:
+ if (aSize.mLimit == NS_UNCONSTRAINEDSIZE) {
+ return aSize.mBase;
+ }
+ return aSize.mLimit;
+ }
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
+ }
+
/**
* Collect the tracks which are growable (matching aSelector) into
* aGrowableTracks, and return the amount of space that can be used
- * to grow those tracks. Specifically, we return aAvailableSpace minus
- * the sum of mBase's (and corresponding grid gaps) in aPlan (clamped to 0)
- * for the tracks in aRange, or zero when there are no growable tracks.
- * @note aPlan[*].mBase represents a planned new base or limit.
+ * to grow those tracks. This method implements CSS Grid §11.5.1.2.
+ * https://drafts.csswg.org/css-grid/#extra-space
*/
- nscoord CollectGrowable(nscoord aAvailableSpace,
- const nsTArray<TrackSize>& aPlan,
- const LineRange& aRange,
- TrackSize::StateBits aSelector,
- nsTArray<uint32_t>& aGrowableTracks) const
+ template<TrackSizingPhase phase>
+ nscoord CollectGrowable(nscoord aAvailableSpace,
+ const LineRange& aRange,
+ TrackSize::StateBits aSelector,
+ nsTArray<uint32_t>& aGrowableTracks) const
{
MOZ_ASSERT(aAvailableSpace > 0, "why call me?");
nscoord space = aAvailableSpace - mGridGap * (aRange.Extent() - 1);
const uint32_t start = aRange.mStart;
const uint32_t end = aRange.mEnd;
for (uint32_t i = start; i < end; ++i) {
- const TrackSize& sz = aPlan[i];
- space -= sz.mBase;
+ const TrackSize& sz = mSizes[i];
+ space -= StartSizeInDistribution<phase>(sz);
if (space <= 0) {
return 0;
}
- if ((sz.mState & aSelector) && !sz.IsFrozen()) {
+ if (sz.mState & aSelector) {
aGrowableTracks.AppendElement(i);
}
}
return aGrowableTracks.IsEmpty() ? 0 : space;
}
- void SetupGrowthPlan(nsTArray<TrackSize>& aPlan,
- const nsTArray<uint32_t>& aTracks) const
+ template<TrackSizingPhase phase>
+ void InitializeItemPlan(nsTArray<TrackSize>& aItemPlan,
+ const nsTArray<uint32_t>& aTracks) const
{
for (uint32_t track : aTracks) {
- aPlan[track] = mSizes[track];
+ auto& plan = aItemPlan[track];
+ const TrackSize& sz = mSizes[track];
+ plan.mBase = StartSizeInDistribution<phase>(sz);
+ bool unlimited = sz.mState & TrackSize::eInfinitelyGrowable;
+ plan.mLimit = unlimited ? NS_UNCONSTRAINEDSIZE : sz.mLimit;
+ plan.mState = sz.mState;
}
}
- void CopyPlanToBase(const nsTArray<TrackSize>& aPlan,
- const nsTArray<uint32_t>& aTracks)
+ template<TrackSizingPhase phase>
+ void InitializePlan(nsTArray<TrackSize>& aPlan) const
{
- for (uint32_t track : aTracks) {
- MOZ_ASSERT(mSizes[track].mBase <= aPlan[track].mBase);
- mSizes[track].mBase = aPlan[track].mBase;
+ for (size_t i = 0, len = aPlan.Length(); i < len; ++i) {
+ auto& plan = aPlan[i];
+ const auto& sz = mSizes[i];
+ plan.mBase = StartSizeInDistribution<phase>(sz);
+ MOZ_ASSERT(phase == TrackSizingPhase::eMaxContentMaximums ||
+ !(sz.mState & TrackSize::eInfinitelyGrowable),
+ "forgot to reset the eInfinitelyGrowable bit?");
+ plan.mState = sz.mState;
}
}
- void CopyPlanToLimit(const nsTArray<TrackSize>& aPlan,
- const nsTArray<uint32_t>& aTracks)
+ template<TrackSizingPhase phase>
+ void CopyPlanToSize(const nsTArray<TrackSize>& aPlan,
+ bool aNeedInfinitelyGrowableFlag = false)
{
- for (uint32_t track : aTracks) {
- MOZ_ASSERT(mSizes[track].mLimit == NS_UNCONSTRAINEDSIZE ||
- mSizes[track].mLimit <= aPlan[track].mBase);
- mSizes[track].mLimit = aPlan[track].mBase;
+ for (size_t i = 0, len = mSizes.Length(); i < len; ++i) {
+ const auto& plan = aPlan[i];
+ MOZ_ASSERT(plan.mBase >= 0);
+ auto& sz = mSizes[i];
+ switch (phase) {
+ case TrackSizingPhase::eIntrinsicMinimums:
+ case TrackSizingPhase::eContentBasedMinimums:
+ case TrackSizingPhase::eMaxContentMinimums:
+ sz.mBase = plan.mBase;
+ break;
+ case TrackSizingPhase::eIntrinsicMaximums:
+ if (plan.mState & TrackSize::eModified) {
+ if (sz.mLimit == NS_UNCONSTRAINEDSIZE &&
+ aNeedInfinitelyGrowableFlag) {
+ sz.mState |= TrackSize::eInfinitelyGrowable;
+ }
+ sz.mLimit = plan.mBase;
+ }
+ break;
+ case TrackSizingPhase::eMaxContentMaximums:
+ if (plan.mState & TrackSize::eModified) {
+ sz.mLimit = plan.mBase;
+ }
+ sz.mState &= ~TrackSize::eInfinitelyGrowable;
+ break;
+ }
}
}
- using FitContentClamper =
- function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
/**
* Grow the planned size for tracks in aGrowableTracks up to their limit
* and then freeze them (all aGrowableTracks must be unfrozen on entry).
@@ -1524,12 +1636,13 @@ struct nsGridContainerFrame::Tracks
* assumed that aPlan have no aSkipFlag set for tracks in aGrowableTracks
* on entry to this method.
*/
- uint32_t MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
- uint32_t aNumGrowable,
- const nsTArray<uint32_t>& aGrowableTracks,
- TrackSize::StateBits aMinSizingSelector,
- TrackSize::StateBits aMaxSizingSelector,
- TrackSize::StateBits aSkipFlag) const
+ static uint32_t
+ MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
+ uint32_t aNumGrowable,
+ const nsTArray<uint32_t>& aGrowableTracks,
+ TrackSize::StateBits aMinSizingSelector,
+ TrackSize::StateBits aMaxSizingSelector,
+ TrackSize::StateBits aSkipFlag)
{
bool foundOneSelected = false;
bool foundOneGrowable = false;
@@ -1559,41 +1672,60 @@ struct nsGridContainerFrame::Tracks
}
/**
- * Increase the planned size for tracks in aGrowableTracks that match
- * aSelector (or all tracks if aSelector is zero) beyond their limit.
+ * Mark all tracks in aGrowableTracks with an eSkipGrowUnlimited bit if
+ * they *shouldn't* grow unlimited in §11.5.1.2.3 "Distribute space beyond
+ * growth limits" https://drafts.csswg.org/css-grid/#extra-space
+ * Return the number of tracks that are still growable.
+ */
+ template<TrackSizingPhase phase>
+ static uint32_t
+ MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
+ const nsTArray<uint32_t>& aGrowableTracks,
+ TrackSize::StateBits aSelector)
+ {
+ uint32_t numGrowable = aGrowableTracks.Length();
+ if (phase == TrackSizingPhase::eIntrinsicMaximums ||
+ phase == TrackSizingPhase::eMaxContentMaximums) {
+ // "when handling any intrinsic growth limit: all affected tracks"
+ return numGrowable;
+ }
+ MOZ_ASSERT(aSelector == (aSelector & TrackSize::eIntrinsicMinSizing) &&
+ (aSelector & TrackSize::eMaxContentMinSizing),
+ "Should only get here for track sizing steps 2.1 to 2.3");
+ // Note that eMaxContentMinSizing is always included. We do those first:
+ numGrowable = MarkExcludedTracks(aPlan, numGrowable, aGrowableTracks,
+ TrackSize::eMaxContentMinSizing,
+ TrackSize::eMaxContentMaxSizing,
+ TrackSize::eSkipGrowUnlimited1);
+ // Now mark min-content/auto min-sizing tracks if requested.
+ auto minOrAutoSelector = aSelector & ~TrackSize::eMaxContentMinSizing;
+ if (minOrAutoSelector) {
+ numGrowable = MarkExcludedTracks(aPlan, numGrowable, aGrowableTracks,
+ minOrAutoSelector,
+ TrackSize::eIntrinsicMaxSizing,
+ TrackSize::eSkipGrowUnlimited2);
+ }
+ return numGrowable;
+ }
+
+ /**
+ * Increase the planned size for tracks in aGrowableTracks that aren't
+ * marked with a eSkipGrowUnlimited flag beyond their limit.
* This implements the "Distribute space beyond growth limits" step in
* https://drafts.csswg.org/css-grid/#distribute-extra-space
*/
void GrowSelectedTracksUnlimited(nscoord aAvailableSpace,
nsTArray<TrackSize>& aPlan,
const nsTArray<uint32_t>& aGrowableTracks,
- TrackSize::StateBits aSelector,
+ uint32_t aNumGrowable,
FitContentClamper aFitContentClamper) const
{
- MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0);
- uint32_t numGrowable = aGrowableTracks.Length();
- if (aSelector) {
- MOZ_ASSERT(aSelector == (aSelector & TrackSize::eIntrinsicMinSizing) &&
- (aSelector & TrackSize::eMaxContentMinSizing),
- "Should only get here for track sizing steps 2.1 to 2.3");
- // Note that eMaxContentMinSizing is always included. We do those first:
- numGrowable = MarkExcludedTracks(aPlan, numGrowable, aGrowableTracks,
- TrackSize::eMaxContentMinSizing,
- TrackSize::eMaxContentMaxSizing,
- TrackSize::eSkipGrowUnlimited1);
- // Now mark min-content/auto min-sizing tracks if requested.
- auto minOrAutoSelector = aSelector & ~TrackSize::eMaxContentMinSizing;
- if (minOrAutoSelector) {
- numGrowable = MarkExcludedTracks(aPlan, numGrowable, aGrowableTracks,
- minOrAutoSelector,
- TrackSize::eIntrinsicMaxSizing,
- TrackSize::eSkipGrowUnlimited2);
- }
- }
+ MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0 &&
+ aNumGrowable <= aGrowableTracks.Length());
nscoord space = aAvailableSpace;
DebugOnly<bool> didClamp = false;
- while (numGrowable) {
- nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
+ while (aNumGrowable) {
+ nscoord spacePerTrack = std::max<nscoord>(space / aNumGrowable, 1);
for (uint32_t track : aGrowableTracks) {
TrackSize& sz = aPlan[track];
if (sz.mState & TrackSize::eSkipGrowUnlimited) {
@@ -1609,7 +1741,7 @@ struct nsGridContainerFrame::Tracks
delta = newBase - sz.mBase;
MOZ_ASSERT(delta >= 0, "track size shouldn't shrink");
sz.mState |= TrackSize::eSkipGrowUnlimited1;
- --numGrowable;
+ --aNumGrowable;
}
}
sz.mBase = newBase;
@@ -1628,46 +1760,30 @@ struct nsGridContainerFrame::Tracks
* Distribute aAvailableSpace to the planned base size for aGrowableTracks
* up to their limits, then distribute the remaining space beyond the limits.
*/
- void DistributeToTrackBases(nscoord aAvailableSpace,
+ template<TrackSizingPhase phase>
+ void DistributeToTrackSizes(nscoord aAvailableSpace,
nsTArray<TrackSize>& aPlan,
+ nsTArray<TrackSize>& aItemPlan,
nsTArray<uint32_t>& aGrowableTracks,
- TrackSize::StateBits aSelector)
+ TrackSize::StateBits aSelector,
+ const FitContentClamper& aFitContentClamper)
{
- SetupGrowthPlan(aPlan, aGrowableTracks);
- nscoord space = GrowTracksToLimit(aAvailableSpace, aPlan, aGrowableTracks, nullptr);
+ InitializeItemPlan<phase>(aItemPlan, aGrowableTracks);
+ nscoord space = GrowTracksToLimit(aAvailableSpace, aItemPlan, aGrowableTracks,
+ aFitContentClamper);
if (space > 0) {
- GrowSelectedTracksUnlimited(space, aPlan, aGrowableTracks, aSelector, nullptr);
+ uint32_t numGrowable =
+ MarkExcludedTracks<phase>(aItemPlan, aGrowableTracks, aSelector);
+ GrowSelectedTracksUnlimited(space, aItemPlan, aGrowableTracks,
+ numGrowable, aFitContentClamper);
}
- CopyPlanToBase(aPlan, aGrowableTracks);
- }
-
- /**
- * Distribute aAvailableSpace to the planned limits for aGrowableTracks.
- */
- void DistributeToTrackLimits(nscoord aAvailableSpace,
- nsTArray<TrackSize>& aPlan,
- nsTArray<uint32_t>& aGrowableTracks,
- const TrackSizingFunctions& aFunctions,
- nscoord aPercentageBasis)
- {
- auto fitContentClamper = [&aFunctions, aPercentageBasis] (uint32_t aTrack,
- nscoord aMinSize,
- nscoord* aSize) {
- nscoord fitContentLimit =
- ::ResolveToDefiniteSize(aFunctions.MaxSizingFor(aTrack), aPercentageBasis);
- if (*aSize > fitContentLimit) {
- *aSize = std::max(aMinSize, fitContentLimit);
- return true;
+ for (uint32_t track : aGrowableTracks) {
+ nscoord& plannedSize = aPlan[track].mBase;
+ nscoord itemIncurredSize = aItemPlan[track].mBase;
+ if (plannedSize < itemIncurredSize) {
+ plannedSize = itemIncurredSize;
}
- return false;
- };
- nscoord space = GrowTracksToLimit(aAvailableSpace, aPlan, aGrowableTracks,
- fitContentClamper);
- if (space > 0) {
- GrowSelectedTracksUnlimited(aAvailableSpace, aPlan, aGrowableTracks,
- TrackSize::StateBits(0), fitContentClamper);
}
- CopyPlanToLimit(aPlan, aGrowableTracks);
}
/**
@@ -3545,19 +3661,27 @@ nsGridContainerFrame::Grid::PlaceGridItems(GridReflowInput& aState,
// Count empty 'auto-fit' tracks in the repeat() range.
// |colAdjust| will have a count for each line in the grid of how many
// tracks were empty between the start of the grid and that line.
+
+ // Since this loop is concerned with just the repeat tracks, we
+ // iterate from 0..NumRepeatTracks() which is the natural range of
+ // mRemoveRepeatTracks. This means we have to add
+ // (mExplicitGridOffset + mRepeatAutoStart) to get a zero-based
+ // index for arrays like mCellMap and colAdjust. We'll then fill out
+ // the colAdjust array for all the remaining lines.
Maybe<nsTArray<uint32_t>> colAdjust;
uint32_t numEmptyCols = 0;
if (aState.mColFunctions.mHasRepeatAuto &&
!gridStyle->mGridTemplateColumns.mIsAutoFill &&
aState.mColFunctions.NumRepeatTracks() > 0) {
- for (uint32_t col = aState.mColFunctions.mRepeatAutoStart,
- endRepeat = aState.mColFunctions.mRepeatAutoEnd,
- numColLines = mGridColEnd + 1;
- col < numColLines; ++col) {
+ const uint32_t repeatStart = (aState.mColFunctions.mExplicitGridOffset +
+ aState.mColFunctions.mRepeatAutoStart);
+ const uint32_t numRepeats = aState.mColFunctions.NumRepeatTracks();
+ const uint32_t numColLines = mGridColEnd + 1;
+ for (uint32_t i = 0; i < numRepeats; ++i) {
if (numEmptyCols) {
- (*colAdjust)[col] = numEmptyCols;
+ (*colAdjust)[repeatStart + i] = numEmptyCols;
}
- if (col < endRepeat && mCellMap.IsEmptyCol(col)) {
+ if (mCellMap.IsEmptyCol(repeatStart + i)) {
++numEmptyCols;
if (colAdjust.isNothing()) {
colAdjust.emplace(numColLines);
@@ -3565,26 +3689,34 @@ nsGridContainerFrame::Grid::PlaceGridItems(GridReflowInput& aState,
PodZero(colAdjust->Elements(), colAdjust->Length());
}
- uint32_t repeatIndex = col - aState.mColFunctions.mRepeatAutoStart;
- MOZ_ASSERT(aState.mColFunctions.mRemovedRepeatTracks.Length() >
- repeatIndex);
- aState.mColFunctions.mRemovedRepeatTracks[repeatIndex] = true;
+ aState.mColFunctions.mRemovedRepeatTracks[i] = true;
+ }
+ }
+ // Fill out the colAdjust array for all the columns after the
+ // repeats.
+ if (numEmptyCols) {
+ for (uint32_t col = repeatStart + numRepeats;
+ col < numColLines; ++col) {
+ (*colAdjust)[col] = numEmptyCols;
}
}
}
+
+ // Do similar work for the row tracks, with the same logic.
Maybe<nsTArray<uint32_t>> rowAdjust;
uint32_t numEmptyRows = 0;
if (aState.mRowFunctions.mHasRepeatAuto &&
!gridStyle->mGridTemplateRows.mIsAutoFill &&
aState.mRowFunctions.NumRepeatTracks() > 0) {
- for (uint32_t row = aState.mRowFunctions.mRepeatAutoStart,
- endRepeat = aState.mRowFunctions.mRepeatAutoEnd,
- numRowLines = mGridRowEnd + 1;
- row < numRowLines; ++row) {
+ const uint32_t repeatStart = (aState.mRowFunctions.mExplicitGridOffset +
+ aState.mRowFunctions.mRepeatAutoStart);
+ const uint32_t numRepeats = aState.mRowFunctions.NumRepeatTracks();
+ const uint32_t numRowLines = mGridRowEnd + 1;
+ for (uint32_t i = 0; i < numRepeats; ++i) {
if (numEmptyRows) {
- (*rowAdjust)[row] = numEmptyRows;
+ (*rowAdjust)[repeatStart + i] = numEmptyRows;
}
- if (row < endRepeat && mCellMap.IsEmptyRow(row)) {
+ if (mCellMap.IsEmptyRow(repeatStart + i)) {
++numEmptyRows;
if (rowAdjust.isNothing()) {
rowAdjust.emplace(numRowLines);
@@ -3592,10 +3724,13 @@ nsGridContainerFrame::Grid::PlaceGridItems(GridReflowInput& aState,
PodZero(rowAdjust->Elements(), rowAdjust->Length());
}
- uint32_t repeatIndex = row - aState.mRowFunctions.mRepeatAutoStart;
- MOZ_ASSERT(aState.mRowFunctions.mRemovedRepeatTracks.Length() >
- repeatIndex);
- aState.mRowFunctions.mRemovedRepeatTracks[repeatIndex] = true;
+ aState.mRowFunctions.mRemovedRepeatTracks[i] = true;
+ }
+ }
+ if (numEmptyRows) {
+ for (uint32_t row = repeatStart + numRepeats;
+ row < numRowLines; ++row) {
+ (*rowAdjust)[row] = numEmptyRows;
}
}
}
@@ -3974,28 +4109,16 @@ nsGridContainerFrame::Tracks::CalculateSizes(
}
}
-bool
-nsGridContainerFrame::Tracks::HasIntrinsicButNoFlexSizingInRange(
- const LineRange& aRange,
- TrackSize::StateBits* aState) const
+TrackSize::StateBits
+nsGridContainerFrame::Tracks::StateBitsForRange(const LineRange& aRange) const
{
- MOZ_ASSERT(!aRange.IsAuto(), "must have a definite range");
+ TrackSize::StateBits state = TrackSize::StateBits(0);
const uint32_t start = aRange.mStart;
const uint32_t end = aRange.mEnd;
- const TrackSize::StateBits selector =
- TrackSize::eIntrinsicMinSizing | TrackSize::eIntrinsicMaxSizing;
- bool foundIntrinsic = false;
for (uint32_t i = start; i < end; ++i) {
- TrackSize::StateBits state = mSizes[i].mState;
- *aState |= state;
- if (state & TrackSize::eFlexMaxSizing) {
- return false;
- }
- if (state & selector) {
- foundIntrinsic = true;
- }
+ state |= mSizes[i].mState;
}
- return foundIntrinsic;
+ return state;
}
bool
@@ -4010,6 +4133,13 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSizeStep1(
CachedIntrinsicSizes cache;
TrackSize& sz = mSizes[aRange.mStart];
WritingMode wm = aState.mWM;
+
+ // Check if we need to apply "Automatic Minimum Size" and cache it.
+ if ((sz.mState & TrackSize::eAutoMinSizing) &&
+ aGridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) {
+ aGridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize;
+ }
+
// Calculate data for "Automatic Minimum Size" clamping, if needed.
bool needed = ((sz.mState & TrackSize::eIntrinsicMinSizing) ||
aConstraint == SizingConstraint::eNoConstraint) &&
@@ -4370,6 +4500,55 @@ nsGridContainerFrame::Tracks::AlignBaselineSubtree(
}
}
+template<nsGridContainerFrame::Tracks::TrackSizingPhase phase>
+bool
+nsGridContainerFrame::Tracks::GrowSizeForSpanningItems(
+ nsTArray<Step2ItemData>::iterator aIter,
+ const nsTArray<Step2ItemData>::iterator aIterEnd,
+ nsTArray<uint32_t>& aTracks,
+ nsTArray<TrackSize>& aPlan,
+ nsTArray<TrackSize>& aItemPlan,
+ TrackSize::StateBits aSelector,
+ const FitContentClamper& aFitContentClamper,
+ bool aNeedInfinitelyGrowableFlag)
+{
+ constexpr bool isMaxSizingPhase =
+ phase == TrackSizingPhase::eIntrinsicMaximums ||
+ phase == TrackSizingPhase::eMaxContentMaximums;
+ bool needToUpdateSizes = false;
+ InitializePlan<phase>(aPlan);
+ for (; aIter != aIterEnd; ++aIter) {
+ const Step2ItemData& item = *aIter;
+ if (!(item.mState & aSelector)) {
+ continue;
+ }
+ if (isMaxSizingPhase) {
+ for (auto j = item.mLineRange.mStart, end = item.mLineRange.mEnd; j < end; ++j) {
+ aPlan[j].mState |= TrackSize::eModified;
+ }
+ }
+ nscoord space = item.SizeContributionForPhase<phase>();
+ if (space <= 0) {
+ continue;
+ }
+ aTracks.ClearAndRetainStorage();
+ space = CollectGrowable<phase>(space, item.mLineRange, aSelector,
+ aTracks);
+ if (space > 0) {
+ DistributeToTrackSizes<phase>(space, aPlan, aItemPlan, aTracks, aSelector,
+ aFitContentClamper);
+ needToUpdateSizes = true;
+ }
+ }
+ if (isMaxSizingPhase) {
+ needToUpdateSizes = true;
+ }
+ if (needToUpdateSizes) {
+ CopyPlanToSize<phase>(aPlan, aNeedInfinitelyGrowableFlag);
+ }
+ return needToUpdateSizes;
+}
+
void
nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
GridReflowInput& aState,
@@ -4379,21 +4558,6 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
nscoord aPercentageBasis,
SizingConstraint aConstraint)
{
- // Some data we collect on each item for Step 2 of the algorithm below.
- struct Step2ItemData
- {
- uint32_t mSpan;
- TrackSize::StateBits mState;
- LineRange mLineRange;
- nscoord mMinSize;
- nscoord mMinContentContribution;
- nscoord mMaxContentContribution;
- nsIFrame* mFrame;
- static bool IsSpanLessThan(const Step2ItemData& a, const Step2ItemData& b)
- {
- return a.mSpan < b.mSpan;
- }
- };
// Resolve Intrinsic Track Sizes
// http://dev.w3.org/csswg/css-grid/#algo-content
@@ -4418,12 +4582,10 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
for (; !iter.AtEnd(); iter.Next()) {
auto& gridItem = aGridItems[iter.GridItemIndex()];
- // Check if we need to apply "Automatic Minimum Size" and cache it.
- MOZ_ASSERT(!(gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize),
- "Why is eApplyAutoMinSize set already?");
- if (gridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) {
- gridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize;
- }
+ MOZ_ASSERT(!(gridItem.mState[mAxis] &
+ (ItemState::eApplyAutoMinSize | ItemState::eIsFlexing |
+ ItemState::eClampMarginBoxMinSize)),
+ "Why are any of these bits set already?");
const GridArea& area = gridItem.mArea;
const LineRange& lineRange = area.*aRange;
@@ -4435,8 +4597,17 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
gridItem.mState[mAxis] |= ItemState::eIsFlexing;
}
} else {
- TrackSize::StateBits state = TrackSize::StateBits(0);
- if (HasIntrinsicButNoFlexSizingInRange(lineRange, &state)) {
+ TrackSize::StateBits state = StateBitsForRange(lineRange);
+
+ // Check if we need to apply "Automatic Minimum Size" and cache it.
+ if ((state & TrackSize::eAutoMinSizing) &&
+ gridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) {
+ gridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize;
+ }
+
+ if ((state & (TrackSize::eIntrinsicMinSizing |
+ TrackSize::eIntrinsicMaxSizing)) &&
+ !(state & TrackSize::eFlexMaxSizing)) {
// Collect data for Step 2.
maxSpan = std::max(maxSpan, span);
if (span >= stateBitsPerSpan.Length()) {
@@ -4500,6 +4671,18 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
// Step 2.
if (maxSpan) {
+ auto fitContentClamper = [&aFunctions, aPercentageBasis] (uint32_t aTrack,
+ nscoord aMinSize,
+ nscoord* aSize)
+ {
+ nscoord fitContentLimit =
+ ::ResolveToDefiniteSize(aFunctions.MaxSizingFor(aTrack), aPercentageBasis);
+ if (*aSize > fitContentLimit) {
+ *aSize = std::max(aMinSize, fitContentLimit);
+ return true;
+ }
+ return false;
+ };
// Sort the collected items on span length, shortest first.
std::stable_sort(step2Items.begin(), step2Items.end(),
Step2ItemData::IsSpanLessThan);
@@ -4507,85 +4690,44 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
nsTArray<uint32_t> tracks(maxSpan);
nsTArray<TrackSize> plan(mSizes.Length());
plan.SetLength(mSizes.Length());
- for (uint32_t i = 0, len = step2Items.Length(); i < len; ) {
- // Start / end index for items of the same span length:
- const uint32_t spanGroupStartIndex = i;
- uint32_t spanGroupEndIndex = len;
- const uint32_t span = step2Items[i].mSpan;
- for (++i; i < len; ++i) {
- if (step2Items[i].mSpan != span) {
- spanGroupEndIndex = i;
- break;
- }
- }
-
+ nsTArray<TrackSize> itemPlan(mSizes.Length());
+ itemPlan.SetLength(mSizes.Length());
+ // Start / end iterator for items of the same span length:
+ auto spanGroupStart = step2Items.begin();
+ auto spanGroupEnd = spanGroupStart;
+ const auto end = step2Items.end();
+ for (; spanGroupStart != end; spanGroupStart = spanGroupEnd) {
+ while (spanGroupEnd != end &&
+ !Step2ItemData::IsSpanLessThan(*spanGroupStart, *spanGroupEnd)) {
+ ++spanGroupEnd;
+ }
+
+ const uint32_t span = spanGroupStart->mSpan;
bool updatedBase = false; // Did we update any mBase in step 2.1 - 2.3?
TrackSize::StateBits selector(TrackSize::eIntrinsicMinSizing);
if (stateBitsPerSpan[span] & selector) {
// Step 2.1 MinSize to intrinsic min-sizing.
- for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) {
- Step2ItemData& item = step2Items[i];
- if (!(item.mState & selector)) {
- continue;
- }
- nscoord space = item.mMinSize;
- if (space <= 0) {
- continue;
- }
- tracks.ClearAndRetainStorage();
- space = CollectGrowable(space, mSizes, item.mLineRange, selector,
- tracks);
- if (space > 0) {
- DistributeToTrackBases(space, plan, tracks, selector);
- updatedBase = true;
- }
- }
+ updatedBase =
+ GrowSizeForSpanningItems<TrackSizingPhase::eIntrinsicMinimums>(
+ spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
}
selector = contentBasedMinSelector;
if (stateBitsPerSpan[span] & selector) {
// Step 2.2 MinContentContribution to min-/max-content (and 'auto' when
// sizing under a min-content constraint) min-sizing.
- for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) {
- Step2ItemData& item = step2Items[i];
- if (!(item.mState & selector)) {
- continue;
- }
- nscoord space = item.mMinContentContribution;
- if (space <= 0) {
- continue;
- }
- tracks.ClearAndRetainStorage();
- space = CollectGrowable(space, mSizes, item.mLineRange, selector,
- tracks);
- if (space > 0) {
- DistributeToTrackBases(space, plan, tracks, selector);
- updatedBase = true;
- }
- }
+ updatedBase |=
+ GrowSizeForSpanningItems<TrackSizingPhase::eContentBasedMinimums>(
+ spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
}
selector = maxContentMinSelector;
if (stateBitsPerSpan[span] & selector) {
// Step 2.3 MaxContentContribution to max-content (and 'auto' when
// sizing under a max-content constraint) min-sizing.
- for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) {
- Step2ItemData& item = step2Items[i];
- if (!(item.mState & selector)) {
- continue;
- }
- nscoord space = item.mMaxContentContribution;
- if (space <= 0) {
- continue;
- }
- tracks.ClearAndRetainStorage();
- space = CollectGrowable(space, mSizes, item.mLineRange, selector,
- tracks);
- if (space > 0) {
- DistributeToTrackBases(space, plan, tracks, selector);
- updatedBase = true;
- }
- }
+ updatedBase |=
+ GrowSizeForSpanningItems<TrackSizingPhase::eMaxContentMinimums>(
+ spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
}
if (updatedBase) {
@@ -4596,63 +4738,22 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
}
}
}
- if (stateBitsPerSpan[span] & TrackSize::eIntrinsicMaxSizing) {
- plan = mSizes;
- for (TrackSize& sz : plan) {
- if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
- // use mBase as the planned limit
- } else {
- sz.mBase = sz.mLimit;
- }
- }
+ selector = TrackSize::eIntrinsicMaxSizing;
+ if (stateBitsPerSpan[span] & selector) {
+ const bool willRunStep2_6 =
+ stateBitsPerSpan[span] & TrackSize::eAutoOrMaxContentMaxSizing;
// Step 2.5 MinSize to intrinsic max-sizing.
- for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) {
- Step2ItemData& item = step2Items[i];
- if (!(item.mState & TrackSize::eIntrinsicMaxSizing)) {
- continue;
- }
- nscoord space = item.mMinSize;
- if (space <= 0) {
- continue;
- }
- tracks.ClearAndRetainStorage();
- space = CollectGrowable(space, plan, item.mLineRange,
- TrackSize::eIntrinsicMaxSizing,
- tracks);
- if (space > 0) {
- DistributeToTrackLimits(space, plan, tracks, aFunctions,
- aPercentageBasis);
- }
- }
- for (size_t j = 0, len = mSizes.Length(); j < len; ++j) {
- TrackSize& sz = plan[j];
- sz.mState &= ~(TrackSize::eFrozen | TrackSize::eSkipGrowUnlimited);
- if (sz.mLimit != NS_UNCONSTRAINEDSIZE) {
- sz.mLimit = sz.mBase; // collect the results from 2.5
- }
- }
+ GrowSizeForSpanningItems<TrackSizingPhase::eIntrinsicMaximums>(
+ spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector,
+ fitContentClamper, willRunStep2_6);
- if (stateBitsPerSpan[span] & TrackSize::eAutoOrMaxContentMaxSizing) {
+ if (willRunStep2_6) {
// Step 2.6 MaxContentContribution to max-content max-sizing.
- for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) {
- Step2ItemData& item = step2Items[i];
- if (!(item.mState & TrackSize::eAutoOrMaxContentMaxSizing)) {
- continue;
- }
- nscoord space = item.mMaxContentContribution;
- if (space <= 0) {
- continue;
- }
- tracks.ClearAndRetainStorage();
- space = CollectGrowable(space, plan, item.mLineRange,
- TrackSize::eAutoOrMaxContentMaxSizing,
- tracks);
- if (space > 0) {
- DistributeToTrackLimits(space, plan, tracks, aFunctions,
- aPercentageBasis);
- }
- }
+ selector = TrackSize::eAutoOrMaxContentMaxSizing;
+ GrowSizeForSpanningItems<TrackSizingPhase::eMaxContentMaximums>(
+ spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector,
+ fitContentClamper);
}
}
}
@@ -4984,6 +5085,7 @@ nsGridContainerFrame::Tracks::AlignJustifyContent(
default:
MOZ_ASSERT_UNREACHABLE("unknown align-/justify-content value");
between = 0; // just to avoid a compiler warning
+ roundingError = 0; // just to avoid a compiler warning
}
between += mGridGap;
for (TrackSize& sz : mSizes) {
@@ -7113,6 +7215,9 @@ nsGridContainerFrame::TrackSize::Dump() const
if (mState & eFrozen) {
printf("frozen ");
}
+ if (mState & eModified) {
+ printf("modified ");
+ }
if (mState & eBreakBefore) {
printf("break-before ");
}