diff options
author | Gaming4JC <g4jc@hyperbola.info> | 2019-09-28 23:16:55 -0400 |
---|---|---|
committer | Gaming4JC <g4jc@hyperbola.info> | 2019-09-28 23:47:04 -0400 |
commit | 22851ce36d97fca5a4768f3c49a4fff6cebd320a (patch) | |
tree | 9f93b1a261a2a307d3f0e53521eb4b0168464208 | |
parent | 81db54a1cbbaf9a8cbc64fcf8b232b0f1f13d14f (diff) | |
download | UXP-22851ce36d97fca5a4768f3c49a4fff6cebd320a.tar UXP-22851ce36d97fca5a4768f3c49a4fff6cebd320a.tar.gz UXP-22851ce36d97fca5a4768f3c49a4fff6cebd320a.tar.lz UXP-22851ce36d97fca5a4768f3c49a4fff6cebd320a.tar.xz UXP-22851ce36d97fca5a4768f3c49a4fff6cebd320a.zip |
Issue #1233 - Part 1: Fix grid overflow and rendering issues by improving Layout CSS-Grid API
List of relevant patches applied:
1425599 part 1 - [css-grid] Change the track sizing algorithm for spanning items so that it accumulates individual item contributions to the plan by max() rather than incrementing the planned size directly.
Also, fix a bug when copying back the planned limits after updating it for the first span group. It should only copy back track sizes that were actaully spanned by those items, other content-sized tracks' limits should remain at "infinity".
1425599 part 2 - [css-grid] Factor out the min-sizing parts of the track sizing for spanned items to a templated method (idempotent change).
1425599 part 3 - [css-grid] Factor out most of the max-sizing parts of the track sizing for spanned items to a templated method (idempotent change).
1425599 part 4 - [css-grid] Factor out the starting base/limit size to a templated method (idempotent change).
1425599 part 5 - [css-grid] Make CollectGrowable a templated method so that it works with either base/limit sizes (idempotent change).
1425599 part 6 - [css-grid] Make the size distribution methods templated with the intent of merging them in a later patch (idempotent change).
This patch also introduces an eInfinitelyGrowable bit to help get rid of the 'limits' temporary track sizes in the next patch.
1425599 part 7 - [css-grid] Remove the 'limits' copy of track sizes since they are no longer needed (idempotent change).
1425599 part 8 - [css-grid] Factor out the fit-content clamping function from DistributeToTrackLimits and pass it as a param instead (idempotent change).
1425599 part 9 - [css-grid] Merge DistributeToTrackLimits/Bases (idempotent change).
1425599 part 10 - [css-grid] Make MarkExcludedTracks a static method since it doesn't use 'this' (idempotent change).
1425599 part 11 - [css-grid] Hoist the marking of excluded tracks out from GrowSelectedTracksUnlimited to a separate method (idempotent change).
1425599 part 12 - [css-grid] Merge CopyPlanToBase/Limits into a templated method instead (idempotent change).
1425599 part 13 - [css-grid] Merge Grow[Base|Limits]ForSpanningItems into a templated method instead (idempotent change).
1425599 part 14 - [css-grid] Use iterators instead of an array + start/end index for the item data (idempotent change).
1425599 part 16 - [css-grid] Make SizeContributionForPhase a template.
1425599 - [css-grid] Follow-up bustage fix for stupid compiler warnings.
1378481 - Assign 'roundingError' in the default branch too, to avoid a maybe-uninitialized compiler warning.
1423292 - [css-grid] Add a couple of ItemState bits to Dump(), and make an ItemState assertion stricter (DEBUG-only changes).
1373678 Part 1: Reduce grid line numbers by count of leading implicit lines, minimum 0.
1416350 - Part 1: Correctly account for removed 'auto-fit' tracks also when there are leading implicit tracks.
1416350 - Part 2: Correct logic for Grid API line numbering with leading implicit tracks.
1418727 part 1 - [css-grid] Introduce StateBitsForRange() that collects the union of the state bits for a range of tracks (idempotent change).
1418727 part 2 - [css-grid] Require that an item spans at least one track with an 'auto' min sizing function for Automatic Minimum Size to apply.
-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 "); } |