diff options
-rw-r--r-- | dom/grid/GridLines.cpp | 42 | ||||
-rw-r--r-- | layout/generic/nsGridContainerFrame.cpp | 661 |
2 files changed, 410 insertions, 293 deletions
diff --git a/dom/grid/GridLines.cpp b/dom/grid/GridLines.cpp index fac645c64..898885346 100644 --- a/dom/grid/GridLines.cpp +++ b/dom/grid/GridLines.cpp @@ -90,7 +90,9 @@ GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo, for (uint32_t i = aTrackInfo->mStartFragmentTrack; i < aTrackInfo->mEndFragmentTrack + 1; i++) { - uint32_t line1Index = i + 1; + // Since line indexes are 1-based, calculate a 1-based value + // for this track to simplify some calculations. + const uint32_t line1Index = i + 1; startOfNextTrack = (i < aTrackInfo->mEndFragmentTrack) ? aTrackInfo->mPositions[i] : @@ -127,7 +129,8 @@ GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo, } } - if (i >= aTrackInfo->mRepeatFirstTrack && + if (i >= (aTrackInfo->mRepeatFirstTrack + + aTrackInfo->mNumLeadingImplicitTracks) && repeatIndex < numRepeatTracks) { numAddedLines += AppendRemovedAutoFits(aTrackInfo, aLineInfo, @@ -139,23 +142,30 @@ GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo, RefPtr<GridLine> line = new GridLine(this); mLines.AppendElement(line); + MOZ_ASSERT(line1Index > 0, "line1Index must be positive."); + bool isBeforeFirstExplicit = + (line1Index <= aTrackInfo->mNumLeadingImplicitTracks); + // Calculate an actionable line number for this line, that could be used + // in a css grid property to align a grid item or area at that line. + // For implicit lines that appear before line 1, report a number of 0. + // We can't report negative indexes, because those have a different + // meaning in the css grid spec (negative indexes are negative-1-based + // from the end of the grid decreasing towards the front). + uint32_t lineNumber = isBeforeFirstExplicit ? 0 : + (line1Index - aTrackInfo->mNumLeadingImplicitTracks + numAddedLines); + GridDeclaration lineType = + (isBeforeFirstExplicit || + line1Index > (aTrackInfo->mNumLeadingImplicitTracks + + aTrackInfo->mNumExplicitTracks + 1)) + ? GridDeclaration::Implicit + : GridDeclaration::Explicit; line->SetLineValues( lineNames, nsPresContext::AppUnitsToDoubleCSSPixels(lastTrackEdge), nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack - lastTrackEdge), - line1Index + numAddedLines, - ( - // Implicit if there are no explicit tracks, or if the index - // is before the first explicit track, or after - // a track beyond the last explicit track. - (aTrackInfo->mNumExplicitTracks == 0) || - (i < aTrackInfo->mNumLeadingImplicitTracks) || - (i > aTrackInfo->mNumLeadingImplicitTracks + - aTrackInfo->mNumExplicitTracks) ? - GridDeclaration::Implicit : - GridDeclaration::Explicit - ) + lineNumber, + lineType ); if (i < aTrackInfo->mEndFragmentTrack) { @@ -215,11 +225,13 @@ GridLines::AppendRemovedAutoFits(const ComputedGridTrackInfo* aTrackInfo, RefPtr<GridLine> line = new GridLine(this); mLines.AppendElement(line); + uint32_t lineNumber = aTrackInfo->mRepeatFirstTrack + + aRepeatIndex + 1; line->SetLineValues( aLineNames, nsPresContext::AppUnitsToDoubleCSSPixels(aLastTrackEdge), nsPresContext::AppUnitsToDoubleCSSPixels(0), - aTrackInfo->mRepeatFirstTrack + aRepeatIndex + 1, + lineNumber, GridDeclaration::Explicit ); 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 "); } |