Bug 1760439 [wpt PR 33220] - Implement FedCM permission delegates in content_shell...
[gecko.git] / layout / generic / nsGridContainerFrame.cpp
blob1f52a5bb9b3cc4ade002c06e237a18a12c2f7fc6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* rendering object for CSS "display: grid | inline-grid" */
9 #include "nsGridContainerFrame.h"
11 #include <functional>
12 #include <limits>
13 #include <stdlib.h> // for div()
14 #include <type_traits>
15 #include "gfxContext.h"
16 #include "mozilla/AutoRestore.h"
17 #include "mozilla/ComputedStyle.h"
18 #include "mozilla/CSSAlignUtils.h"
19 #include "mozilla/dom/GridBinding.h"
20 #include "mozilla/IntegerRange.h"
21 #include "mozilla/Maybe.h"
22 #include "mozilla/PodOperations.h" // for PodZero
23 #include "mozilla/Poison.h"
24 #include "mozilla/PresShell.h"
25 #include "nsAbsoluteContainingBlock.h"
26 #include "nsAlgorithm.h" // for clamped()
27 #include "nsBoxLayoutState.h"
28 #include "nsCSSAnonBoxes.h"
29 #include "nsCSSFrameConstructor.h"
30 #include "nsTHashMap.h"
31 #include "nsDisplayList.h"
32 #include "nsHashKeys.h"
33 #include "nsFieldSetFrame.h"
34 #include "nsIFrameInlines.h"
35 #include "nsPlaceholderFrame.h"
36 #include "nsPresContext.h"
37 #include "nsReadableUtils.h"
38 #include "nsTableWrapperFrame.h"
40 using namespace mozilla;
42 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
43 typedef nsGridContainerFrame::TrackSize TrackSize;
44 typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags;
46 using GridTemplate = StyleGridTemplateComponent;
47 using TrackListValue =
48 StyleGenericTrackListValue<LengthPercentage, StyleInteger>;
49 using TrackRepeat = StyleGenericTrackRepeat<LengthPercentage, StyleInteger>;
50 using NameList = StyleOwnedSlice<StyleCustomIdent>;
51 using SizingConstraint = nsGridContainerFrame::SizingConstraint;
52 using GridItemCachedBAxisMeasurement =
53 nsGridContainerFrame::CachedBAxisMeasurement;
55 static mozilla::LazyLogModule gGridContainerLog("GridContainer");
56 #define GRID_LOG(...) \
57 MOZ_LOG(gGridContainerLog, LogLevel::Debug, (__VA_ARGS__));
59 static const int32_t kMaxLine = StyleMAX_GRID_LINE;
60 static const int32_t kMinLine = StyleMIN_GRID_LINE;
61 // The maximum line number, in the zero-based translated grid.
62 static const uint32_t kTranslatedMaxLine = uint32_t(kMaxLine - kMinLine);
63 static const uint32_t kAutoLine = kTranslatedMaxLine + 3457U;
65 static const nsFrameState kIsSubgridBits =
66 (NS_STATE_GRID_IS_COL_SUBGRID | NS_STATE_GRID_IS_ROW_SUBGRID);
68 namespace mozilla {
70 template <>
71 inline Span<const StyleOwnedSlice<StyleCustomIdent>>
72 GridTemplate::LineNameLists(bool aIsSubgrid) const {
73 if (IsTrackList()) {
74 return AsTrackList()->line_names.AsSpan();
76 if (IsSubgrid() && aIsSubgrid) {
77 return AsSubgrid()->names.AsSpan();
79 MOZ_ASSERT(IsNone() || IsMasonry() || (IsSubgrid() && !aIsSubgrid));
80 return {};
83 template <>
84 inline const StyleTrackBreadth& StyleTrackSize::GetMax() const {
85 if (IsBreadth()) {
86 return AsBreadth();
88 if (IsMinmax()) {
89 return AsMinmax()._1;
91 MOZ_ASSERT(IsFitContent());
92 return AsFitContent();
95 template <>
96 inline const StyleTrackBreadth& StyleTrackSize::GetMin() const {
97 static const StyleTrackBreadth kAuto = StyleTrackBreadth::Auto();
98 if (IsBreadth()) {
99 // <flex> behaves like minmax(auto, <flex>)
100 return AsBreadth().IsFr() ? kAuto : AsBreadth();
102 if (IsMinmax()) {
103 return AsMinmax()._0;
105 MOZ_ASSERT(IsFitContent());
106 return kAuto;
109 } // namespace mozilla
111 static nscoord ClampToCSSMaxBSize(nscoord aSize,
112 const ReflowInput* aReflowInput) {
113 auto maxSize = aReflowInput->ComputedMaxBSize();
114 if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
115 MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
116 aSize = std::min(aSize, maxSize);
118 return aSize;
121 // Same as above and set aStatus INCOMPLETE if aSize wasn't clamped.
122 // (If we clamp aSize it means our size is less than the break point,
123 // i.e. we're effectively breaking in our overflow, so we should leave
124 // aStatus as is (it will likely be set to OVERFLOW_INCOMPLETE later)).
125 static nscoord ClampToCSSMaxBSize(nscoord aSize,
126 const ReflowInput* aReflowInput,
127 nsReflowStatus* aStatus) {
128 auto maxSize = aReflowInput->ComputedMaxBSize();
129 if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
130 MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
131 if (aSize < maxSize) {
132 aStatus->SetIncomplete();
133 } else {
134 aSize = maxSize;
136 } else {
137 aStatus->SetIncomplete();
139 return aSize;
142 template <typename Size>
143 static bool IsPercentOfIndefiniteSize(const Size& aCoord,
144 nscoord aPercentBasis) {
145 return aPercentBasis == NS_UNCONSTRAINEDSIZE && aCoord.HasPercent();
148 static nscoord ResolveToDefiniteSize(const StyleTrackBreadth& aBreadth,
149 nscoord aPercentBasis) {
150 MOZ_ASSERT(aBreadth.IsBreadth());
151 if (::IsPercentOfIndefiniteSize(aBreadth.AsBreadth(), aPercentBasis)) {
152 return nscoord(0);
154 return std::max(nscoord(0), aBreadth.AsBreadth().Resolve(aPercentBasis));
157 // Synthesize a baseline from a border box. For an alphabetical baseline
158 // this is the end edge of the border box. For a central baseline it's
159 // the center of the border box.
160 // https://drafts.csswg.org/css-align-3/#synthesize-baselines
161 // For a 'first baseline' the measure is from the border-box start edge and
162 // for a 'last baseline' the measure is from the border-box end edge.
163 static nscoord SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup,
164 WritingMode aWM,
165 nscoord aBorderBoxSize) {
166 if (aGroup == BaselineSharingGroup::First) {
167 return aWM.IsAlphabeticalBaseline() ? aBorderBoxSize : aBorderBoxSize / 2;
169 MOZ_ASSERT(aGroup == BaselineSharingGroup::Last);
170 // Round up for central baseline offset, to be consistent with eFirst.
171 return aWM.IsAlphabeticalBaseline()
173 : (aBorderBoxSize / 2) + (aBorderBoxSize % 2);
176 // The input sizes for calculating the number of repeat(auto-fill/fit) tracks.
177 // https://drafts.csswg.org/css-grid/#auto-repeat
178 struct RepeatTrackSizingInput {
179 explicit RepeatTrackSizingInput(WritingMode aWM)
180 : mMin(aWM, 0, 0),
181 mSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
182 mMax(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) {}
183 RepeatTrackSizingInput(const LogicalSize& aMin, const LogicalSize& aSize,
184 const LogicalSize& aMax)
185 : mMin(aMin), mSize(aSize), mMax(aMax) {}
187 // This should be used in intrinsic sizing (i.e. when we can't initialize
188 // the sizes directly from ReflowInput values).
189 void InitFromStyle(LogicalAxis aAxis, WritingMode aWM,
190 const ComputedStyle* aStyle) {
191 const auto& pos = aStyle->StylePosition();
192 const bool borderBoxSizing = pos->mBoxSizing == StyleBoxSizing::Border;
193 nscoord bp = NS_UNCONSTRAINEDSIZE; // a sentinel to calculate it only once
194 auto adjustForBoxSizing = [borderBoxSizing, aWM, aAxis, aStyle,
195 &bp](nscoord aSize) {
196 if (!borderBoxSizing) {
197 return aSize;
199 if (bp == NS_UNCONSTRAINEDSIZE) {
200 const auto& padding = aStyle->StylePadding()->mPadding;
201 LogicalMargin border(aWM, aStyle->StyleBorder()->GetComputedBorder());
202 // We can use zero percentage basis since this is only called from
203 // intrinsic sizing code.
204 const nscoord percentageBasis = 0;
205 if (aAxis == eLogicalAxisInline) {
206 bp = std::max(padding.GetIStart(aWM).Resolve(percentageBasis), 0) +
207 std::max(padding.GetIEnd(aWM).Resolve(percentageBasis), 0) +
208 border.IStartEnd(aWM);
209 } else {
210 bp = std::max(padding.GetBStart(aWM).Resolve(percentageBasis), 0) +
211 std::max(padding.GetBEnd(aWM).Resolve(percentageBasis), 0) +
212 border.BStartEnd(aWM);
215 return std::max(aSize - bp, 0);
217 nscoord& min = mMin.Size(aAxis, aWM);
218 nscoord& size = mSize.Size(aAxis, aWM);
219 nscoord& max = mMax.Size(aAxis, aWM);
220 const auto& minCoord =
221 aAxis == eLogicalAxisInline ? pos->MinISize(aWM) : pos->MinBSize(aWM);
222 if (minCoord.ConvertsToLength()) {
223 min = adjustForBoxSizing(minCoord.ToLength());
225 const auto& maxCoord =
226 aAxis == eLogicalAxisInline ? pos->MaxISize(aWM) : pos->MaxBSize(aWM);
227 if (maxCoord.ConvertsToLength()) {
228 max = std::max(min, adjustForBoxSizing(maxCoord.ToLength()));
230 const auto& sizeCoord =
231 aAxis == eLogicalAxisInline ? pos->ISize(aWM) : pos->BSize(aWM);
232 if (sizeCoord.ConvertsToLength()) {
233 size = Clamp(adjustForBoxSizing(sizeCoord.ToLength()), min, max);
237 LogicalSize mMin;
238 LogicalSize mSize;
239 LogicalSize mMax;
242 enum class GridLineSide {
243 BeforeGridGap,
244 AfterGridGap,
247 struct nsGridContainerFrame::TrackSize {
248 enum StateBits : uint16_t {
249 // clang-format off
250 eAutoMinSizing = 0x1,
251 eMinContentMinSizing = 0x2,
252 eMaxContentMinSizing = 0x4,
253 eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing,
254 eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing,
255 eModified = 0x8,
256 eAutoMaxSizing = 0x10,
257 eMinContentMaxSizing = 0x20,
258 eMaxContentMaxSizing = 0x40,
259 eAutoOrMaxContentMaxSizing = eAutoMaxSizing | eMaxContentMaxSizing,
260 eIntrinsicMaxSizing = eAutoOrMaxContentMaxSizing | eMinContentMaxSizing,
261 eFlexMaxSizing = 0x80,
262 eFrozen = 0x100,
263 eSkipGrowUnlimited1 = 0x200,
264 eSkipGrowUnlimited2 = 0x400,
265 eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2,
266 eBreakBefore = 0x800,
267 eFitContent = 0x1000,
268 eInfinitelyGrowable = 0x2000,
270 // These are only used in the masonry axis. They share the same value
271 // as *MinSizing above, but that's OK because we don't use those in
272 // the masonry axis.
274 // This track corresponds to an item margin-box size that is stretching.
275 eItemStretchSize = 0x1,
276 // This bit says that we should clamp that size to mLimit.
277 eClampToLimit = 0x2,
278 // This bit says that the corresponding item has `auto` margin(s).
279 eItemHasAutoMargin = 0x4,
280 // clang-format on
283 StateBits Initialize(nscoord aPercentageBasis, const StyleTrackSize&);
284 bool IsFrozen() const { return mState & eFrozen; }
285 #ifdef DEBUG
286 static void DumpStateBits(StateBits aState);
287 void Dump() const;
288 #endif
290 static bool IsDefiniteMaxSizing(StateBits aStateBits) {
291 return (aStateBits & (eIntrinsicMaxSizing | eFlexMaxSizing)) == 0;
294 nscoord mBase;
295 nscoord mLimit;
296 nscoord mPosition; // zero until we apply 'align/justify-content'
297 // mBaselineSubtreeSize is the size of a baseline-aligned subtree within
298 // this track. One subtree per baseline-sharing group (per track).
299 PerBaseline<nscoord> mBaselineSubtreeSize;
300 StateBits mState;
303 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits)
305 namespace mozilla {
306 template <>
307 struct IsPod<nsGridContainerFrame::TrackSize> : std::true_type {};
308 } // namespace mozilla
310 TrackSize::StateBits nsGridContainerFrame::TrackSize::Initialize(
311 nscoord aPercentageBasis, const StyleTrackSize& aSize) {
312 using Tag = StyleTrackBreadth::Tag;
314 MOZ_ASSERT(mBase == 0 && mLimit == 0 && mState == 0,
315 "track size data is expected to be initialized to zero");
316 mBaselineSubtreeSize[BaselineSharingGroup::First] = nscoord(0);
317 mBaselineSubtreeSize[BaselineSharingGroup::Last] = nscoord(0);
319 auto& min = aSize.GetMin();
320 auto& max = aSize.GetMax();
322 Tag minSizeTag = min.tag;
323 Tag maxSizeTag = max.tag;
324 if (aSize.IsFitContent()) {
325 // In layout, fit-content(size) behaves as minmax(auto, max-content), with
326 // 'size' as an additional upper-bound.
327 mState = eFitContent;
328 minSizeTag = Tag::Auto;
329 maxSizeTag = Tag::MaxContent;
331 if (::IsPercentOfIndefiniteSize(min, aPercentageBasis)) {
332 // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-percentage
333 // "If the inline or block size of the grid container is indefinite,
334 // <percentage> values relative to that size are treated as 'auto'."
335 minSizeTag = Tag::Auto;
337 if (::IsPercentOfIndefiniteSize(max, aPercentageBasis)) {
338 maxSizeTag = Tag::Auto;
341 // http://dev.w3.org/csswg/css-grid/#algo-init
342 switch (minSizeTag) {
343 case Tag::Auto:
344 mState |= eAutoMinSizing;
345 break;
346 case Tag::MinContent:
347 mState |= eMinContentMinSizing;
348 break;
349 case Tag::MaxContent:
350 mState |= eMaxContentMinSizing;
351 break;
352 default:
353 MOZ_ASSERT(!min.IsFr(), "<flex> min-sizing is invalid as a track size");
354 mBase = ::ResolveToDefiniteSize(min, aPercentageBasis);
356 switch (maxSizeTag) {
357 case Tag::Auto:
358 mState |= eAutoMaxSizing;
359 mLimit = NS_UNCONSTRAINEDSIZE;
360 break;
361 case Tag::MinContent:
362 case Tag::MaxContent:
363 mState |= maxSizeTag == Tag::MinContent ? eMinContentMaxSizing
364 : eMaxContentMaxSizing;
365 mLimit = NS_UNCONSTRAINEDSIZE;
366 break;
367 case Tag::Fr:
368 mState |= eFlexMaxSizing;
369 mLimit = mBase;
370 break;
371 default:
372 mLimit = ::ResolveToDefiniteSize(max, aPercentageBasis);
373 if (mLimit < mBase) {
374 mLimit = mBase;
377 return mState;
381 * A LineRange can be definite or auto - when it's definite it represents
382 * a consecutive set of tracks between a starting line and an ending line.
383 * Before it's definite it can also represent an auto position with a span,
384 * where mStart == kAutoLine and mEnd is the (non-zero positive) span.
385 * For normal-flow items, the invariant mStart < mEnd holds when both
386 * lines are definite.
388 * For abs.pos. grid items, mStart and mEnd may both be kAutoLine, meaning
389 * "attach this side to the grid container containing block edge".
390 * Additionally, mStart <= mEnd holds when both are definite (non-kAutoLine),
391 * i.e. the invariant is slightly relaxed compared to normal flow items.
393 struct nsGridContainerFrame::LineRange {
394 LineRange(int32_t aStart, int32_t aEnd)
395 : mUntranslatedStart(aStart), mUntranslatedEnd(aEnd) {
396 #ifdef DEBUG
397 if (!IsAutoAuto()) {
398 if (IsAuto()) {
399 MOZ_ASSERT(aEnd >= kMinLine && aEnd <= kMaxLine, "invalid span");
400 } else {
401 MOZ_ASSERT(aStart >= kMinLine && aStart <= kMaxLine,
402 "invalid start line");
403 MOZ_ASSERT(aEnd == int32_t(kAutoLine) ||
404 (aEnd >= kMinLine && aEnd <= kMaxLine),
405 "invalid end line");
408 #endif
410 bool IsAutoAuto() const { return mStart == kAutoLine && mEnd == kAutoLine; }
411 bool IsAuto() const { return mStart == kAutoLine; }
412 bool IsDefinite() const { return mStart != kAutoLine; }
413 uint32_t Extent() const {
414 MOZ_ASSERT(mEnd != kAutoLine, "Extent is undefined for abs.pos. 'auto'");
415 if (IsAuto()) {
416 MOZ_ASSERT(mEnd >= 1 && mEnd < uint32_t(kMaxLine), "invalid span");
417 return mEnd;
419 return mEnd - mStart;
423 * Return an object suitable for iterating this range.
425 auto Range() const { return IntegerRange<uint32_t>(mStart, mEnd); }
428 * Resolve this auto range to start at aStart, making it definite.
429 * @param aClampMaxLine the maximum allowed line number (zero-based)
430 * Precondition: this range IsAuto()
432 void ResolveAutoPosition(uint32_t aStart, uint32_t aClampMaxLine) {
433 MOZ_ASSERT(IsAuto(), "Why call me?");
434 mStart = aStart;
435 mEnd += aStart;
436 // Clamp to aClampMaxLine, which is where kMaxLine is in the explicit
437 // grid in a non-subgrid axis; this implements clamping per
438 // http://dev.w3.org/csswg/css-grid/#overlarge-grids
439 // In a subgrid axis it's the end of the grid in that axis.
440 if (MOZ_UNLIKELY(mStart >= aClampMaxLine)) {
441 mEnd = aClampMaxLine;
442 mStart = mEnd - 1;
443 } else if (MOZ_UNLIKELY(mEnd > aClampMaxLine)) {
444 mEnd = aClampMaxLine;
448 * Translate the lines to account for (empty) removed tracks. This method
449 * is only for grid items and should only be called after placement.
450 * aNumRemovedTracks contains a count for each line in the grid how many
451 * tracks were removed between the start of the grid and that line.
453 void AdjustForRemovedTracks(const nsTArray<uint32_t>& aNumRemovedTracks) {
454 MOZ_ASSERT(mStart != kAutoLine, "invalid resolved line for a grid item");
455 MOZ_ASSERT(mEnd != kAutoLine, "invalid resolved line for a grid item");
456 uint32_t numRemovedTracks = aNumRemovedTracks[mStart];
457 MOZ_ASSERT(numRemovedTracks == aNumRemovedTracks[mEnd],
458 "tracks that a grid item spans can't be removed");
459 mStart -= numRemovedTracks;
460 mEnd -= numRemovedTracks;
463 * Translate the lines to account for (empty) removed tracks. This method
464 * is only for abs.pos. children and should only be called after placement.
465 * Same as for in-flow items, but we don't touch 'auto' lines here and we
466 * also need to adjust areas that span into the removed tracks.
468 void AdjustAbsPosForRemovedTracks(
469 const nsTArray<uint32_t>& aNumRemovedTracks) {
470 if (mStart != kAutoLine) {
471 mStart -= aNumRemovedTracks[mStart];
473 if (mEnd != kAutoLine) {
474 MOZ_ASSERT(mStart == kAutoLine || mEnd > mStart, "invalid line range");
475 mEnd -= aNumRemovedTracks[mEnd];
479 * Return the contribution of this line range for step 2 in
480 * http://dev.w3.org/csswg/css-grid/#auto-placement-algo
482 uint32_t HypotheticalEnd() const { return mEnd; }
484 * Given an array of track sizes, return the starting position and length
485 * of the tracks in this line range.
487 void ToPositionAndLength(const nsTArray<TrackSize>& aTrackSizes,
488 nscoord* aPos, nscoord* aLength) const;
490 * Given an array of track sizes, return the length of the tracks in this
491 * line range.
493 nscoord ToLength(const nsTArray<TrackSize>& aTrackSizes) const;
495 * Given an array of track sizes and a grid origin coordinate, adjust the
496 * abs.pos. containing block along an axis given by aPos and aLength.
497 * aPos and aLength should already be initialized to the grid container
498 * containing block for this axis before calling this method.
500 void ToPositionAndLengthForAbsPos(const Tracks& aTracks, nscoord aGridOrigin,
501 nscoord* aPos, nscoord* aLength) const;
503 void Translate(int32_t aOffset) {
504 MOZ_ASSERT(IsDefinite());
505 mStart += aOffset;
506 mEnd += aOffset;
509 /** Swap the start/end sides of this range. */
510 void ReverseDirection(uint32_t aGridEnd) {
511 MOZ_ASSERT(IsDefinite());
512 MOZ_ASSERT(aGridEnd >= mEnd);
513 uint32_t newStart = aGridEnd - mEnd;
514 mEnd = aGridEnd - mStart;
515 mStart = newStart;
519 * @note We'll use the signed member while resolving definite positions
520 * to line numbers (1-based), which may become negative for implicit lines
521 * to the top/left of the explicit grid. PlaceGridItems() then translates
522 * the whole grid to a 0,0 origin and we'll use the unsigned member from
523 * there on.
525 union {
526 uint32_t mStart;
527 int32_t mUntranslatedStart;
529 union {
530 uint32_t mEnd;
531 int32_t mUntranslatedEnd;
534 protected:
535 LineRange() : mStart(0), mEnd(0) {}
539 * Helper class to construct a LineRange from translated lines.
540 * The ctor only accepts translated definite line numbers.
542 struct nsGridContainerFrame::TranslatedLineRange : public LineRange {
543 TranslatedLineRange(uint32_t aStart, uint32_t aEnd) {
544 MOZ_ASSERT(aStart < aEnd && aEnd <= kTranslatedMaxLine);
545 mStart = aStart;
546 mEnd = aEnd;
551 * A GridArea is the area in the grid for a grid item.
552 * The area is represented by two LineRanges, both of which can be auto
553 * (@see LineRange) in intermediate steps while the item is being placed.
554 * @see PlaceGridItems
556 struct nsGridContainerFrame::GridArea {
557 GridArea(const LineRange& aCols, const LineRange& aRows)
558 : mCols(aCols), mRows(aRows) {}
559 bool IsDefinite() const { return mCols.IsDefinite() && mRows.IsDefinite(); }
560 LineRange& LineRangeForAxis(LogicalAxis aAxis) {
561 return aAxis == eLogicalAxisInline ? mCols : mRows;
563 const LineRange& LineRangeForAxis(LogicalAxis aAxis) const {
564 return aAxis == eLogicalAxisInline ? mCols : mRows;
566 LineRange mCols;
567 LineRange mRows;
570 struct nsGridContainerFrame::GridItemInfo {
572 * Item state per axis.
574 enum StateBits : uint16_t {
575 // clang-format off
576 eIsFlexing = 0x1, // does the item span a flex track?
577 eFirstBaseline = 0x2, // participate in 'first baseline' alignment?
578 // ditto 'last baseline', mutually exclusive w. eFirstBaseline
579 eLastBaseline = 0x4,
580 eIsBaselineAligned = eFirstBaseline | eLastBaseline,
581 // One of e[Self|Content]Baseline is set when eIsBaselineAligned is true
582 eSelfBaseline = 0x8, // is it *-self:[last ]baseline alignment?
583 // Ditto *-content:[last ]baseline. Mutually exclusive w. eSelfBaseline.
584 eContentBaseline = 0x10,
585 // The baseline affects the margin or padding on the item's end side when
586 // this bit is set. In a grid-axis it's always set for eLastBaseline and
587 // always unset for eFirstBaseline. In a masonry-axis, it's set for
588 // baseline groups in the EndStretch set and unset for the StartStretch set.
589 eEndSideBaseline = 0x20,
590 eAllBaselineBits = eIsBaselineAligned | eSelfBaseline | eContentBaseline |
591 eEndSideBaseline,
592 // Should apply Automatic Minimum Size per:
593 // https://drafts.csswg.org/css-grid/#min-size-auto
594 eApplyAutoMinSize = 0x40,
595 // Clamp per https://drafts.csswg.org/css-grid/#min-size-auto
596 eClampMarginBoxMinSize = 0x80,
597 eIsSubgrid = 0x100,
598 // set on subgrids and items in subgrids if they are adjacent to the grid
599 // start/end edge (excluding grid-aligned abs.pos. frames)
600 eStartEdge = 0x200,
601 eEndEdge = 0x400,
602 eEdgeBits = eStartEdge | eEndEdge,
603 // Set if this item was auto-placed in this axis.
604 eAutoPlacement = 0x800,
605 // Set if this item is the last item in its track (masonry layout only)
606 eIsLastItemInMasonryTrack = 0x1000,
607 // clang-format on
610 GridItemInfo(nsIFrame* aFrame, const GridArea& aArea);
612 static bool BaselineAlignmentAffectsEndSide(StateBits state) {
613 return state & StateBits::eEndSideBaseline;
617 * Inhibit subgrid layout unless the item is placed in the first "track" in
618 * a parent masonry-axis, or has definite placement or spans all tracks in
619 * the parent grid-axis.
620 * TODO: this is stricter than what the Masonry proposal currently states
621 * (bug 1627581)
623 void MaybeInhibitSubgridInMasonry(nsGridContainerFrame* aParent,
624 uint32_t aGridAxisTrackCount);
627 * Inhibit subgridding in aAxis for this item.
629 void InhibitSubgrid(nsGridContainerFrame* aParent, LogicalAxis aAxis);
632 * Return a copy of this item with its row/column data swapped.
634 GridItemInfo Transpose() const {
635 GridItemInfo info(mFrame, GridArea(mArea.mRows, mArea.mCols));
636 info.mState[0] = mState[1];
637 info.mState[1] = mState[0];
638 info.mBaselineOffset[0] = mBaselineOffset[1];
639 info.mBaselineOffset[1] = mBaselineOffset[0];
640 return info;
643 /** Swap the start/end sides in aAxis. */
644 inline void ReverseDirection(LogicalAxis aAxis, uint32_t aGridEnd);
646 // Is this item a subgrid in the given container axis?
647 bool IsSubgrid(LogicalAxis aAxis) const {
648 return mState[aAxis] & StateBits::eIsSubgrid;
651 // Is this item a subgrid in either axis?
652 bool IsSubgrid() const {
653 return IsSubgrid(eLogicalAxisInline) || IsSubgrid(eLogicalAxisBlock);
656 // Return the (inner) grid container frame associated with this subgrid item.
657 nsGridContainerFrame* SubgridFrame() const {
658 MOZ_ASSERT(IsSubgrid());
659 nsGridContainerFrame* gridFrame = GetGridContainerFrame(mFrame);
660 MOZ_ASSERT(gridFrame && gridFrame->IsSubgrid());
661 return gridFrame;
665 * Adjust our grid areas to account for removed auto-fit tracks in aAxis.
667 void AdjustForRemovedTracks(LogicalAxis aAxis,
668 const nsTArray<uint32_t>& aNumRemovedTracks);
671 * If the item is [align|justify]-self:[last ]baseline aligned in the given
672 * axis then set aBaselineOffset to the baseline offset and return aAlign.
673 * Otherwise, return a fallback alignment.
675 StyleAlignFlags GetSelfBaseline(StyleAlignFlags aAlign, LogicalAxis aAxis,
676 nscoord* aBaselineOffset) const {
677 MOZ_ASSERT(aAlign == StyleAlignFlags::BASELINE ||
678 aAlign == StyleAlignFlags::LAST_BASELINE);
679 if (!(mState[aAxis] & eSelfBaseline)) {
680 return aAlign == StyleAlignFlags::BASELINE ? StyleAlignFlags::SELF_START
681 : StyleAlignFlags::SELF_END;
683 *aBaselineOffset = mBaselineOffset[aAxis];
684 return aAlign;
687 // Return true if we should apply Automatic Minimum Size to this item.
688 // https://drafts.csswg.org/css-grid/#min-size-auto
689 // @note the caller should also check that the item spans at least one track
690 // that has a min track sizing function that is 'auto' before applying it.
691 bool ShouldApplyAutoMinSize(WritingMode aContainerWM,
692 LogicalAxis aContainerAxis,
693 nscoord aPercentageBasis) const {
694 const bool isInlineAxis = aContainerAxis == eLogicalAxisInline;
695 const auto* pos =
696 mFrame->IsTableWrapperFrame()
697 ? mFrame->PrincipalChildList().FirstChild()->StylePosition()
698 : mFrame->StylePosition();
699 const auto& size =
700 isInlineAxis ? pos->ISize(aContainerWM) : pos->BSize(aContainerWM);
701 // max-content and min-content should behave as initial value in block axis.
702 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
703 // for block size dimension on sizing properties (e.g. height), so we
704 // treat it as `auto`.
705 bool isAuto = size.IsAuto() ||
706 (isInlineAxis ==
707 aContainerWM.IsOrthogonalTo(mFrame->GetWritingMode()) &&
708 size.BehavesLikeInitialValueOnBlockAxis());
709 // NOTE: if we have a definite size then our automatic minimum size
710 // can't affect our size. Excluding these simplifies applying
711 // the clamping in the right cases later.
712 if (!isAuto && !::IsPercentOfIndefiniteSize(size, aPercentageBasis)) {
713 return false;
715 const auto& minSize = isInlineAxis ? pos->MinISize(aContainerWM)
716 : pos->MinBSize(aContainerWM);
717 // max-content and min-content should behave as initial value in block axis.
718 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
719 // for block size dimension on sizing properties (e.g. height), so we
720 // treat it as `auto`.
721 isAuto = minSize.IsAuto() ||
722 (isInlineAxis ==
723 aContainerWM.IsOrthogonalTo(mFrame->GetWritingMode()) &&
724 minSize.BehavesLikeInitialValueOnBlockAxis());
725 return isAuto &&
726 mFrame->StyleDisplay()->mOverflowX == StyleOverflow::Visible;
729 #ifdef DEBUG
730 void Dump() const;
731 #endif
733 static bool IsStartRowLessThan(const GridItemInfo* a, const GridItemInfo* b) {
734 return a->mArea.mRows.mStart < b->mArea.mRows.mStart;
737 // Sorting functions for 'masonry-auto-flow:next'. We sort the items that
738 // were placed into the first track by the Grid placement algorithm first
739 // (to honor that placement). All other items will be placed by the Masonry
740 // layout algorithm (their Grid placement in the masonry axis is irrelevant).
741 static bool RowMasonryOrdered(const GridItemInfo* a, const GridItemInfo* b) {
742 return a->mArea.mRows.mStart == 0 && b->mArea.mRows.mStart != 0 &&
743 !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
745 static bool ColMasonryOrdered(const GridItemInfo* a, const GridItemInfo* b) {
746 return a->mArea.mCols.mStart == 0 && b->mArea.mCols.mStart != 0 &&
747 !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
750 // Sorting functions for 'masonry-auto-flow:definite-first'. Similar to
751 // the above, but here we also sort items with a definite item placement in
752 // the grid axis in track order before 'auto'-placed items. We also sort all
753 // continuations first since they use the same placement as their
754 // first-in-flow (we treat them as "definite" regardless of eAutoPlacement).
755 static bool RowMasonryDefiniteFirst(const GridItemInfo* a,
756 const GridItemInfo* b) {
757 bool isContinuationA = a->mFrame->GetPrevInFlow();
758 bool isContinuationB = b->mFrame->GetPrevInFlow();
759 if (isContinuationA != isContinuationB) {
760 return isContinuationA;
762 auto masonryA = a->mArea.mRows.mStart;
763 auto gridA = a->mState[eLogicalAxisInline] & StateBits::eAutoPlacement;
764 auto masonryB = b->mArea.mRows.mStart;
765 auto gridB = b->mState[eLogicalAxisInline] & StateBits::eAutoPlacement;
766 return (masonryA == 0 ? masonryB != 0 : (masonryB != 0 && gridA < gridB)) &&
767 !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
769 static bool ColMasonryDefiniteFirst(const GridItemInfo* a,
770 const GridItemInfo* b) {
771 MOZ_ASSERT(!a->mFrame->GetPrevInFlow() && !b->mFrame->GetPrevInFlow(),
772 "fragmentation not supported in inline axis");
773 auto masonryA = a->mArea.mCols.mStart;
774 auto gridA = a->mState[eLogicalAxisBlock] & StateBits::eAutoPlacement;
775 auto masonryB = b->mArea.mCols.mStart;
776 auto gridB = b->mState[eLogicalAxisBlock] & StateBits::eAutoPlacement;
777 return (masonryA == 0 ? masonryB != 0 : (masonryB != 0 && gridA < gridB)) &&
778 !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
781 nsIFrame* const mFrame;
782 GridArea mArea;
783 // Offset from the margin edge to the baseline (LogicalAxis index). It's from
784 // the start edge when eFirstBaseline is set, end edge otherwise. It's mutable
785 // since we update the value fairly late (just before reflowing the item).
786 mutable nscoord mBaselineOffset[2];
787 mutable StateBits mState[2]; // state bits per axis (LogicalAxis index)
788 static_assert(mozilla::eLogicalAxisBlock == 0, "unexpected index value");
789 static_assert(mozilla::eLogicalAxisInline == 1, "unexpected index value");
792 using GridItemInfo = nsGridContainerFrame::GridItemInfo;
793 using ItemState = GridItemInfo::StateBits;
794 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ItemState)
796 GridItemInfo::GridItemInfo(nsIFrame* aFrame, const GridArea& aArea)
797 : mFrame(aFrame), mArea(aArea) {
798 mState[eLogicalAxisBlock] =
799 StateBits(mArea.mRows.mStart == kAutoLine ? eAutoPlacement : 0);
800 mState[eLogicalAxisInline] =
801 StateBits(mArea.mCols.mStart == kAutoLine ? eAutoPlacement : 0);
802 if (auto* gridFrame = GetGridContainerFrame(mFrame)) {
803 auto parentWM = aFrame->GetParent()->GetWritingMode();
804 bool isOrthogonal = parentWM.IsOrthogonalTo(gridFrame->GetWritingMode());
805 if (gridFrame->IsColSubgrid()) {
806 mState[isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline] |=
807 StateBits::eIsSubgrid;
809 if (gridFrame->IsRowSubgrid()) {
810 mState[isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock] |=
811 StateBits::eIsSubgrid;
814 mBaselineOffset[eLogicalAxisBlock] = nscoord(0);
815 mBaselineOffset[eLogicalAxisInline] = nscoord(0);
818 void GridItemInfo::ReverseDirection(LogicalAxis aAxis, uint32_t aGridEnd) {
819 mArea.LineRangeForAxis(aAxis).ReverseDirection(aGridEnd);
820 ItemState& state = mState[aAxis];
821 ItemState newState = state & ~ItemState::eEdgeBits;
822 if (state & ItemState::eStartEdge) {
823 newState |= ItemState::eEndEdge;
825 if (state & ItemState::eEndEdge) {
826 newState |= ItemState::eStartEdge;
828 state = newState;
831 void GridItemInfo::InhibitSubgrid(nsGridContainerFrame* aParent,
832 LogicalAxis aAxis) {
833 MOZ_ASSERT(IsSubgrid(aAxis));
834 auto bit = NS_STATE_GRID_IS_COL_SUBGRID;
835 if (aParent->GetWritingMode().IsOrthogonalTo(mFrame->GetWritingMode()) !=
836 (aAxis == eLogicalAxisBlock)) {
837 bit = NS_STATE_GRID_IS_ROW_SUBGRID;
839 MOZ_ASSERT(SubgridFrame()->HasAnyStateBits(bit));
840 SubgridFrame()->RemoveStateBits(bit);
841 mState[aAxis] &= StateBits(~StateBits::eIsSubgrid);
844 void GridItemInfo::MaybeInhibitSubgridInMasonry(nsGridContainerFrame* aParent,
845 uint32_t aGridAxisTrackCount) {
846 if (IsSubgrid(eLogicalAxisInline) && aParent->IsMasonry(eLogicalAxisBlock) &&
847 mArea.mRows.mStart != 0 && mArea.mCols.Extent() != aGridAxisTrackCount &&
848 (mState[eLogicalAxisInline] & eAutoPlacement)) {
849 InhibitSubgrid(aParent, eLogicalAxisInline);
850 return;
852 if (IsSubgrid(eLogicalAxisBlock) && aParent->IsMasonry(eLogicalAxisInline) &&
853 mArea.mCols.mStart != 0 && mArea.mRows.Extent() != aGridAxisTrackCount &&
854 (mState[eLogicalAxisBlock] & eAutoPlacement)) {
855 InhibitSubgrid(aParent, eLogicalAxisBlock);
859 // Each subgrid stores this data about its items etc on a frame property.
860 struct nsGridContainerFrame::Subgrid {
861 Subgrid(const GridArea& aArea, bool aIsOrthogonal, WritingMode aCBWM)
862 : mArea(aArea),
863 mGridColEnd(0),
864 mGridRowEnd(0),
865 mMarginBorderPadding(aCBWM),
866 mIsOrthogonal(aIsOrthogonal) {}
868 // Return the relevant line range for the subgrid column axis.
869 const LineRange& SubgridCols() const {
870 return mIsOrthogonal ? mArea.mRows : mArea.mCols;
872 // Return the relevant line range for the subgrid row axis.
873 const LineRange& SubgridRows() const {
874 return mIsOrthogonal ? mArea.mCols : mArea.mRows;
877 // The subgrid's items.
878 nsTArray<GridItemInfo> mGridItems;
879 // The subgrid's abs.pos. items.
880 nsTArray<GridItemInfo> mAbsPosItems;
881 // The subgrid's area as a grid item, i.e. in its parent's grid space.
882 GridArea mArea;
883 // The (inner) grid size for the subgrid, zero-based.
884 uint32_t mGridColEnd;
885 uint32_t mGridRowEnd;
886 // The margin+border+padding for the subgrid box in its parent grid's WM.
887 // (This also includes the size of any scrollbars.)
888 LogicalMargin mMarginBorderPadding;
889 // Does the subgrid frame have orthogonal writing-mode to its parent grid
890 // container?
891 bool mIsOrthogonal;
893 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, Subgrid)
895 using Subgrid = nsGridContainerFrame::Subgrid;
897 void GridItemInfo::AdjustForRemovedTracks(
898 LogicalAxis aAxis, const nsTArray<uint32_t>& aNumRemovedTracks) {
899 const bool abspos = mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
900 auto& lines = mArea.LineRangeForAxis(aAxis);
901 if (abspos) {
902 lines.AdjustAbsPosForRemovedTracks(aNumRemovedTracks);
903 } else {
904 lines.AdjustForRemovedTracks(aNumRemovedTracks);
906 if (IsSubgrid()) {
907 auto* subgrid = SubgridFrame()->GetProperty(Subgrid::Prop());
908 if (subgrid) {
909 auto& lines = subgrid->mArea.LineRangeForAxis(aAxis);
910 if (abspos) {
911 lines.AdjustAbsPosForRemovedTracks(aNumRemovedTracks);
912 } else {
913 lines.AdjustForRemovedTracks(aNumRemovedTracks);
920 * Track size data for use by subgrids (which don't do sizing of their own
921 * in a subgridded axis). A non-subgrid container stores its resolved sizes,
922 * but only if it has any subgrid children. A subgrid always stores one.
923 * In a subgridded axis, we copy the parent's sizes (see CopyUsedTrackSizes).
925 * This struct us stored on a frame property, which may be null before the track
926 * sizing step for the given container. A null property is semantically
927 * equivalent to mCanResolveLineRangeSize being false in both axes.
928 * @note the axis used to access this data is in the grid container's own
929 * writing-mode, same as in other track-sizing functions.
931 struct nsGridContainerFrame::UsedTrackSizes {
932 UsedTrackSizes() : mCanResolveLineRangeSize{false, false} {}
935 * Setup mSizes by copying track sizes from aFrame's grid container
936 * parent when aAxis is subgridded (and recurse if the parent is a subgrid
937 * that doesn't have sizes yet), or by running the Track Sizing Algo when
938 * the axis is not subgridded (for a subgrid).
939 * Set mCanResolveLineRangeSize[aAxis] to true once we have obtained
940 * sizes for an axis (if it's already true then this method is a NOP).
942 void ResolveTrackSizesForAxis(nsGridContainerFrame* aFrame, LogicalAxis aAxis,
943 gfxContext& aRC);
945 /** Helper function for the above method */
946 void ResolveSubgridTrackSizesForAxis(nsGridContainerFrame* aFrame,
947 LogicalAxis aAxis, Subgrid* aSubgrid,
948 gfxContext& aRC,
949 nscoord aContentBoxSize);
951 // This only has valid sizes when mCanResolveLineRangeSize is true in
952 // the same axis. It may have zero tracks (a grid with only abs.pos.
953 // subgrids/items may have zero tracks).
954 PerLogicalAxis<nsTArray<TrackSize>> mSizes;
955 // True if mSizes can be used to resolve line range sizes in an axis.
956 PerLogicalAxis<bool> mCanResolveLineRangeSize;
958 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, UsedTrackSizes)
960 using UsedTrackSizes = nsGridContainerFrame::UsedTrackSizes;
962 #ifdef DEBUG
963 void nsGridContainerFrame::GridItemInfo::Dump() const {
964 auto Dump1 = [this](const char* aMsg, LogicalAxis aAxis) {
965 auto state = mState[aAxis];
966 if (!state) {
967 return;
969 printf("%s", aMsg);
970 if (state & ItemState::eEdgeBits) {
971 printf("subgrid-adjacent-edges(");
972 if (state & ItemState::eStartEdge) {
973 printf("start ");
975 if (state & ItemState::eEndEdge) {
976 printf("end");
978 printf(") ");
980 if (state & ItemState::eAutoPlacement) {
981 printf("masonry-auto ");
983 if (state & ItemState::eIsSubgrid) {
984 printf("subgrid ");
986 if (state & ItemState::eIsFlexing) {
987 printf("flexing ");
989 if (state & ItemState::eApplyAutoMinSize) {
990 printf("auto-min-size ");
992 if (state & ItemState::eClampMarginBoxMinSize) {
993 printf("clamp ");
995 if (state & ItemState::eIsLastItemInMasonryTrack) {
996 printf("last-in-track ");
998 if (state & ItemState::eFirstBaseline) {
999 printf("first baseline %s-alignment ",
1000 (state & ItemState::eSelfBaseline) ? "self" : "content");
1002 if (state & ItemState::eLastBaseline) {
1003 printf("last baseline %s-alignment ",
1004 (state & ItemState::eSelfBaseline) ? "self" : "content");
1006 if (state & ItemState::eIsBaselineAligned) {
1007 printf("%.2fpx", NSAppUnitsToFloatPixels(mBaselineOffset[aAxis],
1008 AppUnitsPerCSSPixel()));
1010 printf("\n");
1012 printf("grid-row: %d %d\n", mArea.mRows.mStart, mArea.mRows.mEnd);
1013 Dump1(" grid block-axis: ", eLogicalAxisBlock);
1014 printf("grid-column: %d %d\n", mArea.mCols.mStart, mArea.mCols.mEnd);
1015 Dump1(" grid inline-axis: ", eLogicalAxisInline);
1017 #endif
1020 * Encapsulates CSS track-sizing functions.
1022 struct nsGridContainerFrame::TrackSizingFunctions {
1023 private:
1024 TrackSizingFunctions(const GridTemplate& aTemplate,
1025 const StyleImplicitGridTracks& aAutoSizing,
1026 const Maybe<size_t>& aRepeatAutoIndex, bool aIsSubgrid)
1027 : mTemplate(aTemplate),
1028 mTrackListValues(aTemplate.TrackListValues()),
1029 mAutoSizing(aAutoSizing),
1030 mExplicitGridOffset(0),
1031 mRepeatAutoStart(aRepeatAutoIndex.valueOr(0)),
1032 mRepeatAutoEnd(mRepeatAutoStart),
1033 mHasRepeatAuto(aRepeatAutoIndex.isSome()) {
1034 MOZ_ASSERT(!mHasRepeatAuto || !aIsSubgrid,
1035 "a track-list for a subgrid can't have an <auto-repeat> track");
1036 if (!aIsSubgrid) {
1037 ExpandNonRepeatAutoTracks();
1040 #ifdef DEBUG
1041 if (mHasRepeatAuto) {
1042 MOZ_ASSERT(mExpandedTracks.Length() >= 1);
1043 const unsigned maxTrack = kMaxLine - 1;
1044 // If the exanded tracks are out of range of the maximum track, we
1045 // can't compare the repeat-auto start. It will be removed later during
1046 // grid item placement in that situation.
1047 if (mExpandedTracks.Length() < maxTrack) {
1048 MOZ_ASSERT(mRepeatAutoStart < mExpandedTracks.Length());
1051 #endif
1054 public:
1055 TrackSizingFunctions(const GridTemplate& aGridTemplate,
1056 const StyleImplicitGridTracks& aAutoSizing,
1057 bool aIsSubgrid)
1058 : TrackSizingFunctions(aGridTemplate, aAutoSizing,
1059 aGridTemplate.RepeatAutoIndex(), aIsSubgrid) {}
1061 private:
1062 enum { ForSubgridFallbackTag };
1063 TrackSizingFunctions(const GridTemplate& aGridTemplate,
1064 const StyleImplicitGridTracks& aAutoSizing,
1065 decltype(ForSubgridFallbackTag))
1066 : TrackSizingFunctions(aGridTemplate, aAutoSizing, Nothing(),
1067 /* aIsSubgrid */ true) {}
1069 public:
1071 * This is used in a subgridded axis to resolve sizes before its parent's
1072 * sizes are known for intrinsic sizing purposes. It copies the slice of
1073 * the nearest non-subgridded axis' track sizing functions spanned by
1074 * the subgrid.
1076 * FIXME: this was written before there was a spec... the spec now says:
1077 * "If calculating the layout of a grid item in this step depends on
1078 * the available space in the block axis, assume the available space
1079 * that it would have if any row with a definite max track sizing
1080 * function had that size and all other rows were infinite."
1081 * https://drafts.csswg.org/css-grid-2/#subgrid-sizing
1083 static TrackSizingFunctions ForSubgridFallback(
1084 nsGridContainerFrame* aSubgridFrame, const Subgrid* aSubgrid,
1085 nsGridContainerFrame* aParentGridContainer, LogicalAxis aParentAxis) {
1086 MOZ_ASSERT(aSubgrid);
1087 MOZ_ASSERT(aSubgridFrame->IsSubgrid(aSubgrid->mIsOrthogonal
1088 ? GetOrthogonalAxis(aParentAxis)
1089 : aParentAxis));
1090 nsGridContainerFrame* parent = aParentGridContainer;
1091 auto parentAxis = aParentAxis;
1092 LineRange range = aSubgrid->mArea.LineRangeForAxis(parentAxis);
1093 // Find our nearest non-subgridded axis and use its track sizing functions.
1094 while (parent->IsSubgrid(parentAxis)) {
1095 const auto* parentSubgrid = parent->GetProperty(Subgrid::Prop());
1096 auto* grandParent = parent->ParentGridContainerForSubgrid();
1097 auto grandParentWM = grandParent->GetWritingMode();
1098 bool isSameDirInAxis =
1099 parent->GetWritingMode().ParallelAxisStartsOnSameSide(parentAxis,
1100 grandParentWM);
1101 if (MOZ_UNLIKELY(!isSameDirInAxis)) {
1102 auto end = parentAxis == eLogicalAxisBlock ? parentSubgrid->mGridRowEnd
1103 : parentSubgrid->mGridColEnd;
1104 range.ReverseDirection(end);
1105 // range is now in the same direction as the grand-parent's axis
1107 auto grandParentAxis = parentSubgrid->mIsOrthogonal
1108 ? GetOrthogonalAxis(parentAxis)
1109 : parentAxis;
1110 const auto& parentRange =
1111 parentSubgrid->mArea.LineRangeForAxis(grandParentAxis);
1112 range.Translate(parentRange.mStart);
1113 // range is now in the grand-parent's coordinates
1114 parentAxis = grandParentAxis;
1115 parent = grandParent;
1117 const auto* pos = parent->StylePosition();
1118 const auto isInlineAxis = parentAxis == eLogicalAxisInline;
1119 const auto& szf =
1120 isInlineAxis ? pos->mGridTemplateRows : pos->mGridTemplateColumns;
1121 const auto& autoSizing =
1122 isInlineAxis ? pos->mGridAutoColumns : pos->mGridAutoRows;
1123 return TrackSizingFunctions(szf, autoSizing, ForSubgridFallbackTag);
1127 * Initialize the number of auto-fill/fit tracks to use.
1128 * This can be zero if no auto-fill/fit track was specified, or if the repeat
1129 * begins after the maximum allowed track.
1131 void InitRepeatTracks(const NonNegativeLengthPercentageOrNormal& aGridGap,
1132 nscoord aMinSize, nscoord aSize, nscoord aMaxSize) {
1133 const uint32_t maxTrack = kMaxLine - 1;
1134 // Check for a repeat after the maximum allowed track.
1135 if (MOZ_UNLIKELY(mRepeatAutoStart >= maxTrack)) {
1136 mHasRepeatAuto = false;
1137 mRepeatAutoStart = 0;
1138 mRepeatAutoEnd = 0;
1139 return;
1141 uint32_t repeatTracks =
1142 CalculateRepeatFillCount(aGridGap, aMinSize, aSize, aMaxSize) *
1143 NumRepeatTracks();
1144 // Clamp the number of repeat tracks to the maximum possible track.
1145 repeatTracks = std::min(repeatTracks, maxTrack - mRepeatAutoStart);
1146 SetNumRepeatTracks(repeatTracks);
1147 // Blank out the removed flags for each of these tracks.
1148 mRemovedRepeatTracks.SetLength(repeatTracks);
1149 for (auto& track : mRemovedRepeatTracks) {
1150 track = false;
1154 uint32_t CalculateRepeatFillCount(
1155 const NonNegativeLengthPercentageOrNormal& aGridGap, nscoord aMinSize,
1156 nscoord aSize, nscoord aMaxSize) const {
1157 if (!mHasRepeatAuto) {
1158 return 0;
1160 // At this point no tracks will have been collapsed, so the RepeatEndDelta
1161 // should not be negative.
1162 MOZ_ASSERT(RepeatEndDelta() >= 0);
1163 // Note that this uses NumRepeatTracks and mRepeatAutoStart/End, although
1164 // the result of this method is used to change those values to a fully
1165 // expanded value. Spec quotes are from
1166 // https://drafts.csswg.org/css-grid/#repeat-notation
1167 const uint32_t numTracks = mExpandedTracks.Length() + RepeatEndDelta();
1168 MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track");
1169 if (MOZ_UNLIKELY(numTracks >= kMaxLine)) {
1170 // The fixed tracks plus an entire repetition is either larger or as
1171 // large as the maximum track, so we do not need to measure how many
1172 // repetitions will fit. This also avoids needing to check for if
1173 // kMaxLine - numTracks would underflow at the end where we clamp the
1174 // result.
1175 return 1;
1177 nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize;
1178 if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) {
1179 // "Otherwise, the specified track list repeats only once."
1180 return 1;
1182 nscoord repeatTrackSum = 0;
1183 // Note that one repeat() track size is included in |sum| in this loop.
1184 nscoord sum = 0;
1185 const nscoord percentBasis = aSize;
1186 for (uint32_t i = 0; i < numTracks; ++i) {
1187 // "treating each track as its max track sizing function if that is
1188 // definite or as its minimum track sizing function otherwise"
1189 // https://drafts.csswg.org/css-grid/#valdef-repeat-auto-fill
1190 const auto& sizingFunction = SizingFor(i);
1191 const auto& maxCoord = sizingFunction.GetMax();
1192 const auto* coord = &maxCoord;
1193 if (!coord->IsBreadth()) {
1194 coord = &sizingFunction.GetMin();
1195 if (!coord->IsBreadth()) {
1196 return 1;
1199 nscoord trackSize = ::ResolveToDefiniteSize(*coord, percentBasis);
1200 if (i >= mRepeatAutoStart && i < mRepeatAutoEnd) {
1201 // Use a minimum 1px for the repeat() track-size.
1202 if (trackSize < AppUnitsPerCSSPixel()) {
1203 trackSize = AppUnitsPerCSSPixel();
1205 repeatTrackSum += trackSize;
1207 sum += trackSize;
1209 nscoord gridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aSize);
1210 if (numTracks > 1) {
1211 // Add grid-gaps for all the tracks including the repeat() track.
1212 sum += gridGap * (numTracks - 1);
1214 // Calculate the max number of tracks that fits without overflow.
1215 nscoord available = maxFill != NS_UNCONSTRAINEDSIZE ? maxFill : aMinSize;
1216 nscoord spaceToFill = available - sum;
1217 if (spaceToFill <= 0) {
1218 // "if any number of repetitions would overflow, then 1 repetition"
1219 return 1;
1221 // Calculate the max number of tracks that fits without overflow.
1222 // Since we already have one repetition in sum, we can simply add one grid
1223 // gap for each element in the repeat.
1224 div_t q = div(spaceToFill, repeatTrackSum + gridGap * NumRepeatTracks());
1225 // The +1 here is for the one repeat track we already accounted for above.
1226 uint32_t numRepeatTracks = q.quot + 1;
1227 if (q.rem != 0 && maxFill == NS_UNCONSTRAINEDSIZE) {
1228 // "Otherwise, if the grid container has a definite min size in
1229 // the relevant axis, the number of repetitions is the largest possible
1230 // positive integer that fulfills that minimum requirement."
1231 ++numRepeatTracks; // one more to ensure the grid is at least min-size
1233 // Clamp the number of repeat tracks so that the last line <= kMaxLine.
1234 // (note that |numTracks| already includes one repeat() track)
1235 MOZ_ASSERT(numTracks >= NumRepeatTracks());
1236 const uint32_t maxRepeatTrackCount = kMaxLine - numTracks;
1237 const uint32_t maxRepetitions = maxRepeatTrackCount / NumRepeatTracks();
1238 return std::min(numRepeatTracks, maxRepetitions);
1242 * Compute the explicit grid end line number (in a zero-based grid).
1243 * @param aGridTemplateAreasEnd 'grid-template-areas' end line in this axis
1245 uint32_t ComputeExplicitGridEnd(uint32_t aGridTemplateAreasEnd) {
1246 uint32_t end = NumExplicitTracks() + 1;
1247 end = std::max(end, aGridTemplateAreasEnd);
1248 end = std::min(end, uint32_t(kMaxLine));
1249 return end;
1251 const StyleTrackSize& SizingFor(uint32_t aTrackIndex) const {
1252 static const StyleTrackSize kAutoTrackSize =
1253 StyleTrackSize::Breadth(StyleTrackBreadth::Auto());
1254 // |aIndex| is the relative index to mAutoSizing. A negative value means it
1255 // is the last Nth element.
1256 auto getImplicitSize = [this](int32_t aIndex) -> const StyleTrackSize& {
1257 MOZ_ASSERT(!(mAutoSizing.Length() == 1 &&
1258 mAutoSizing.AsSpan()[0] == kAutoTrackSize),
1259 "It's impossible to have one track with auto value because we "
1260 "filter out this case during parsing");
1262 if (mAutoSizing.IsEmpty()) {
1263 return kAutoTrackSize;
1266 // If multiple track sizes are given, the pattern is repeated as necessary
1267 // to find the size of the implicit tracks.
1268 int32_t i = aIndex % int32_t(mAutoSizing.Length());
1269 if (i < 0) {
1270 i += mAutoSizing.Length();
1272 return mAutoSizing.AsSpan()[i];
1275 if (MOZ_UNLIKELY(aTrackIndex < mExplicitGridOffset)) {
1276 // The last implicit grid track before the explicit grid receives the
1277 // last specified size, and so on backwards. Therefore we pass the
1278 // negative relative index to imply that we should get the implicit size
1279 // from the last Nth specified grid auto size.
1280 return getImplicitSize(int32_t(aTrackIndex) -
1281 int32_t(mExplicitGridOffset));
1283 uint32_t index = aTrackIndex - mExplicitGridOffset;
1284 MOZ_ASSERT(mRepeatAutoStart <= mRepeatAutoEnd);
1286 if (index >= mRepeatAutoStart) {
1287 if (index < mRepeatAutoEnd) {
1288 // Expand the repeat tracks.
1289 const auto& indices = mExpandedTracks[mRepeatAutoStart];
1290 const TrackListValue& value = mTrackListValues[indices.first];
1292 // We expect the default to be used for all track repeats.
1293 MOZ_ASSERT(indices.second == 0);
1295 const auto& repeatTracks = value.AsTrackRepeat().track_sizes.AsSpan();
1297 // Find the repeat track to use, skipping over any collapsed tracks.
1298 const uint32_t finalRepeatIndex = (index - mRepeatAutoStart);
1299 uint32_t repeatWithCollapsed = 0;
1300 // NOTE: We need SizingFor before the final collapsed tracks are known.
1301 // We know that it's invalid to have empty mRemovedRepeatTracks when
1302 // there are any repeat tracks, so we can detect that situation here.
1303 if (mRemovedRepeatTracks.IsEmpty()) {
1304 repeatWithCollapsed = finalRepeatIndex;
1305 } else {
1306 // Count up through the repeat tracks, until we have seen
1307 // finalRepeatIndex number of non-collapsed tracks.
1308 for (uint32_t repeatNoCollapsed = 0;
1309 repeatNoCollapsed < finalRepeatIndex; repeatWithCollapsed++) {
1310 if (!mRemovedRepeatTracks[repeatWithCollapsed]) {
1311 repeatNoCollapsed++;
1314 // If we stopped iterating on a collapsed track, continue to the next
1315 // non-collapsed track.
1316 while (mRemovedRepeatTracks[repeatWithCollapsed]) {
1317 repeatWithCollapsed++;
1320 return repeatTracks[repeatWithCollapsed % repeatTracks.Length()];
1321 } else {
1322 // The index is after the repeat auto range, adjust it to skip over the
1323 // repeat value. This will have no effect if there is no auto repeat,
1324 // since then RepeatEndDelta will return zero.
1325 index -= RepeatEndDelta();
1328 if (index >= mExpandedTracks.Length()) {
1329 return getImplicitSize(index - mExpandedTracks.Length());
1331 auto& indices = mExpandedTracks[index];
1332 const TrackListValue& value = mTrackListValues[indices.first];
1333 if (value.IsTrackSize()) {
1334 MOZ_ASSERT(indices.second == 0);
1335 return value.AsTrackSize();
1337 return value.AsTrackRepeat().track_sizes.AsSpan()[indices.second];
1339 const StyleTrackBreadth& MaxSizingFor(uint32_t aTrackIndex) const {
1340 return SizingFor(aTrackIndex).GetMax();
1342 const StyleTrackBreadth& MinSizingFor(uint32_t aTrackIndex) const {
1343 return SizingFor(aTrackIndex).GetMin();
1345 uint32_t NumExplicitTracks() const {
1346 return mExpandedTracks.Length() + RepeatEndDelta();
1348 uint32_t NumRepeatTracks() const { return mRepeatAutoEnd - mRepeatAutoStart; }
1349 // The difference between mExplicitGridEnd and mSizingFunctions.Length().
1350 int32_t RepeatEndDelta() const {
1351 return mHasRepeatAuto ? int32_t(NumRepeatTracks()) - 1 : 0;
1353 void SetNumRepeatTracks(uint32_t aNumRepeatTracks) {
1354 MOZ_ASSERT(mHasRepeatAuto || aNumRepeatTracks == 0);
1355 mRepeatAutoEnd = mRepeatAutoStart + aNumRepeatTracks;
1358 // Store mTrackListValues into mExpandedTracks with `repeat(INTEGER, ...)`
1359 // tracks expanded.
1360 void ExpandNonRepeatAutoTracks() {
1361 for (size_t i = 0; i < mTrackListValues.Length(); ++i) {
1362 auto& value = mTrackListValues[i];
1363 if (value.IsTrackSize()) {
1364 mExpandedTracks.EmplaceBack(i, 0);
1365 continue;
1367 auto& repeat = value.AsTrackRepeat();
1368 if (!repeat.count.IsNumber()) {
1369 MOZ_ASSERT(i == mRepeatAutoStart);
1370 mRepeatAutoStart = mExpandedTracks.Length();
1371 mRepeatAutoEnd = mRepeatAutoStart + repeat.track_sizes.Length();
1372 mExpandedTracks.EmplaceBack(i, 0);
1373 continue;
1375 for (auto j : IntegerRange(repeat.count.AsNumber())) {
1376 Unused << j;
1377 size_t trackSizesCount = repeat.track_sizes.Length();
1378 for (auto k : IntegerRange(trackSizesCount)) {
1379 mExpandedTracks.EmplaceBack(i, k);
1383 if (MOZ_UNLIKELY(mExpandedTracks.Length() > kMaxLine - 1)) {
1384 mExpandedTracks.TruncateLength(kMaxLine - 1);
1385 if (mHasRepeatAuto && mRepeatAutoStart > kMaxLine - 1) {
1386 // The `repeat(auto-fill/fit)` track is outside the clamped grid.
1387 mHasRepeatAuto = false;
1392 // Some style data references, for easy access.
1393 const GridTemplate& mTemplate;
1394 const Span<const TrackListValue> mTrackListValues;
1395 const StyleImplicitGridTracks& mAutoSizing;
1396 // An array from expanded track sizes (without expanding auto-repeat, which is
1397 // included just once at `mRepeatAutoStart`).
1399 // Each entry contains two indices, the first into mTrackListValues, and a
1400 // second one inside mTrackListValues' repeat value, if any, or zero
1401 // otherwise.
1402 nsTArray<std::pair<size_t, size_t>> mExpandedTracks;
1403 // Offset from the start of the implicit grid to the first explicit track.
1404 uint32_t mExplicitGridOffset;
1405 // The index of the repeat(auto-fill/fit) track, or zero if there is none.
1406 // Relative to mExplicitGridOffset (repeat tracks are explicit by definition).
1407 uint32_t mRepeatAutoStart;
1408 // The (hypothetical) index of the last such repeat() track.
1409 uint32_t mRepeatAutoEnd;
1410 // True if there is a specified repeat(auto-fill/fit) track.
1411 bool mHasRepeatAuto;
1412 // True if this track (relative to mRepeatAutoStart) is a removed auto-fit.
1413 // Indexed relative to mExplicitGridOffset + mRepeatAutoStart.
1414 nsTArray<bool> mRemovedRepeatTracks;
1418 * Utility class to find line names. It provides an interface to lookup line
1419 * names with a dynamic number of repeat(auto-fill/fit) tracks taken into
1420 * account.
1422 class MOZ_STACK_CLASS nsGridContainerFrame::LineNameMap {
1423 public:
1425 * Create a LineNameMap.
1426 * @param aStylePosition the style for the grid container
1427 * @param aImplicitNamedAreas the implicit areas for the grid container
1428 * @param aGridTemplate is the grid-template-rows/columns data for this axis
1429 * @param aParentLineNameMap the parent grid's map parallel to this map, or
1430 * null if this map isn't for a subgrid
1431 * @param aRange the subgrid's range in the parent grid, or null
1432 * @param aIsSameDirection true if our axis progresses in the same direction
1433 * in the subgrid and parent
1435 LineNameMap(const nsStylePosition* aStylePosition,
1436 const ImplicitNamedAreas* aImplicitNamedAreas,
1437 const TrackSizingFunctions& aTracks,
1438 const LineNameMap* aParentLineNameMap, const LineRange* aRange,
1439 bool aIsSameDirection)
1440 : mStylePosition(aStylePosition),
1441 mAreas(aImplicitNamedAreas),
1442 mRepeatAutoStart(aTracks.mRepeatAutoStart),
1443 mRepeatAutoEnd(aTracks.mRepeatAutoEnd),
1444 mRepeatEndDelta(aTracks.RepeatEndDelta()),
1445 mParentLineNameMap(aParentLineNameMap),
1446 mRange(aRange),
1447 mIsSameDirection(aIsSameDirection),
1448 mHasRepeatAuto(aTracks.mHasRepeatAuto) {
1449 if (MOZ_UNLIKELY(aRange)) { // subgrid case
1450 mClampMinLine = 1;
1451 mClampMaxLine = 1 + aRange->Extent();
1452 mRepeatAutoEnd = mRepeatAutoStart;
1453 const auto& styleSubgrid = aTracks.mTemplate.AsSubgrid();
1454 const auto fillLen = styleSubgrid->fill_len;
1455 mHasRepeatAuto = fillLen != 0;
1456 if (mHasRepeatAuto) {
1457 const auto& lineNameLists = styleSubgrid->names;
1458 const int32_t extraAutoFillLineCount =
1459 mClampMaxLine - lineNameLists.Length();
1460 // Maximum possible number of repeat name lists. This must be reduced
1461 // to a whole number of repetitions of the fill length.
1462 const uint32_t possibleRepeatLength =
1463 std::max<int32_t>(0, extraAutoFillLineCount + fillLen);
1464 const uint32_t repeatRemainder = possibleRepeatLength % fillLen;
1465 mRepeatAutoStart = styleSubgrid->fill_start;
1466 mRepeatAutoEnd =
1467 mRepeatAutoStart + possibleRepeatLength - repeatRemainder;
1469 } else {
1470 mClampMinLine = kMinLine;
1471 mClampMaxLine = kMaxLine;
1472 if (mHasRepeatAuto) {
1473 mTrackAutoRepeatLineNames =
1474 aTracks.mTemplate.GetRepeatAutoValue()->line_names.AsSpan();
1477 ExpandRepeatLineNames(!!aRange, aTracks);
1478 if (mHasRepeatAuto) {
1479 // We need mTemplateLinesEnd to be after all line names.
1480 // mExpandedLineNames has one repetition of the repeat(auto-fit/fill)
1481 // track name lists already, so we must subtract the number of repeat
1482 // track name lists to get to the number of non-repeat tracks, minus 2
1483 // because the first and last line name lists are shared with the
1484 // preceding and following non-repeat line name lists. We then add
1485 // mRepeatEndDelta to include the interior line name lists from repeat
1486 // tracks.
1487 mTemplateLinesEnd = mExpandedLineNames.Length() -
1488 (mTrackAutoRepeatLineNames.Length() - 2) +
1489 mRepeatEndDelta;
1490 } else {
1491 mTemplateLinesEnd = mExpandedLineNames.Length();
1493 MOZ_ASSERT(mHasRepeatAuto || mRepeatEndDelta <= 0);
1494 MOZ_ASSERT(!mHasRepeatAuto || aRange ||
1495 (mExpandedLineNames.Length() >= 2 &&
1496 mRepeatAutoStart <= mExpandedLineNames.Length()));
1499 // Store line names into mExpandedLineNames with `repeat(INTEGER, ...)`
1500 // expanded (for non-subgrid), and all `repeat(...)` expanded (for subgrid).
1501 void ExpandRepeatLineNames(bool aIsSubgrid,
1502 const TrackSizingFunctions& aTracks) {
1503 auto lineNameLists = aTracks.mTemplate.LineNameLists(aIsSubgrid);
1505 const auto& trackListValues = aTracks.mTrackListValues;
1506 const NameList* nameListToMerge = nullptr;
1507 // NOTE(emilio): We rely on std::move clearing out the array.
1508 SmallPointerArray<const NameList> names;
1509 // This adjusts for outputting the repeat auto names in subgrid. In that
1510 // case, all of the repeat values are handled in a single iteration.
1511 const uint32_t subgridRepeatDelta =
1512 (aIsSubgrid && mHasRepeatAuto)
1513 ? (aTracks.mTemplate.AsSubgrid()->fill_len - 1)
1514 : 0;
1515 const uint32_t end = std::min<uint32_t>(
1516 lineNameLists.Length() - subgridRepeatDelta, mClampMaxLine + 1);
1517 for (uint32_t i = 0; i < end; ++i) {
1518 if (aIsSubgrid) {
1519 if (MOZ_UNLIKELY(mHasRepeatAuto && i == mRepeatAutoStart)) {
1520 // XXX expand 'auto-fill' names for subgrid for now since HasNameAt()
1521 // only deals with auto-repeat **tracks** currently.
1522 const auto& styleSubgrid = aTracks.mTemplate.AsSubgrid();
1523 MOZ_ASSERT(styleSubgrid->fill_len > 0);
1524 for (auto j = i; j < mRepeatAutoEnd; ++j) {
1525 const auto repeatIndex = (j - i) % styleSubgrid->fill_len;
1526 names.AppendElement(
1527 &lineNameLists[styleSubgrid->fill_start + repeatIndex]);
1528 mExpandedLineNames.AppendElement(std::move(names));
1530 } else if (mHasRepeatAuto && i > mRepeatAutoStart) {
1531 const auto& styleSubgrid = aTracks.mTemplate.AsSubgrid();
1532 names.AppendElement(&lineNameLists[i + styleSubgrid->fill_len - 1]);
1533 mExpandedLineNames.AppendElement(std::move(names));
1534 } else {
1535 names.AppendElement(&lineNameLists[i]);
1536 mExpandedLineNames.AppendElement(std::move(names));
1538 // XXX expand repeat(<integer>, ...) line names here (bug 1583429)
1539 continue;
1542 if (nameListToMerge) {
1543 names.AppendElement(nameListToMerge);
1544 nameListToMerge = nullptr;
1546 names.AppendElement(&lineNameLists[i]);
1547 if (i >= trackListValues.Length()) {
1548 mExpandedLineNames.AppendElement(std::move(names));
1549 continue;
1551 const auto& value = trackListValues[i];
1552 if (value.IsTrackSize()) {
1553 mExpandedLineNames.AppendElement(std::move(names));
1554 continue;
1556 const auto& repeat = value.AsTrackRepeat();
1557 if (!repeat.count.IsNumber()) {
1558 const auto repeatNames = repeat.line_names.AsSpan();
1559 // If the repeat was truncated due to more than kMaxLine tracks, then
1560 // the repeat will no longer be set on mRepeatAutoStart).
1561 MOZ_ASSERT(!mHasRepeatAuto ||
1562 mRepeatAutoStart == mExpandedLineNames.Length());
1563 MOZ_ASSERT(repeatNames.Length() >= 2);
1564 for (const auto j : IntegerRange(repeatNames.Length() - 1)) {
1565 names.AppendElement(&repeatNames[j]);
1566 mExpandedLineNames.AppendElement(std::move(names));
1568 nameListToMerge = &repeatNames[repeatNames.Length() - 1];
1569 continue;
1571 for (auto j : IntegerRange(repeat.count.AsNumber())) {
1572 Unused << j;
1573 if (nameListToMerge) {
1574 names.AppendElement(nameListToMerge);
1575 nameListToMerge = nullptr;
1577 size_t trackSizesCount = repeat.track_sizes.Length();
1578 auto repeatLineNames = repeat.line_names.AsSpan();
1579 MOZ_ASSERT(repeatLineNames.Length() == trackSizesCount ||
1580 repeatLineNames.Length() == trackSizesCount + 1);
1581 for (auto k : IntegerRange(trackSizesCount)) {
1582 names.AppendElement(&repeatLineNames[k]);
1583 mExpandedLineNames.AppendElement(std::move(names));
1585 if (repeatLineNames.Length() == trackSizesCount + 1) {
1586 nameListToMerge = &repeatLineNames[trackSizesCount];
1591 if (MOZ_UNLIKELY(mExpandedLineNames.Length() > uint32_t(mClampMaxLine))) {
1592 mExpandedLineNames.TruncateLength(mClampMaxLine);
1594 if (MOZ_UNLIKELY(mHasRepeatAuto && aIsSubgrid)) {
1595 mHasRepeatAuto = false; // we've expanded all subgrid auto-fill lines
1600 * Find the aNth occurrence of aName, searching forward if aNth is positive,
1601 * and in reverse if aNth is negative (aNth == 0 is invalid), starting from
1602 * aFromIndex (not inclusive), and return a 1-based line number.
1603 * Also take into account there is an unconditional match at the lines in
1604 * aImplicitLines.
1605 * Return zero if aNth occurrences can't be found. In that case, aNth has
1606 * been decremented with the number of occurrences that were found (if any).
1608 * E.g. to search for "A 2" forward from the start of the grid: aName is "A"
1609 * aNth is 2 and aFromIndex is zero. To search for "A -2", aNth is -2 and
1610 * aFromIndex is ExplicitGridEnd + 1 (which is the line "before" the last
1611 * line when we're searching in reverse). For "span A 2", aNth is 2 when
1612 * used on a grid-[row|column]-end property and -2 for a *-start property,
1613 * and aFromIndex is the line (which we should skip) on the opposite property.
1615 uint32_t FindNamedLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
1616 const nsTArray<uint32_t>& aImplicitLines) const {
1617 MOZ_ASSERT(aName);
1618 MOZ_ASSERT(!aName->IsEmpty());
1619 MOZ_ASSERT(aNth && *aNth != 0);
1620 if (*aNth > 0) {
1621 return FindLine(aName, aNth, aFromIndex, aImplicitLines);
1623 int32_t nth = -*aNth;
1624 int32_t line = RFindLine(aName, &nth, aFromIndex, aImplicitLines);
1625 *aNth = -nth;
1626 return line;
1630 * Return a set of lines in aImplicitLines which matches the area name aName
1631 * on aSide. For example, for aName "a" and aSide being an end side, it
1632 * returns the line numbers which would match "a-end" in the relevant axis.
1633 * For subgrids it includes searching the relevant axis in all ancestor
1634 * grids too (within this subgrid's spanned area). If an ancestor has
1635 * opposite direction, we switch aSide to the opposite logical side so we
1636 * match on the same physical side as the original subgrid we're resolving
1637 * the name for.
1639 void FindNamedAreas(nsAtom* aName, LogicalSide aSide,
1640 nsTArray<uint32_t>& aImplicitLines) const {
1641 // True if we're currently in a map that has the same direction as 'this'.
1642 bool sameDirectionAsThis = true;
1643 uint32_t min = !mParentLineNameMap ? 1 : mClampMinLine;
1644 uint32_t max = mClampMaxLine;
1645 for (auto* map = this; true;) {
1646 uint32_t line = map->FindNamedArea(aName, aSide, min, max);
1647 if (line > 0) {
1648 if (MOZ_LIKELY(sameDirectionAsThis)) {
1649 line -= min - 1;
1650 } else {
1651 line = max - line + 1;
1653 aImplicitLines.AppendElement(line);
1655 auto* parent = map->mParentLineNameMap;
1656 if (!parent) {
1657 if (MOZ_UNLIKELY(aImplicitLines.Length() > 1)) {
1658 // Remove duplicates and sort in ascending order.
1659 aImplicitLines.Sort();
1660 for (size_t i = 0; i < aImplicitLines.Length(); ++i) {
1661 uint32_t prev = aImplicitLines[i];
1662 auto j = i + 1;
1663 const auto start = j;
1664 while (j < aImplicitLines.Length() && aImplicitLines[j] == prev) {
1665 ++j;
1667 if (j != start) {
1668 aImplicitLines.RemoveElementsAt(start, j - start);
1672 return;
1674 if (MOZ_UNLIKELY(!map->mIsSameDirection)) {
1675 aSide = GetOppositeSide(aSide);
1676 sameDirectionAsThis = !sameDirectionAsThis;
1678 min = map->TranslateToParentMap(min);
1679 max = map->TranslateToParentMap(max);
1680 if (min > max) {
1681 MOZ_ASSERT(!map->mIsSameDirection);
1682 std::swap(min, max);
1684 map = parent;
1689 * Return true if any implicit named areas match aName, in this map or
1690 * in any of our ancestor maps.
1692 bool HasImplicitNamedArea(nsAtom* aName) const {
1693 const auto* map = this;
1694 do {
1695 if (map->mAreas && map->mAreas->has(aName)) {
1696 return true;
1698 map = map->mParentLineNameMap;
1699 } while (map);
1700 return false;
1703 // For generating line name data for devtools.
1704 nsTArray<nsTArray<StyleCustomIdent>>
1705 GetResolvedLineNamesForComputedGridTrackInfo() const {
1706 nsTArray<nsTArray<StyleCustomIdent>> result;
1707 for (auto& expandedLine : mExpandedLineNames) {
1708 nsTArray<StyleCustomIdent> line;
1709 for (auto* chunk : expandedLine) {
1710 for (auto& name : chunk->AsSpan()) {
1711 line.AppendElement(name);
1714 result.AppendElement(std::move(line));
1716 return result;
1719 nsTArray<RefPtr<nsAtom>> GetExplicitLineNamesAtIndex(uint32_t aIndex) const {
1720 nsTArray<RefPtr<nsAtom>> lineNames;
1721 if (aIndex < mTemplateLinesEnd) {
1722 const auto nameLists = GetLineNamesAt(aIndex);
1723 for (const NameList* nameList : nameLists) {
1724 for (const auto& name : nameList->AsSpan()) {
1725 lineNames.AppendElement(name.AsAtom());
1729 return lineNames;
1732 const nsTArray<SmallPointerArray<const NameList>>& ExpandedLineNames() const {
1733 return mExpandedLineNames;
1735 const Span<const StyleOwnedSlice<StyleCustomIdent>>&
1736 TrackAutoRepeatLineNames() const {
1737 return mTrackAutoRepeatLineNames;
1739 bool HasRepeatAuto() const { return mHasRepeatAuto; }
1740 uint32_t NumRepeatTracks() const { return mRepeatAutoEnd - mRepeatAutoStart; }
1741 uint32_t RepeatAutoStart() const { return mRepeatAutoStart; }
1743 // The min/max line number (1-based) for clamping.
1744 int32_t mClampMinLine;
1745 int32_t mClampMaxLine;
1747 private:
1748 // Return true if this map represents a subgridded axis.
1749 bool IsSubgridded() const { return mParentLineNameMap != nullptr; }
1752 * @see FindNamedLine, this function searches forward.
1754 uint32_t FindLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
1755 const nsTArray<uint32_t>& aImplicitLines) const {
1756 MOZ_ASSERT(aNth && *aNth > 0);
1757 int32_t nth = *aNth;
1758 // For a subgrid we need to search to the end of the grid rather than
1759 // the end of the local name list, since ancestors might match.
1760 const uint32_t end = IsSubgridded() ? mClampMaxLine : mTemplateLinesEnd;
1761 uint32_t line;
1762 uint32_t i = aFromIndex;
1763 for (; i < end; i = line) {
1764 line = i + 1;
1765 if (Contains(i, aName) || aImplicitLines.Contains(line)) {
1766 if (--nth == 0) {
1767 return line;
1771 for (auto implicitLine : aImplicitLines) {
1772 if (implicitLine > i) {
1773 // implicitLine is after the lines we searched above so it's last.
1774 // (grid-template-areas has more tracks than
1775 // grid-template-[rows|columns])
1776 if (--nth == 0) {
1777 return implicitLine;
1781 MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
1782 *aNth = nth;
1783 return 0;
1787 * @see FindNamedLine, this function searches in reverse.
1789 uint32_t RFindLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
1790 const nsTArray<uint32_t>& aImplicitLines) const {
1791 MOZ_ASSERT(aNth && *aNth > 0);
1792 if (MOZ_UNLIKELY(aFromIndex == 0)) {
1793 return 0; // There are no named lines beyond the start of the explicit
1794 // grid.
1796 --aFromIndex; // (shift aFromIndex so we can treat it as inclusive)
1797 int32_t nth = *aNth;
1798 // Implicit lines may be beyond the explicit grid so we match those
1799 // first if it's within the mTemplateLinesEnd..aFromIndex range.
1800 // aImplicitLines is presumed sorted.
1801 // For a subgrid we need to search to the end of the grid rather than
1802 // the end of the local name list, since ancestors might match.
1803 const uint32_t end = IsSubgridded() ? mClampMaxLine : mTemplateLinesEnd;
1804 for (auto implicitLine : Reversed(aImplicitLines)) {
1805 if (implicitLine <= end) {
1806 break;
1808 if (implicitLine < aFromIndex) {
1809 if (--nth == 0) {
1810 return implicitLine;
1814 for (uint32_t i = std::min(aFromIndex, end); i; --i) {
1815 if (Contains(i - 1, aName) || aImplicitLines.Contains(i)) {
1816 if (--nth == 0) {
1817 return i;
1821 MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
1822 *aNth = nth;
1823 return 0;
1826 // Return true if aName exists at aIndex in this map or any parent map.
1827 bool Contains(uint32_t aIndex, nsAtom* aName) const {
1828 const auto* map = this;
1829 while (true) {
1830 if (aIndex < map->mTemplateLinesEnd && map->HasNameAt(aIndex, aName)) {
1831 return true;
1833 auto* parent = map->mParentLineNameMap;
1834 if (!parent) {
1835 return false;
1837 uint32_t line = map->TranslateToParentMap(aIndex + 1);
1838 MOZ_ASSERT(line >= 1, "expected a 1-based line number");
1839 aIndex = line - 1;
1840 map = parent;
1842 MOZ_ASSERT_UNREACHABLE("we always return from inside the loop above");
1845 static bool Contains(Span<const StyleCustomIdent> aNames, nsAtom* aName) {
1846 for (auto& name : aNames) {
1847 if (name.AsAtom() == aName) {
1848 return true;
1851 return false;
1854 // Return true if aName exists at aIndex in this map.
1855 bool HasNameAt(const uint32_t aIndex, nsAtom* const aName) const {
1856 const auto nameLists = GetLineNamesAt(aIndex);
1857 for (const NameList* nameList : nameLists) {
1858 if (Contains(nameList->AsSpan(), aName)) {
1859 return true;
1862 return false;
1865 // Get the line names at an index.
1866 // This accounts for auto repeat. The results may be spread over multiple name
1867 // lists returned in the array, which is done to avoid unneccessarily copying
1868 // the arrays to concatenate them.
1869 SmallPointerArray<const NameList> GetLineNamesAt(
1870 const uint32_t aIndex) const {
1871 SmallPointerArray<const NameList> names;
1872 // The index into mExpandedLineNames to use, if aIndex doesn't point to a
1873 // name inside of a auto repeat.
1874 uint32_t repeatAdjustedIndex = aIndex;
1875 if (mHasRepeatAuto) {
1876 // If the index is inside of the auto repeat, use the repeat line
1877 // names. Otherwise, if the index is past the end of the repeat it must
1878 // be adjusted to acount for the repeat tracks.
1879 // mExpandedLineNames has the first and last line name lists from the
1880 // repeat in it already, so we can just ignore aIndex == mRepeatAutoStart
1881 // and treat when aIndex == mRepeatAutoEnd the same as any line after the
1882 // the repeat.
1883 const uint32_t maxRepeatLine = mTrackAutoRepeatLineNames.Length() - 1;
1884 if (aIndex > mRepeatAutoStart && aIndex < mRepeatAutoEnd) {
1885 // The index is inside the auto repeat. Calculate the lines to use,
1886 // including the previous repetitions final names when we roll over
1887 // from one repetition to the next.
1888 const uint32_t repeatIndex =
1889 (aIndex - mRepeatAutoStart) % maxRepeatLine;
1890 if (repeatIndex == 0) {
1891 // The index is at the start of a new repetition. The start of the
1892 // first repetition is intentionally ignored above, so this will
1893 // consider both the end of the previous repetition and the start
1894 // the one that contains aIndex.
1895 names.AppendElement(&mTrackAutoRepeatLineNames[maxRepeatLine]);
1897 names.AppendElement(&mTrackAutoRepeatLineNames[repeatIndex]);
1898 return names;
1900 if (aIndex != mRepeatAutoStart && aIndex >= mRepeatAutoEnd) {
1901 // Adjust the index to account for the line names of the repeat.
1902 repeatAdjustedIndex -= mRepeatEndDelta;
1903 repeatAdjustedIndex += mTrackAutoRepeatLineNames.Length() - 2;
1906 MOZ_ASSERT(names.IsEmpty());
1907 // The index is not inside the repeat tracks, or no repeat tracks exist.
1908 const auto& nameLists = mExpandedLineNames[repeatAdjustedIndex];
1909 for (const NameList* nameList : nameLists) {
1910 names.AppendElement(nameList);
1912 return names;
1915 // Translate a subgrid line (1-based) to a parent line (1-based).
1916 uint32_t TranslateToParentMap(uint32_t aLine) const {
1917 if (MOZ_LIKELY(mIsSameDirection)) {
1918 return aLine + mRange->mStart;
1920 MOZ_ASSERT(mRange->mEnd + 1 >= aLine);
1921 return mRange->mEnd - (aLine - 1) + 1;
1925 * Return the 1-based line that match aName in 'grid-template-areas'
1926 * on the side aSide. Clamp the result to aMin..aMax but require
1927 * that some part of the area is inside for it to match.
1928 * Return zero if there is no match.
1930 uint32_t FindNamedArea(nsAtom* aName, LogicalSide aSide, int32_t aMin,
1931 int32_t aMax) const {
1932 if (const NamedArea* area = FindNamedArea(aName)) {
1933 int32_t start = IsBlock(aSide) ? area->rows.start : area->columns.start;
1934 int32_t end = IsBlock(aSide) ? area->rows.end : area->columns.end;
1935 if (IsStart(aSide)) {
1936 if (start >= aMin) {
1937 if (start <= aMax) {
1938 return start;
1940 } else if (end >= aMin) {
1941 return aMin;
1943 } else {
1944 if (end <= aMax) {
1945 if (end >= aMin) {
1946 return end;
1948 } else if (start <= aMax) {
1949 return aMax;
1953 return 0; // no match
1957 * A convenience method to lookup a name in 'grid-template-areas'.
1958 * @return null if not found
1960 const NamedArea* FindNamedArea(nsAtom* aName) const {
1961 if (mStylePosition->mGridTemplateAreas.IsNone()) {
1962 return nullptr;
1964 const auto areas = mStylePosition->mGridTemplateAreas.AsAreas();
1965 for (const NamedArea& area : areas->areas.AsSpan()) {
1966 if (area.name.AsAtom() == aName) {
1967 return &area;
1970 return nullptr;
1973 // Some style data references, for easy access.
1974 const nsStylePosition* mStylePosition;
1975 const ImplicitNamedAreas* mAreas;
1976 // The expanded list of line-names. Each entry is usually a single NameList,
1977 // but can be multiple in the case where repeat() expands to something that
1978 // has a line name list at the end.
1979 nsTArray<SmallPointerArray<const NameList>> mExpandedLineNames;
1980 // The repeat(auto-fill/fit) track value, if any. (always empty for subgrid)
1981 Span<const StyleOwnedSlice<StyleCustomIdent>> mTrackAutoRepeatLineNames;
1982 // The index of the repeat(auto-fill/fit) track, or zero if there is none.
1983 uint32_t mRepeatAutoStart;
1984 // The index one past the end of the repeat(auto-fill/fit) tracks. Equal to
1985 // mRepeatAutoStart if there are no repeat(auto-fill/fit) tracks.
1986 uint32_t mRepeatAutoEnd;
1987 // The total number of repeat tracks minus 1.
1988 int32_t mRepeatEndDelta;
1989 // The end of the line name lists with repeat(auto-fill/fit) tracks accounted
1990 // for.
1991 uint32_t mTemplateLinesEnd;
1993 // The parent line map, or null if this map isn't for a subgrid.
1994 const LineNameMap* mParentLineNameMap;
1995 // The subgrid's range, or null if this map isn't for a subgrid.
1996 const LineRange* mRange;
1997 // True if the subgrid/parent axes progresses in the same direction.
1998 const bool mIsSameDirection;
2000 // True if there is a specified repeat(auto-fill/fit) track.
2001 bool mHasRepeatAuto;
2005 * State for the tracks in one dimension.
2007 struct nsGridContainerFrame::Tracks {
2008 explicit Tracks(LogicalAxis aAxis)
2009 : mContentBoxSize(NS_UNCONSTRAINEDSIZE),
2010 mGridGap(NS_UNCONSTRAINEDSIZE),
2011 mStateUnion(TrackSize::StateBits(0)),
2012 mAxis(aAxis),
2013 mCanResolveLineRangeSize(false),
2014 mIsMasonry(false) {
2015 mBaselineSubtreeAlign[BaselineSharingGroup::First] = StyleAlignFlags::AUTO;
2016 mBaselineSubtreeAlign[BaselineSharingGroup::Last] = StyleAlignFlags::AUTO;
2017 mBaseline[BaselineSharingGroup::First] = NS_INTRINSIC_ISIZE_UNKNOWN;
2018 mBaseline[BaselineSharingGroup::Last] = NS_INTRINSIC_ISIZE_UNKNOWN;
2021 void Initialize(const TrackSizingFunctions& aFunctions,
2022 const NonNegativeLengthPercentageOrNormal& aGridGap,
2023 uint32_t aNumTracks, nscoord aContentBoxSize);
2026 * Return the union of the state bits for the tracks in aRange.
2028 TrackSize::StateBits StateBitsForRange(const LineRange& aRange) const;
2030 // Some data we collect for aligning baseline-aligned items.
2031 struct ItemBaselineData {
2032 uint32_t mBaselineTrack;
2033 nscoord mBaseline;
2034 nscoord mSize;
2035 GridItemInfo* mGridItem;
2036 static bool IsBaselineTrackLessThan(const ItemBaselineData& a,
2037 const ItemBaselineData& b) {
2038 return a.mBaselineTrack < b.mBaselineTrack;
2043 * Calculate baseline offsets for the given set of items.
2044 * Helper for InitialzeItemBaselines.
2046 void CalculateItemBaselines(nsTArray<ItemBaselineData>& aBaselineItems,
2047 BaselineSharingGroup aBaselineGroup);
2050 * Initialize grid item baseline state and offsets.
2052 void InitializeItemBaselines(GridReflowInput& aState,
2053 nsTArray<GridItemInfo>& aGridItems);
2056 * A masonry axis has four baseline alignment sets and each set can have
2057 * a first- and last-baseline alignment group, for a total of eight possible
2058 * baseline alignment groups, as follows:
2059 * set 1: the first item in each `start` or `stretch` grid track
2060 * set 2: the last item in each `start` grid track
2061 * set 3: the last item in each `end` or `stretch` grid track
2062 * set 4: the first item in each `end` grid track
2063 * (`start`/`end`/`stretch` refers to the relevant `align/justify-tracks`
2064 * value of the (grid-axis) start track for the item) Baseline-alignment for
2065 * set 1 and 2 always adjusts the item's padding or margin on the start side,
2066 * and set 3 and 4 on the end side, for both first- and last-baseline groups
2067 * in the set. (This is similar to regular grid which always adjusts
2068 * first-baseline groups on the start side and last-baseline groups on the
2069 * end-side. The crux is that those groups are always aligned to the track's
2070 * start/end side respectively.)
2072 struct BaselineAlignmentSet {
2073 bool MatchTrackAlignment(StyleAlignFlags aTrackAlignment) const {
2074 if (mTrackAlignmentSet == BaselineAlignmentSet::StartStretch) {
2075 return aTrackAlignment == StyleAlignFlags::START ||
2076 (aTrackAlignment == StyleAlignFlags::STRETCH &&
2077 mItemSet == BaselineAlignmentSet::FirstItems);
2079 return aTrackAlignment == StyleAlignFlags::END ||
2080 (aTrackAlignment == StyleAlignFlags::STRETCH &&
2081 mItemSet == BaselineAlignmentSet::LastItems);
2084 enum ItemSet { FirstItems, LastItems };
2085 ItemSet mItemSet = FirstItems;
2086 enum TrackAlignmentSet { StartStretch, EndStretch };
2087 TrackAlignmentSet mTrackAlignmentSet = StartStretch;
2089 void InitializeItemBaselinesInMasonryAxis(
2090 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
2091 BaselineAlignmentSet aSet, const nsSize& aContainerSize,
2092 nsTArray<nscoord>& aTrackSizes,
2093 nsTArray<ItemBaselineData>& aFirstBaselineItems,
2094 nsTArray<ItemBaselineData>& aLastBaselineItems);
2097 * Apply the additional alignment needed to align the baseline-aligned subtree
2098 * the item belongs to within its baseline track.
2100 void AlignBaselineSubtree(const GridItemInfo& aGridItem) const;
2102 enum class TrackSizingPhase {
2103 IntrinsicMinimums,
2104 ContentBasedMinimums,
2105 MaxContentMinimums,
2106 IntrinsicMaximums,
2107 MaxContentMaximums,
2110 // Some data we collect on each item for Step 2 of the Track Sizing Algorithm
2111 // in ResolveIntrinsicSize below.
2112 struct Step2ItemData final {
2113 uint32_t mSpan;
2114 TrackSize::StateBits mState;
2115 LineRange mLineRange;
2116 nscoord mMinSize;
2117 nscoord mMinContentContribution;
2118 nscoord mMaxContentContribution;
2119 nsIFrame* mFrame;
2120 static bool IsSpanLessThan(const Step2ItemData& a, const Step2ItemData& b) {
2121 return a.mSpan < b.mSpan;
2124 template <TrackSizingPhase phase>
2125 nscoord SizeContributionForPhase() const {
2126 switch (phase) {
2127 case TrackSizingPhase::IntrinsicMinimums:
2128 return mMinSize;
2129 case TrackSizingPhase::ContentBasedMinimums:
2130 case TrackSizingPhase::IntrinsicMaximums:
2131 return mMinContentContribution;
2132 case TrackSizingPhase::MaxContentMinimums:
2133 case TrackSizingPhase::MaxContentMaximums:
2134 return mMaxContentContribution;
2136 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
2140 using FitContentClamper =
2141 std::function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
2143 // Helper method for ResolveIntrinsicSize.
2144 template <TrackSizingPhase phase>
2145 bool GrowSizeForSpanningItems(nsTArray<Step2ItemData>::iterator aIter,
2146 const nsTArray<Step2ItemData>::iterator aEnd,
2147 nsTArray<uint32_t>& aTracks,
2148 nsTArray<TrackSize>& aPlan,
2149 nsTArray<TrackSize>& aItemPlan,
2150 TrackSize::StateBits aSelector,
2151 const FitContentClamper& aClamper = nullptr,
2152 bool aNeedInfinitelyGrowableFlag = false);
2154 * Resolve Intrinsic Track Sizes.
2155 * http://dev.w3.org/csswg/css-grid/#algo-content
2157 void ResolveIntrinsicSize(GridReflowInput& aState,
2158 nsTArray<GridItemInfo>& aGridItems,
2159 const TrackSizingFunctions& aFunctions,
2160 LineRange GridArea::*aRange,
2161 nscoord aPercentageBasis,
2162 SizingConstraint aConstraint);
2165 * Helper for ResolveIntrinsicSize. It implements step 1 "size tracks to fit
2166 * non-spanning items" in the spec. Return true if the track has a <flex>
2167 * max-sizing function, false otherwise.
2169 bool ResolveIntrinsicSizeStep1(GridReflowInput& aState,
2170 const TrackSizingFunctions& aFunctions,
2171 nscoord aPercentageBasis,
2172 SizingConstraint aConstraint,
2173 const LineRange& aRange,
2174 const GridItemInfo& aGridItem);
2176 // Helper method that returns the track size to use in §11.5.1.2
2177 // https://drafts.csswg.org/css-grid/#extra-space
2178 template <TrackSizingPhase phase>
2179 static nscoord StartSizeInDistribution(const TrackSize& aSize) {
2180 switch (phase) {
2181 case TrackSizingPhase::IntrinsicMinimums:
2182 case TrackSizingPhase::ContentBasedMinimums:
2183 case TrackSizingPhase::MaxContentMinimums:
2184 return aSize.mBase;
2185 case TrackSizingPhase::IntrinsicMaximums:
2186 case TrackSizingPhase::MaxContentMaximums:
2187 if (aSize.mLimit == NS_UNCONSTRAINEDSIZE) {
2188 return aSize.mBase;
2190 return aSize.mLimit;
2192 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
2196 * Collect the tracks which are growable (matching aSelector) into
2197 * aGrowableTracks, and return the amount of space that can be used
2198 * to grow those tracks. This method implements CSS Grid §11.5.1.2.
2199 * https://drafts.csswg.org/css-grid/#extra-space
2201 template <TrackSizingPhase phase>
2202 nscoord CollectGrowable(nscoord aAvailableSpace, const LineRange& aRange,
2203 TrackSize::StateBits aSelector,
2204 nsTArray<uint32_t>& aGrowableTracks) const {
2205 MOZ_ASSERT(aAvailableSpace > 0, "why call me?");
2206 nscoord space = aAvailableSpace - mGridGap * (aRange.Extent() - 1);
2207 for (auto i : aRange.Range()) {
2208 const TrackSize& sz = mSizes[i];
2209 space -= StartSizeInDistribution<phase>(sz);
2210 if (space <= 0) {
2211 return 0;
2213 if (sz.mState & aSelector) {
2214 aGrowableTracks.AppendElement(i);
2217 return aGrowableTracks.IsEmpty() ? 0 : space;
2220 template <TrackSizingPhase phase>
2221 void InitializeItemPlan(nsTArray<TrackSize>& aItemPlan,
2222 const nsTArray<uint32_t>& aTracks) const {
2223 for (uint32_t track : aTracks) {
2224 auto& plan = aItemPlan[track];
2225 const TrackSize& sz = mSizes[track];
2226 plan.mBase = StartSizeInDistribution<phase>(sz);
2227 bool unlimited = sz.mState & TrackSize::eInfinitelyGrowable;
2228 plan.mLimit = unlimited ? NS_UNCONSTRAINEDSIZE : sz.mLimit;
2229 plan.mState = sz.mState;
2233 template <TrackSizingPhase phase>
2234 void InitializePlan(nsTArray<TrackSize>& aPlan) const {
2235 for (size_t i = 0, len = aPlan.Length(); i < len; ++i) {
2236 auto& plan = aPlan[i];
2237 const auto& sz = mSizes[i];
2238 plan.mBase = StartSizeInDistribution<phase>(sz);
2239 MOZ_ASSERT(phase == TrackSizingPhase::MaxContentMaximums ||
2240 !(sz.mState & TrackSize::eInfinitelyGrowable),
2241 "forgot to reset the eInfinitelyGrowable bit?");
2242 plan.mState = sz.mState;
2246 template <TrackSizingPhase phase>
2247 void CopyPlanToSize(const nsTArray<TrackSize>& aPlan,
2248 bool aNeedInfinitelyGrowableFlag = false) {
2249 for (size_t i = 0, len = mSizes.Length(); i < len; ++i) {
2250 const auto& plan = aPlan[i];
2251 MOZ_ASSERT(plan.mBase >= 0);
2252 auto& sz = mSizes[i];
2253 switch (phase) {
2254 case TrackSizingPhase::IntrinsicMinimums:
2255 case TrackSizingPhase::ContentBasedMinimums:
2256 case TrackSizingPhase::MaxContentMinimums:
2257 sz.mBase = plan.mBase;
2258 break;
2259 case TrackSizingPhase::IntrinsicMaximums:
2260 if (plan.mState & TrackSize::eModified) {
2261 if (sz.mLimit == NS_UNCONSTRAINEDSIZE &&
2262 aNeedInfinitelyGrowableFlag) {
2263 sz.mState |= TrackSize::eInfinitelyGrowable;
2265 sz.mLimit = plan.mBase;
2267 break;
2268 case TrackSizingPhase::MaxContentMaximums:
2269 if (plan.mState & TrackSize::eModified) {
2270 sz.mLimit = plan.mBase;
2272 sz.mState &= ~TrackSize::eInfinitelyGrowable;
2273 break;
2279 * Grow the planned size for tracks in aGrowableTracks up to their limit
2280 * and then freeze them (all aGrowableTracks must be unfrozen on entry).
2281 * Subtract the space added from aAvailableSpace and return that.
2283 nscoord GrowTracksToLimit(nscoord aAvailableSpace, nsTArray<TrackSize>& aPlan,
2284 const nsTArray<uint32_t>& aGrowableTracks,
2285 const FitContentClamper& aFitContentClamper) const {
2286 MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0);
2287 nscoord space = aAvailableSpace;
2288 uint32_t numGrowable = aGrowableTracks.Length();
2289 while (true) {
2290 nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
2291 for (uint32_t track : aGrowableTracks) {
2292 TrackSize& sz = aPlan[track];
2293 if (sz.IsFrozen()) {
2294 continue;
2296 nscoord newBase = sz.mBase + spacePerTrack;
2297 nscoord limit = sz.mLimit;
2298 if (MOZ_UNLIKELY((sz.mState & TrackSize::eFitContent) &&
2299 aFitContentClamper)) {
2300 // Clamp the limit to the fit-content() size, for §12.5.2 step 5/6.
2301 aFitContentClamper(track, sz.mBase, &limit);
2303 if (newBase > limit) {
2304 nscoord consumed = limit - sz.mBase;
2305 if (consumed > 0) {
2306 space -= consumed;
2307 sz.mBase = limit;
2309 sz.mState |= TrackSize::eFrozen;
2310 if (--numGrowable == 0) {
2311 return space;
2313 } else {
2314 sz.mBase = newBase;
2315 space -= spacePerTrack;
2317 MOZ_ASSERT(space >= 0);
2318 if (space == 0) {
2319 return 0;
2323 MOZ_ASSERT_UNREACHABLE("we don't exit the loop above except by return");
2324 return 0;
2328 * Helper for GrowSelectedTracksUnlimited. For the set of tracks (S) that
2329 * match aMinSizingSelector: if a track in S doesn't match aMaxSizingSelector
2330 * then mark it with aSkipFlag. If all tracks in S were marked then unmark
2331 * them. Return aNumGrowable minus the number of tracks marked. It is
2332 * assumed that aPlan have no aSkipFlag set for tracks in aGrowableTracks
2333 * on entry to this method.
2335 static uint32_t MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
2336 uint32_t aNumGrowable,
2337 const nsTArray<uint32_t>& aGrowableTracks,
2338 TrackSize::StateBits aMinSizingSelector,
2339 TrackSize::StateBits aMaxSizingSelector,
2340 TrackSize::StateBits aSkipFlag) {
2341 bool foundOneSelected = false;
2342 bool foundOneGrowable = false;
2343 uint32_t numGrowable = aNumGrowable;
2344 for (uint32_t track : aGrowableTracks) {
2345 TrackSize& sz = aPlan[track];
2346 const auto state = sz.mState;
2347 if (state & aMinSizingSelector) {
2348 foundOneSelected = true;
2349 if (state & aMaxSizingSelector) {
2350 foundOneGrowable = true;
2351 continue;
2353 sz.mState |= aSkipFlag;
2354 MOZ_ASSERT(numGrowable != 0);
2355 --numGrowable;
2358 // 12.5 "if there are no such tracks, then all affected tracks"
2359 if (foundOneSelected && !foundOneGrowable) {
2360 for (uint32_t track : aGrowableTracks) {
2361 aPlan[track].mState &= ~aSkipFlag;
2363 numGrowable = aNumGrowable;
2365 return numGrowable;
2369 * Mark all tracks in aGrowableTracks with an eSkipGrowUnlimited bit if
2370 * they *shouldn't* grow unlimited in §11.5.1.2.3 "Distribute space beyond
2371 * growth limits" https://drafts.csswg.org/css-grid/#extra-space
2372 * Return the number of tracks that are still growable.
2374 template <TrackSizingPhase phase>
2375 static uint32_t MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
2376 const nsTArray<uint32_t>& aGrowableTracks,
2377 TrackSize::StateBits aSelector) {
2378 uint32_t numGrowable = aGrowableTracks.Length();
2379 if (phase == TrackSizingPhase::IntrinsicMaximums ||
2380 phase == TrackSizingPhase::MaxContentMaximums) {
2381 // "when handling any intrinsic growth limit: all affected tracks"
2382 return numGrowable;
2384 MOZ_ASSERT(aSelector == (aSelector & TrackSize::eIntrinsicMinSizing) &&
2385 (aSelector & TrackSize::eMaxContentMinSizing),
2386 "Should only get here for track sizing steps 2.1 to 2.3");
2387 // Note that eMaxContentMinSizing is always included. We do those first:
2388 numGrowable = MarkExcludedTracks(
2389 aPlan, numGrowable, aGrowableTracks, TrackSize::eMaxContentMinSizing,
2390 TrackSize::eMaxContentMaxSizing, TrackSize::eSkipGrowUnlimited1);
2391 // Now mark min-content/auto min-sizing tracks if requested.
2392 auto minOrAutoSelector = aSelector & ~TrackSize::eMaxContentMinSizing;
2393 if (minOrAutoSelector) {
2394 numGrowable = MarkExcludedTracks(
2395 aPlan, numGrowable, aGrowableTracks, minOrAutoSelector,
2396 TrackSize::eIntrinsicMaxSizing, TrackSize::eSkipGrowUnlimited2);
2398 return numGrowable;
2402 * Increase the planned size for tracks in aGrowableTracks that aren't
2403 * marked with a eSkipGrowUnlimited flag beyond their limit.
2404 * This implements the "Distribute space beyond growth limits" step in
2405 * https://drafts.csswg.org/css-grid/#distribute-extra-space
2407 void GrowSelectedTracksUnlimited(
2408 nscoord aAvailableSpace, nsTArray<TrackSize>& aPlan,
2409 const nsTArray<uint32_t>& aGrowableTracks, uint32_t aNumGrowable,
2410 const FitContentClamper& aFitContentClamper) const {
2411 MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0 &&
2412 aNumGrowable <= aGrowableTracks.Length());
2413 nscoord space = aAvailableSpace;
2414 DebugOnly<bool> didClamp = false;
2415 while (aNumGrowable) {
2416 nscoord spacePerTrack = std::max<nscoord>(space / aNumGrowable, 1);
2417 for (uint32_t track : aGrowableTracks) {
2418 TrackSize& sz = aPlan[track];
2419 if (sz.mState & TrackSize::eSkipGrowUnlimited) {
2420 continue; // an excluded track
2422 nscoord delta = spacePerTrack;
2423 nscoord newBase = sz.mBase + delta;
2424 if (MOZ_UNLIKELY((sz.mState & TrackSize::eFitContent) &&
2425 aFitContentClamper)) {
2426 // Clamp newBase to the fit-content() size, for §12.5.2 step 5/6.
2427 if (aFitContentClamper(track, sz.mBase, &newBase)) {
2428 didClamp = true;
2429 delta = newBase - sz.mBase;
2430 MOZ_ASSERT(delta >= 0, "track size shouldn't shrink");
2431 sz.mState |= TrackSize::eSkipGrowUnlimited1;
2432 --aNumGrowable;
2435 sz.mBase = newBase;
2436 space -= delta;
2437 MOZ_ASSERT(space >= 0);
2438 if (space == 0) {
2439 return;
2443 MOZ_ASSERT(didClamp,
2444 "we don't exit the loop above except by return, "
2445 "unless we clamped some track's size");
2449 * Distribute aAvailableSpace to the planned base size for aGrowableTracks
2450 * up to their limits, then distribute the remaining space beyond the limits.
2452 template <TrackSizingPhase phase>
2453 void DistributeToTrackSizes(nscoord aAvailableSpace,
2454 nsTArray<TrackSize>& aPlan,
2455 nsTArray<TrackSize>& aItemPlan,
2456 nsTArray<uint32_t>& aGrowableTracks,
2457 TrackSize::StateBits aSelector,
2458 const FitContentClamper& aFitContentClamper) {
2459 InitializeItemPlan<phase>(aItemPlan, aGrowableTracks);
2460 nscoord space = GrowTracksToLimit(aAvailableSpace, aItemPlan,
2461 aGrowableTracks, aFitContentClamper);
2462 if (space > 0) {
2463 uint32_t numGrowable =
2464 MarkExcludedTracks<phase>(aItemPlan, aGrowableTracks, aSelector);
2465 GrowSelectedTracksUnlimited(space, aItemPlan, aGrowableTracks,
2466 numGrowable, aFitContentClamper);
2468 for (uint32_t track : aGrowableTracks) {
2469 nscoord& plannedSize = aPlan[track].mBase;
2470 nscoord itemIncurredSize = aItemPlan[track].mBase;
2471 if (plannedSize < itemIncurredSize) {
2472 plannedSize = itemIncurredSize;
2478 * Distribute aAvailableSize to the tracks. This implements 12.6 at:
2479 * http://dev.w3.org/csswg/css-grid/#algo-grow-tracks
2481 void DistributeFreeSpace(nscoord aAvailableSize) {
2482 const uint32_t numTracks = mSizes.Length();
2483 if (MOZ_UNLIKELY(numTracks == 0 || aAvailableSize <= 0)) {
2484 return;
2486 if (aAvailableSize == NS_UNCONSTRAINEDSIZE) {
2487 for (TrackSize& sz : mSizes) {
2488 sz.mBase = sz.mLimit;
2490 } else {
2491 // Compute free space and count growable tracks.
2492 nscoord space = aAvailableSize;
2493 uint32_t numGrowable = numTracks;
2494 for (const TrackSize& sz : mSizes) {
2495 space -= sz.mBase;
2496 MOZ_ASSERT(sz.mBase <= sz.mLimit);
2497 if (sz.mBase == sz.mLimit) {
2498 --numGrowable;
2501 // Distribute the free space evenly to the growable tracks. If not exactly
2502 // divisable the remainder is added to the leading tracks.
2503 while (space > 0 && numGrowable) {
2504 nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
2505 for (uint32_t i = 0; i < numTracks && space > 0; ++i) {
2506 TrackSize& sz = mSizes[i];
2507 if (sz.mBase == sz.mLimit) {
2508 continue;
2510 nscoord newBase = sz.mBase + spacePerTrack;
2511 if (newBase >= sz.mLimit) {
2512 space -= sz.mLimit - sz.mBase;
2513 sz.mBase = sz.mLimit;
2514 --numGrowable;
2515 } else {
2516 space -= spacePerTrack;
2517 sz.mBase = newBase;
2525 * Implements "12.7.1. Find the Size of an 'fr'".
2526 * http://dev.w3.org/csswg/css-grid/#algo-find-fr-size
2527 * (The returned value is a 'nscoord' divided by a factor - a floating type
2528 * is used to avoid intermediary rounding errors.)
2530 float FindFrUnitSize(const LineRange& aRange,
2531 const nsTArray<uint32_t>& aFlexTracks,
2532 const TrackSizingFunctions& aFunctions,
2533 nscoord aSpaceToFill) const;
2536 * Implements the "find the used flex fraction" part of StretchFlexibleTracks.
2537 * (The returned value is a 'nscoord' divided by a factor - a floating type
2538 * is used to avoid intermediary rounding errors.)
2540 float FindUsedFlexFraction(GridReflowInput& aState,
2541 nsTArray<GridItemInfo>& aGridItems,
2542 const nsTArray<uint32_t>& aFlexTracks,
2543 const TrackSizingFunctions& aFunctions,
2544 nscoord aAvailableSize) const;
2547 * Implements "12.7. Stretch Flexible Tracks"
2548 * http://dev.w3.org/csswg/css-grid/#algo-flex-tracks
2550 void StretchFlexibleTracks(GridReflowInput& aState,
2551 nsTArray<GridItemInfo>& aGridItems,
2552 const TrackSizingFunctions& aFunctions,
2553 nscoord aAvailableSize);
2556 * Implements "12.3. Track Sizing Algorithm"
2557 * http://dev.w3.org/csswg/css-grid/#algo-track-sizing
2559 void CalculateSizes(GridReflowInput& aState,
2560 nsTArray<GridItemInfo>& aGridItems,
2561 const TrackSizingFunctions& aFunctions,
2562 nscoord aContentBoxSize, LineRange GridArea::*aRange,
2563 SizingConstraint aConstraint);
2566 * Apply 'align/justify-content', whichever is relevant for this axis.
2567 * https://drafts.csswg.org/css-align-3/#propdef-align-content
2569 void AlignJustifyContent(const nsStylePosition* aStyle,
2570 StyleContentDistribution aAligmentStyleValue,
2571 WritingMode aWM, nscoord aContentBoxSize,
2572 bool aIsSubgridded);
2574 nscoord GridLineEdge(uint32_t aLine, GridLineSide aSide) const {
2575 if (MOZ_UNLIKELY(mSizes.IsEmpty())) {
2576 // https://drafts.csswg.org/css-grid/#grid-definition
2577 // "... the explicit grid still contains one grid line in each axis."
2578 MOZ_ASSERT(aLine == 0, "We should only resolve line 1 in an empty grid");
2579 return nscoord(0);
2581 MOZ_ASSERT(aLine <= mSizes.Length(), "mSizes is too small");
2582 if (aSide == GridLineSide::BeforeGridGap) {
2583 if (aLine == 0) {
2584 return nscoord(0);
2586 const TrackSize& sz = mSizes[aLine - 1];
2587 return sz.mPosition + sz.mBase;
2589 if (aLine == mSizes.Length()) {
2590 return mContentBoxSize;
2592 return mSizes[aLine].mPosition;
2595 nscoord SumOfGridTracksAndGaps() {
2596 return SumOfGridTracks() + SumOfGridGaps();
2599 nscoord SumOfGridTracks() const {
2600 nscoord result = 0;
2601 for (const TrackSize& size : mSizes) {
2602 result += size.mBase;
2604 return result;
2607 nscoord SumOfGridGaps() const {
2608 auto len = mSizes.Length();
2609 return MOZ_LIKELY(len > 1) ? (len - 1) * mGridGap : 0;
2613 * Break before aRow, i.e. set the eBreakBefore flag on aRow and set the grid
2614 * gap before aRow to zero (and shift all rows after it by the removed gap).
2616 void BreakBeforeRow(uint32_t aRow) {
2617 MOZ_ASSERT(mAxis == eLogicalAxisBlock,
2618 "Should only be fragmenting in the block axis (between rows)");
2619 nscoord prevRowEndPos = 0;
2620 if (aRow != 0) {
2621 auto& prevSz = mSizes[aRow - 1];
2622 prevRowEndPos = prevSz.mPosition + prevSz.mBase;
2624 auto& sz = mSizes[aRow];
2625 const nscoord gap = sz.mPosition - prevRowEndPos;
2626 sz.mState |= TrackSize::eBreakBefore;
2627 if (gap != 0) {
2628 for (uint32_t i = aRow, len = mSizes.Length(); i < len; ++i) {
2629 mSizes[i].mPosition -= gap;
2635 * Set the size of aRow to aSize and adjust the position of all rows after it.
2637 void ResizeRow(uint32_t aRow, nscoord aNewSize) {
2638 MOZ_ASSERT(mAxis == eLogicalAxisBlock,
2639 "Should only be fragmenting in the block axis (between rows)");
2640 MOZ_ASSERT(aNewSize >= 0);
2641 auto& sz = mSizes[aRow];
2642 nscoord delta = aNewSize - sz.mBase;
2643 NS_WARNING_ASSERTION(delta != nscoord(0), "Useless call to ResizeRow");
2644 sz.mBase = aNewSize;
2645 const uint32_t numRows = mSizes.Length();
2646 for (uint32_t r = aRow + 1; r < numRows; ++r) {
2647 mSizes[r].mPosition += delta;
2651 nscoord ResolveSize(const LineRange& aRange) const {
2652 MOZ_ASSERT(mCanResolveLineRangeSize);
2653 MOZ_ASSERT(aRange.Extent() > 0, "grid items cover at least one track");
2654 nscoord pos, size;
2655 aRange.ToPositionAndLength(mSizes, &pos, &size);
2656 return size;
2659 #ifdef DEBUG
2660 void Dump() const;
2661 #endif
2663 CopyableAutoTArray<TrackSize, 32> mSizes;
2664 nscoord mContentBoxSize;
2665 nscoord mGridGap;
2666 // The first(last)-baseline for the first(last) track in this axis.
2667 PerBaseline<nscoord> mBaseline;
2668 // The union of the track min/max-sizing state bits in this axis.
2669 TrackSize::StateBits mStateUnion;
2670 LogicalAxis mAxis;
2671 // Used for aligning a baseline-aligned subtree of items. The only possible
2672 // values are StyleAlignFlags::{START,END,CENTER,AUTO}. AUTO means there are
2673 // no baseline-aligned items in any track in that axis.
2674 // There is one alignment value for each BaselineSharingGroup.
2675 PerBaseline<StyleAlignFlags> mBaselineSubtreeAlign;
2676 // True if track positions and sizes are final in this axis.
2677 bool mCanResolveLineRangeSize;
2678 // True if this axis has masonry layout.
2679 bool mIsMasonry;
2682 #ifdef DEBUG
2683 void nsGridContainerFrame::Tracks::Dump() const {
2684 printf("%zu %s %s ", mSizes.Length(), mIsMasonry ? "masonry" : "grid",
2685 mAxis == eLogicalAxisBlock ? "rows" : "columns");
2686 TrackSize::DumpStateBits(mStateUnion);
2687 printf("\n");
2688 for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
2689 printf(" %d: ", i);
2690 mSizes[i].Dump();
2691 printf("\n");
2693 double px = AppUnitsPerCSSPixel();
2694 printf("Baselines: %.2fpx %2fpx\n",
2695 mBaseline[BaselineSharingGroup::First] / px,
2696 mBaseline[BaselineSharingGroup::Last] / px);
2697 printf("Gap: %.2fpx\n", mGridGap / px);
2698 printf("ContentBoxSize: %.2fpx\n", mContentBoxSize / px);
2700 #endif
2703 * Grid data shared by all continuations, owned by the first-in-flow.
2704 * The data is initialized from the first-in-flow's GridReflowInput at
2705 * the end of its reflow. Fragmentation will modify mRows.mSizes -
2706 * the mPosition to remove the row gap at the break boundary, the mState
2707 * by setting the eBreakBefore flag, and mBase is modified when we decide
2708 * to grow a row. mOriginalRowData is setup by the first-in-flow and
2709 * not modified after that. It's used for undoing the changes to mRows.
2710 * mCols, mGridItems, mAbsPosItems are used for initializing the grid
2711 * reflow input for continuations, see GridReflowInput::Initialize below.
2713 struct nsGridContainerFrame::SharedGridData {
2714 SharedGridData()
2715 : mCols(eLogicalAxisInline),
2716 mRows(eLogicalAxisBlock),
2717 mGenerateComputedGridInfo(false) {}
2718 Tracks mCols;
2719 Tracks mRows;
2720 struct RowData {
2721 nscoord mBase; // the original track size
2722 nscoord mGap; // the original gap before a track
2724 nsTArray<RowData> mOriginalRowData;
2725 nsTArray<GridItemInfo> mGridItems;
2726 nsTArray<GridItemInfo> mAbsPosItems;
2727 bool mGenerateComputedGridInfo;
2730 * Only set on the first-in-flow. Continuations will Initialize() their
2731 * GridReflowInput from it.
2733 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, SharedGridData)
2736 struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput {
2737 GridReflowInput(nsGridContainerFrame* aFrame, const ReflowInput& aRI)
2738 : GridReflowInput(aFrame, *aRI.mRenderingContext, &aRI,
2739 aRI.mStylePosition, aRI.GetWritingMode()) {}
2740 GridReflowInput(nsGridContainerFrame* aFrame, gfxContext& aRC)
2741 : GridReflowInput(aFrame, aRC, nullptr, aFrame->StylePosition(),
2742 aFrame->GetWritingMode()) {}
2745 * Initialize our track sizes and grid item info using the shared
2746 * state from aGridContainerFrame first-in-flow.
2748 void InitializeForContinuation(nsGridContainerFrame* aGridContainerFrame,
2749 nscoord aConsumedBSize) {
2750 MOZ_ASSERT(aGridContainerFrame->GetPrevInFlow(),
2751 "don't call this on the first-in-flow");
2752 MOZ_ASSERT(mGridItems.IsEmpty() && mAbsPosItems.IsEmpty(),
2753 "shouldn't have any item data yet");
2755 // Get the SharedGridData from the first-in-flow. Also calculate the number
2756 // of fragments before this so that we can figure out our start row below.
2757 uint32_t fragment = 0;
2758 nsIFrame* firstInFlow = aGridContainerFrame;
2759 for (auto pif = aGridContainerFrame->GetPrevInFlow(); pif;
2760 pif = pif->GetPrevInFlow()) {
2761 ++fragment;
2762 firstInFlow = pif;
2764 mSharedGridData = firstInFlow->GetProperty(SharedGridData::Prop());
2765 MOZ_ASSERT(mSharedGridData, "first-in-flow must have SharedGridData");
2767 // Find the start row for this fragment and undo breaks after that row
2768 // since the breaks might be different from the last reflow.
2769 auto& rowSizes = mSharedGridData->mRows.mSizes;
2770 const uint32_t numRows = rowSizes.Length();
2771 mStartRow = numRows;
2772 for (uint32_t row = 0, breakCount = 0; row < numRows; ++row) {
2773 if (rowSizes[row].mState & TrackSize::eBreakBefore) {
2774 if (fragment == ++breakCount) {
2775 mStartRow = row;
2776 mFragBStart = rowSizes[row].mPosition;
2777 // Restore the original size for |row| and grid gaps / state after it.
2778 const auto& origRowData = mSharedGridData->mOriginalRowData;
2779 rowSizes[row].mBase = origRowData[row].mBase;
2780 nscoord prevEndPos = rowSizes[row].mPosition + rowSizes[row].mBase;
2781 while (++row < numRows) {
2782 auto& sz = rowSizes[row];
2783 const auto& orig = origRowData[row];
2784 sz.mPosition = prevEndPos + orig.mGap;
2785 sz.mBase = orig.mBase;
2786 sz.mState &= ~TrackSize::eBreakBefore;
2787 prevEndPos = sz.mPosition + sz.mBase;
2789 break;
2793 if (mStartRow == numRows ||
2794 aGridContainerFrame->IsMasonry(eLogicalAxisBlock)) {
2795 // All of the grid's rows fit inside of previous grid-container fragments,
2796 // or it's a masonry axis.
2797 mFragBStart = aConsumedBSize;
2800 // Copy the shared track state.
2801 // XXX consider temporarily swapping the array elements instead and swapping
2802 // XXX them back after we're done reflowing, for better performance.
2803 // XXX (bug 1252002)
2804 mCols = mSharedGridData->mCols;
2805 mRows = mSharedGridData->mRows;
2807 if (firstInFlow->GetProperty(UsedTrackSizes::Prop())) {
2808 auto* prop = aGridContainerFrame->GetProperty(UsedTrackSizes::Prop());
2809 if (!prop) {
2810 prop = new UsedTrackSizes();
2811 aGridContainerFrame->SetProperty(UsedTrackSizes::Prop(), prop);
2813 prop->mCanResolveLineRangeSize = {true, true};
2814 prop->mSizes[eLogicalAxisInline].Assign(mCols.mSizes);
2815 prop->mSizes[eLogicalAxisBlock].Assign(mRows.mSizes);
2818 // Copy item data from each child's first-in-flow data in mSharedGridData.
2819 // XXX NOTE: This is O(n^2) in the number of items. (bug 1252186)
2820 mIter.Reset();
2821 for (; !mIter.AtEnd(); mIter.Next()) {
2822 nsIFrame* child = *mIter;
2823 nsIFrame* childFirstInFlow = child->FirstInFlow();
2824 DebugOnly<size_t> len = mGridItems.Length();
2825 for (auto& itemInfo : mSharedGridData->mGridItems) {
2826 if (itemInfo.mFrame == childFirstInFlow) {
2827 auto item =
2828 mGridItems.AppendElement(GridItemInfo(child, itemInfo.mArea));
2829 // Copy the item's baseline data so that the item's last fragment can
2830 // do 'last baseline' alignment if necessary.
2831 item->mState[0] |= itemInfo.mState[0] & ItemState::eAllBaselineBits;
2832 item->mState[1] |= itemInfo.mState[1] & ItemState::eAllBaselineBits;
2833 item->mBaselineOffset[0] = itemInfo.mBaselineOffset[0];
2834 item->mBaselineOffset[1] = itemInfo.mBaselineOffset[1];
2835 item->mState[0] |= itemInfo.mState[0] & ItemState::eAutoPlacement;
2836 item->mState[1] |= itemInfo.mState[1] & ItemState::eAutoPlacement;
2837 break;
2840 MOZ_ASSERT(mGridItems.Length() == len + 1, "can't find GridItemInfo");
2843 // XXX NOTE: This is O(n^2) in the number of abs.pos. items. (bug 1252186)
2844 nsFrameList absPosChildren(aGridContainerFrame->GetChildList(
2845 aGridContainerFrame->GetAbsoluteListID()));
2846 for (auto f : absPosChildren) {
2847 nsIFrame* childFirstInFlow = f->FirstInFlow();
2848 DebugOnly<size_t> len = mAbsPosItems.Length();
2849 for (auto& itemInfo : mSharedGridData->mAbsPosItems) {
2850 if (itemInfo.mFrame == childFirstInFlow) {
2851 mAbsPosItems.AppendElement(GridItemInfo(f, itemInfo.mArea));
2852 break;
2855 MOZ_ASSERT(mAbsPosItems.Length() == len + 1, "can't find GridItemInfo");
2858 // Copy in the computed grid info state bit
2859 if (mSharedGridData->mGenerateComputedGridInfo) {
2860 aGridContainerFrame->SetShouldGenerateComputedInfo(true);
2865 * Calculate our track sizes in the given axis.
2867 void CalculateTrackSizesForAxis(LogicalAxis aAxis, const Grid& aGrid,
2868 nscoord aCBSize,
2869 SizingConstraint aConstraint);
2872 * Calculate our track sizes.
2874 void CalculateTrackSizes(const Grid& aGrid, const LogicalSize& aContentBox,
2875 SizingConstraint aConstraint);
2878 * Return the percentage basis for a grid item in its writing-mode.
2879 * If aAxis is eLogicalAxisInline then we return NS_UNCONSTRAINEDSIZE in
2880 * both axes since we know all track sizes are indefinite at this point
2881 * (we calculate column sizes before row sizes). Otherwise, assert that
2882 * column sizes are known and calculate the size for aGridItem.mArea.mCols
2883 * and use NS_UNCONSTRAINEDSIZE in the other axis.
2884 * @param aAxis the axis we're currently calculating track sizes for
2886 LogicalSize PercentageBasisFor(LogicalAxis aAxis,
2887 const GridItemInfo& aGridItem) const;
2890 * Return the containing block for a grid item occupying aArea.
2892 LogicalRect ContainingBlockFor(const GridArea& aArea) const;
2895 * Return the containing block for an abs.pos. grid item occupying aArea.
2896 * Any 'auto' lines in the grid area will be aligned with grid container
2897 * containing block on that side.
2898 * @param aGridOrigin the origin of the grid
2899 * @param aGridCB the grid container containing block (its padding area)
2901 LogicalRect ContainingBlockForAbsPos(const GridArea& aArea,
2902 const LogicalPoint& aGridOrigin,
2903 const LogicalRect& aGridCB) const;
2906 * Apply `align/justify-content` alignment in our masonry axis.
2907 * This aligns the "masonry box" within our content box size.
2909 void AlignJustifyContentInMasonryAxis(nscoord aMasonryBoxSize,
2910 nscoord aContentBoxSize);
2912 * Apply `align/justify-tracks` alignment in our masonry axis.
2914 void AlignJustifyTracksInMasonryAxis(const LogicalSize& aContentSize,
2915 const nsSize& aContainerSize);
2917 // Helper for CollectSubgridItemsForAxis.
2918 static void CollectSubgridForAxis(LogicalAxis aAxis, WritingMode aContainerWM,
2919 const LineRange& aRangeInAxis,
2920 const LineRange& aRangeInOppositeAxis,
2921 const GridItemInfo& aItem,
2922 const nsTArray<GridItemInfo>& aItems,
2923 nsTArray<GridItemInfo>& aResult) {
2924 const auto oppositeAxis = GetOrthogonalAxis(aAxis);
2925 bool itemIsSubgridInOppositeAxis = aItem.IsSubgrid(oppositeAxis);
2926 auto subgridWM = aItem.mFrame->GetWritingMode();
2927 bool isOrthogonal = subgridWM.IsOrthogonalTo(aContainerWM);
2928 bool isSameDirInAxis =
2929 subgridWM.ParallelAxisStartsOnSameSide(aAxis, aContainerWM);
2930 bool isSameDirInOppositeAxis =
2931 subgridWM.ParallelAxisStartsOnSameSide(oppositeAxis, aContainerWM);
2932 if (isOrthogonal) {
2933 // We'll Transpose the area below so these needs to be transposed as well.
2934 std::swap(isSameDirInAxis, isSameDirInOppositeAxis);
2936 uint32_t offsetInAxis = aRangeInAxis.mStart;
2937 uint32_t gridEndInAxis = aRangeInAxis.Extent();
2938 uint32_t offsetInOppositeAxis = aRangeInOppositeAxis.mStart;
2939 uint32_t gridEndInOppositeAxis = aRangeInOppositeAxis.Extent();
2940 for (const auto& subgridItem : aItems) {
2941 auto newItem = aResult.AppendElement(
2942 isOrthogonal ? subgridItem.Transpose() : subgridItem);
2943 if (MOZ_UNLIKELY(!isSameDirInAxis)) {
2944 newItem->ReverseDirection(aAxis, gridEndInAxis);
2946 newItem->mArea.LineRangeForAxis(aAxis).Translate(offsetInAxis);
2947 if (itemIsSubgridInOppositeAxis) {
2948 if (MOZ_UNLIKELY(!isSameDirInOppositeAxis)) {
2949 newItem->ReverseDirection(oppositeAxis, gridEndInOppositeAxis);
2951 LineRange& range = newItem->mArea.LineRangeForAxis(oppositeAxis);
2952 range.Translate(offsetInOppositeAxis);
2954 if (newItem->IsSubgrid(aAxis)) {
2955 auto* subgrid =
2956 subgridItem.SubgridFrame()->GetProperty(Subgrid::Prop());
2957 CollectSubgridForAxis(aAxis, aContainerWM,
2958 newItem->mArea.LineRangeForAxis(aAxis),
2959 newItem->mArea.LineRangeForAxis(oppositeAxis),
2960 *newItem, subgrid->mGridItems, aResult);
2965 // Copy all descendant items from all our subgrid children that are subgridded
2966 // in aAxis recursively into aResult. All item grid area's and state are
2967 // translated to our coordinates.
2968 void CollectSubgridItemsForAxis(LogicalAxis aAxis,
2969 nsTArray<GridItemInfo>& aResult) const {
2970 for (const auto& item : mGridItems) {
2971 if (item.IsSubgrid(aAxis)) {
2972 const auto oppositeAxis = GetOrthogonalAxis(aAxis);
2973 auto* subgrid = item.SubgridFrame()->GetProperty(Subgrid::Prop());
2974 CollectSubgridForAxis(aAxis, mWM, item.mArea.LineRangeForAxis(aAxis),
2975 item.mArea.LineRangeForAxis(oppositeAxis), item,
2976 subgrid->mGridItems, aResult);
2981 Tracks& TracksFor(LogicalAxis aAxis) {
2982 return aAxis == eLogicalAxisBlock ? mRows : mCols;
2984 const Tracks& TracksFor(LogicalAxis aAxis) const {
2985 return aAxis == eLogicalAxisBlock ? mRows : mCols;
2988 CSSOrderAwareFrameIterator mIter;
2989 const nsStylePosition* const mGridStyle;
2990 Tracks mCols;
2991 Tracks mRows;
2992 TrackSizingFunctions mColFunctions;
2993 TrackSizingFunctions mRowFunctions;
2995 * Info about each (normal flow) grid item.
2997 nsTArray<GridItemInfo> mGridItems;
2999 * Info about each grid-aligned abs.pos. child.
3001 nsTArray<GridItemInfo> mAbsPosItems;
3004 * @note mReflowInput may be null when using the 2nd ctor above. In this case
3005 * we'll construct a dummy parent reflow input if we need it to calculate
3006 * min/max-content contributions when sizing tracks.
3008 const ReflowInput* const mReflowInput;
3009 gfxContext& mRenderingContext;
3010 nsGridContainerFrame* const mFrame;
3011 SharedGridData* mSharedGridData; // [weak] owned by mFrame's first-in-flow.
3012 /** Computed border+padding with mSkipSides applied. */
3013 LogicalMargin mBorderPadding;
3015 * BStart of this fragment in "grid space" (i.e. the concatenation of content
3016 * areas of all fragments). Equal to mRows.mSizes[mStartRow].mPosition,
3017 * or, if this fragment starts after the last row, the ConsumedBSize().
3019 nscoord mFragBStart;
3020 /** The start row for this fragment. */
3021 uint32_t mStartRow;
3023 * The start row for the next fragment, if any. If mNextFragmentStartRow ==
3024 * mStartRow then there are no rows in this fragment.
3026 uint32_t mNextFragmentStartRow;
3027 /** Our tentative ApplySkipSides bits. */
3028 LogicalSides mSkipSides;
3029 const WritingMode mWM;
3030 /** Initialized lazily, when we find the fragmentainer. */
3031 bool mInFragmentainer;
3033 private:
3034 GridReflowInput(nsGridContainerFrame* aFrame, gfxContext& aRenderingContext,
3035 const ReflowInput* aReflowInput,
3036 const nsStylePosition* aGridStyle, const WritingMode& aWM)
3037 : mIter(aFrame, kPrincipalList),
3038 mGridStyle(aGridStyle),
3039 mCols(eLogicalAxisInline),
3040 mRows(eLogicalAxisBlock),
3041 mColFunctions(mGridStyle->mGridTemplateColumns,
3042 mGridStyle->mGridAutoColumns,
3043 aFrame->IsSubgrid(eLogicalAxisInline)),
3044 mRowFunctions(mGridStyle->mGridTemplateRows, mGridStyle->mGridAutoRows,
3045 aFrame->IsSubgrid(eLogicalAxisBlock)),
3046 mReflowInput(aReflowInput),
3047 mRenderingContext(aRenderingContext),
3048 mFrame(aFrame),
3049 mSharedGridData(nullptr),
3050 mBorderPadding(aWM),
3051 mFragBStart(0),
3052 mStartRow(0),
3053 mNextFragmentStartRow(0),
3054 mSkipSides(aFrame->GetWritingMode()),
3055 mWM(aWM),
3056 mInFragmentainer(false) {
3057 MOZ_ASSERT(!aReflowInput || aReflowInput->mFrame == mFrame);
3058 if (aReflowInput) {
3059 mBorderPadding = aReflowInput->ComputedLogicalBorderPadding(mWM);
3060 mSkipSides = aFrame->PreReflowBlockLevelLogicalSkipSides();
3061 mBorderPadding.ApplySkipSides(mSkipSides);
3063 mCols.mIsMasonry = aFrame->IsMasonry(eLogicalAxisInline);
3064 mRows.mIsMasonry = aFrame->IsMasonry(eLogicalAxisBlock);
3065 MOZ_ASSERT(!(mCols.mIsMasonry && mRows.mIsMasonry),
3066 "can't have masonry layout in both axes");
3070 using GridReflowInput = nsGridContainerFrame::GridReflowInput;
3073 * The Grid implements grid item placement and the state of the grid -
3074 * the size of the explicit/implicit grid, which cells are occupied etc.
3076 struct MOZ_STACK_CLASS nsGridContainerFrame::Grid {
3077 explicit Grid(const Grid* aParentGrid = nullptr) : mParentGrid(aParentGrid) {}
3080 * Place all child frames into the grid and expand the (implicit) grid as
3081 * needed. The allocated GridAreas are stored in the GridAreaProperty
3082 * frame property on the child frame.
3083 * @param aRepeatSizing the container's [min-|max-]*size - used to determine
3084 * the number of repeat(auto-fill/fit) tracks.
3086 void PlaceGridItems(GridReflowInput& aState,
3087 const RepeatTrackSizingInput& aRepeatSizing);
3089 void SubgridPlaceGridItems(GridReflowInput& aParentState, Grid* aParentGrid,
3090 const GridItemInfo& aGridItem);
3093 * As above but for an abs.pos. child. Any 'auto' lines will be represented
3094 * by kAutoLine in the LineRange result.
3095 * @param aGridStart the first line in the final, but untranslated grid
3096 * @param aGridEnd the last line in the final, but untranslated grid
3098 LineRange ResolveAbsPosLineRange(const StyleGridLine& aStart,
3099 const StyleGridLine& aEnd,
3100 const LineNameMap& aNameMap,
3101 LogicalAxis aAxis, uint32_t aExplicitGridEnd,
3102 int32_t aGridStart, int32_t aGridEnd,
3103 const nsStylePosition* aStyle);
3106 * Return a GridArea for abs.pos. item with non-auto lines placed at
3107 * a definite line (1-based) with placement errors resolved. One or both
3108 * positions may still be 'auto'.
3109 * @param aChild the abs.pos. grid item to place
3110 * @param aStyle the StylePosition() for the grid container
3112 GridArea PlaceAbsPos(nsIFrame* aChild, const LineNameMap& aColLineNameMap,
3113 const LineNameMap& aRowLineNameMap,
3114 const nsStylePosition* aStyle);
3117 * Find the first column in row aLockedRow starting at aStartCol where aArea
3118 * could be placed without overlapping other items. The returned column may
3119 * cause aArea to overflow the current implicit grid bounds if placed there.
3121 uint32_t FindAutoCol(uint32_t aStartCol, uint32_t aLockedRow,
3122 const GridArea* aArea) const;
3125 * Place aArea in the first column (in row aArea->mRows.mStart) starting at
3126 * aStartCol without overlapping other items. The resulting aArea may
3127 * overflow the current implicit grid bounds.
3128 * @param aClampMaxColLine the maximum allowed column line number (zero-based)
3129 * Pre-condition: aArea->mRows.IsDefinite() is true.
3130 * Post-condition: aArea->IsDefinite() is true.
3132 void PlaceAutoCol(uint32_t aStartCol, GridArea* aArea,
3133 uint32_t aClampMaxColLine) const;
3136 * Find the first row in column aLockedCol starting at aStartRow where aArea
3137 * could be placed without overlapping other items. The returned row may
3138 * cause aArea to overflow the current implicit grid bounds if placed there.
3140 uint32_t FindAutoRow(uint32_t aLockedCol, uint32_t aStartRow,
3141 const GridArea* aArea) const;
3144 * Place aArea in the first row (in column aArea->mCols.mStart) starting at
3145 * aStartRow without overlapping other items. The resulting aArea may
3146 * overflow the current implicit grid bounds.
3147 * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
3148 * Pre-condition: aArea->mCols.IsDefinite() is true.
3149 * Post-condition: aArea->IsDefinite() is true.
3151 void PlaceAutoRow(uint32_t aStartRow, GridArea* aArea,
3152 uint32_t aClampMaxRowLine) const;
3155 * Place aArea in the first column starting at aStartCol,aStartRow without
3156 * causing it to overlap other items or overflow mGridColEnd.
3157 * If there's no such column in aStartRow, continue in position 1,aStartRow+1.
3158 * @param aClampMaxColLine the maximum allowed column line number (zero-based)
3159 * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
3160 * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
3161 * Post-condition: aArea->IsDefinite() is true.
3163 void PlaceAutoAutoInRowOrder(uint32_t aStartCol, uint32_t aStartRow,
3164 GridArea* aArea, uint32_t aClampMaxColLine,
3165 uint32_t aClampMaxRowLine) const;
3168 * Place aArea in the first row starting at aStartCol,aStartRow without
3169 * causing it to overlap other items or overflow mGridRowEnd.
3170 * If there's no such row in aStartCol, continue in position aStartCol+1,1.
3171 * @param aClampMaxColLine the maximum allowed column line number (zero-based)
3172 * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
3173 * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
3174 * Post-condition: aArea->IsDefinite() is true.
3176 void PlaceAutoAutoInColOrder(uint32_t aStartCol, uint32_t aStartRow,
3177 GridArea* aArea, uint32_t aClampMaxColLine,
3178 uint32_t aClampMaxRowLine) const;
3181 * Return aLine if it's inside the aMin..aMax range (inclusive),
3182 * otherwise return kAutoLine.
3184 static int32_t AutoIfOutside(int32_t aLine, int32_t aMin, int32_t aMax) {
3185 MOZ_ASSERT(aMin <= aMax);
3186 if (aLine < aMin || aLine > aMax) {
3187 return kAutoLine;
3189 return aLine;
3193 * Inflate the implicit grid to include aArea.
3194 * @param aArea may be definite or auto
3196 void InflateGridFor(const GridArea& aArea) {
3197 mGridColEnd = std::max(mGridColEnd, aArea.mCols.HypotheticalEnd());
3198 mGridRowEnd = std::max(mGridRowEnd, aArea.mRows.HypotheticalEnd());
3199 MOZ_ASSERT(mGridColEnd <= kTranslatedMaxLine &&
3200 mGridRowEnd <= kTranslatedMaxLine);
3204 * Calculates the empty tracks in a repeat(auto-fit).
3205 * @param aOutNumEmptyLines Outputs the number of tracks which are empty.
3206 * @param aSizingFunctions Sizing functions for the relevant axis.
3207 * @param aNumGridLines Number of grid lines for the relevant axis.
3208 * @param aIsEmptyFunc Functor to check if a cell is empty. This should be
3209 * mCellMap.IsColEmpty or mCellMap.IsRowEmpty, depending on the axis.
3211 template <typename IsEmptyFuncT>
3212 static Maybe<nsTArray<uint32_t>> CalculateAdjustForAutoFitElements(
3213 uint32_t* aOutNumEmptyTracks, TrackSizingFunctions& aSizingFunctions,
3214 uint32_t aNumGridLines, IsEmptyFuncT aIsEmptyFunc);
3217 * Return a line number for (non-auto) aLine, per:
3218 * http://dev.w3.org/csswg/css-grid/#line-placement
3219 * @param aLine style data for the line (must be non-auto)
3220 * @param aNth a number of lines to find from aFromIndex, negative if the
3221 * search should be in reverse order. In the case aLine has
3222 * a specified line name, it's permitted to pass in zero which
3223 * will be treated as one.
3224 * @param aFromIndex the zero-based index to start counting from
3225 * @param aLineNameList the explicit named lines
3226 * @param aSide the axis+edge we're resolving names for (e.g. if we're
3227 resolving a grid-row-start line, pass eLogicalSideBStart)
3228 * @param aExplicitGridEnd the last line in the explicit grid
3229 * @param aStyle the StylePosition() for the grid container
3230 * @return a definite line (1-based), clamped to
3231 * the mClampMinLine..mClampMaxLine range
3233 int32_t ResolveLine(const StyleGridLine& aLine, int32_t aNth,
3234 uint32_t aFromIndex, const LineNameMap& aNameMap,
3235 LogicalSide aSide, uint32_t aExplicitGridEnd,
3236 const nsStylePosition* aStyle);
3239 * Helper method for ResolveLineRange.
3240 * @see ResolveLineRange
3241 * @return a pair (start,end) of lines
3243 typedef std::pair<int32_t, int32_t> LinePair;
3244 LinePair ResolveLineRangeHelper(const StyleGridLine& aStart,
3245 const StyleGridLine& aEnd,
3246 const LineNameMap& aNameMap,
3247 LogicalAxis aAxis, uint32_t aExplicitGridEnd,
3248 const nsStylePosition* aStyle);
3251 * Return a LineRange based on the given style data. Non-auto lines
3252 * are resolved to a definite line number (1-based) per:
3253 * http://dev.w3.org/csswg/css-grid/#line-placement
3254 * with placement errors corrected per:
3255 * http://dev.w3.org/csswg/css-grid/#grid-placement-errors
3256 * @param aStyle the StylePosition() for the grid container
3257 * @param aStart style data for the start line
3258 * @param aEnd style data for the end line
3259 * @param aLineNameList the explicit named lines
3260 * @param aAxis the axis we're resolving names in
3261 * @param aExplicitGridEnd the last line in the explicit grid
3262 * @param aStyle the StylePosition() for the grid container
3264 LineRange ResolveLineRange(const StyleGridLine& aStart,
3265 const StyleGridLine& aEnd,
3266 const LineNameMap& aNameMap, LogicalAxis aAxis,
3267 uint32_t aExplicitGridEnd,
3268 const nsStylePosition* aStyle);
3271 * Return a GridArea with non-auto lines placed at a definite line (1-based)
3272 * with placement errors resolved. One or both positions may still
3273 * be 'auto'.
3274 * @param aChild the grid item
3275 * @param aStyle the StylePosition() for the grid container
3277 GridArea PlaceDefinite(nsIFrame* aChild, const LineNameMap& aColLineNameMap,
3278 const LineNameMap& aRowLineNameMap,
3279 const nsStylePosition* aStyle);
3281 bool HasImplicitNamedArea(nsAtom* aName) const {
3282 return mAreas && mAreas->has(aName);
3285 // Return true if aString ends in aSuffix and has at least one character
3286 // before the suffix. Assign aIndex to where the suffix starts.
3287 static bool IsNameWithSuffix(nsAtom* aString, const nsString& aSuffix,
3288 uint32_t* aIndex) {
3289 if (StringEndsWith(nsDependentAtomString(aString), aSuffix)) {
3290 *aIndex = aString->GetLength() - aSuffix.Length();
3291 return *aIndex != 0;
3293 return false;
3296 static bool IsNameWithEndSuffix(nsAtom* aString, uint32_t* aIndex) {
3297 return IsNameWithSuffix(aString, u"-end"_ns, aIndex);
3300 static bool IsNameWithStartSuffix(nsAtom* aString, uint32_t* aIndex) {
3301 return IsNameWithSuffix(aString, u"-start"_ns, aIndex);
3304 // Return the relevant parent LineNameMap for the given subgrid axis aAxis.
3305 const LineNameMap* ParentLineMapForAxis(bool aIsOrthogonal,
3306 LogicalAxis aAxis) const {
3307 if (!mParentGrid) {
3308 return nullptr;
3310 bool isRows = aIsOrthogonal == (aAxis == eLogicalAxisInline);
3311 return isRows ? mParentGrid->mRowNameMap : mParentGrid->mColNameMap;
3314 void SetLineMaps(const LineNameMap* aColNameMap,
3315 const LineNameMap* aRowNameMap) {
3316 mColNameMap = aColNameMap;
3317 mRowNameMap = aRowNameMap;
3321 * A CellMap holds state for each cell in the grid.
3322 * It's row major. It's sparse in the sense that it only has enough rows to
3323 * cover the last row that has a grid item. Each row only has enough entries
3324 * to cover columns that are occupied *on that row*, i.e. it's not a full
3325 * matrix covering the entire implicit grid. An absent Cell means that it's
3326 * unoccupied by any grid item.
3328 struct CellMap {
3329 struct Cell {
3330 constexpr Cell() : mIsOccupied(false) {}
3331 bool mIsOccupied : 1;
3334 void Fill(const GridArea& aGridArea) {
3335 MOZ_ASSERT(aGridArea.IsDefinite());
3336 MOZ_ASSERT(aGridArea.mRows.mStart < aGridArea.mRows.mEnd);
3337 MOZ_ASSERT(aGridArea.mCols.mStart < aGridArea.mCols.mEnd);
3338 const auto numRows = aGridArea.mRows.mEnd;
3339 const auto numCols = aGridArea.mCols.mEnd;
3340 mCells.EnsureLengthAtLeast(numRows);
3341 for (auto i = aGridArea.mRows.mStart; i < numRows; ++i) {
3342 nsTArray<Cell>& cellsInRow = mCells[i];
3343 cellsInRow.EnsureLengthAtLeast(numCols);
3344 for (auto j = aGridArea.mCols.mStart; j < numCols; ++j) {
3345 cellsInRow[j].mIsOccupied = true;
3350 uint32_t IsEmptyCol(uint32_t aCol) const {
3351 for (auto& row : mCells) {
3352 if (aCol < row.Length() && row[aCol].mIsOccupied) {
3353 return false;
3356 return true;
3358 uint32_t IsEmptyRow(uint32_t aRow) const {
3359 if (aRow >= mCells.Length()) {
3360 return true;
3362 for (const Cell& cell : mCells[aRow]) {
3363 if (cell.mIsOccupied) {
3364 return false;
3367 return true;
3369 #ifdef DEBUG
3370 void Dump() const {
3371 const size_t numRows = mCells.Length();
3372 for (size_t i = 0; i < numRows; ++i) {
3373 const nsTArray<Cell>& cellsInRow = mCells[i];
3374 const size_t numCols = cellsInRow.Length();
3375 printf("%lu:\t", (unsigned long)i + 1);
3376 for (size_t j = 0; j < numCols; ++j) {
3377 printf(cellsInRow[j].mIsOccupied ? "X " : ". ");
3379 printf("\n");
3382 #endif
3384 nsTArray<nsTArray<Cell>> mCells;
3388 * State for each cell in the grid.
3390 CellMap mCellMap;
3392 * @see HasImplicitNamedArea.
3394 ImplicitNamedAreas* mAreas;
3396 * The last column grid line (1-based) in the explicit grid.
3397 * (i.e. the number of explicit columns + 1)
3399 uint32_t mExplicitGridColEnd;
3401 * The last row grid line (1-based) in the explicit grid.
3402 * (i.e. the number of explicit rows + 1)
3404 uint32_t mExplicitGridRowEnd;
3405 // Same for the implicit grid, except these become zero-based after
3406 // resolving definite lines.
3407 uint32_t mGridColEnd;
3408 uint32_t mGridRowEnd;
3411 * Offsets from the start of the implicit grid to the start of the translated
3412 * explicit grid. They are zero if there are no implicit lines before 1,1.
3413 * e.g. "grid-column: span 3 / 1" makes mExplicitGridOffsetCol = 3 and the
3414 * corresponding GridArea::mCols will be 0 / 3 in the zero-based translated
3415 * grid.
3417 uint32_t mExplicitGridOffsetCol;
3418 uint32_t mExplicitGridOffsetRow;
3421 * Our parent grid if any.
3423 const Grid* mParentGrid;
3426 * Our LineNameMaps.
3428 const LineNameMap* mColNameMap;
3429 const LineNameMap* mRowNameMap;
3433 * Compute margin+border+padding for aGridItem.mFrame (a subgrid) and store it
3434 * on its Subgrid property (and return that property).
3435 * aPercentageBasis is in the grid item's writing-mode.
3437 static Subgrid* SubgridComputeMarginBorderPadding(
3438 const GridItemInfo& aGridItem, const LogicalSize& aPercentageBasis) {
3439 auto* subgridFrame = aGridItem.SubgridFrame();
3440 auto cbWM = aGridItem.mFrame->GetParent()->GetWritingMode();
3441 auto* subgrid = subgridFrame->GetProperty(Subgrid::Prop());
3442 auto wm = subgridFrame->GetWritingMode();
3443 auto pmPercentageBasis = cbWM.IsOrthogonalTo(wm) ? aPercentageBasis.BSize(wm)
3444 : aPercentageBasis.ISize(wm);
3445 SizeComputationInput sz(subgridFrame, nullptr, cbWM, pmPercentageBasis);
3446 subgrid->mMarginBorderPadding =
3447 sz.ComputedLogicalMargin(cbWM) + sz.ComputedLogicalBorderPadding(cbWM);
3449 if (aGridItem.mFrame != subgridFrame) {
3450 nsIScrollableFrame* scrollFrame = aGridItem.mFrame->GetScrollTargetFrame();
3451 if (scrollFrame) {
3452 MOZ_ASSERT(
3453 sz.ComputedLogicalMargin(cbWM) == LogicalMargin(cbWM) &&
3454 sz.ComputedLogicalBorder(cbWM) == LogicalMargin(cbWM),
3455 "A scrolled inner frame should not have any margin or border!");
3457 // Add the margin and border from the (outer) scroll frame.
3458 SizeComputationInput szScrollFrame(aGridItem.mFrame, nullptr, cbWM,
3459 pmPercentageBasis);
3460 subgrid->mMarginBorderPadding +=
3461 szScrollFrame.ComputedLogicalMargin(cbWM) +
3462 szScrollFrame.ComputedLogicalBorder(cbWM);
3464 nsMargin ssz = scrollFrame->GetActualScrollbarSizes();
3465 subgrid->mMarginBorderPadding += LogicalMargin(cbWM, ssz);
3468 if (aGridItem.mFrame->IsFieldSetFrame()) {
3469 const auto* f = static_cast<nsFieldSetFrame*>(aGridItem.mFrame);
3470 const auto* inner = f->GetInner();
3471 auto wm = inner->GetWritingMode();
3472 LogicalPoint pos = inner->GetLogicalPosition(aGridItem.mFrame->GetSize());
3473 // The legend is always on the BStart side and it inflates the fieldset's
3474 // "border area" size. The inner frame's b-start pos equals that size.
3475 LogicalMargin offsets(wm, pos.B(wm), 0, 0, 0);
3476 subgrid->mMarginBorderPadding += offsets.ConvertTo(cbWM, wm);
3479 return subgrid;
3482 static void CopyUsedTrackSizes(nsTArray<TrackSize>& aResult,
3483 const nsGridContainerFrame* aUsedTrackSizesFrame,
3484 const UsedTrackSizes* aUsedTrackSizes,
3485 const nsGridContainerFrame* aSubgridFrame,
3486 const Subgrid* aSubgrid,
3487 LogicalAxis aSubgridAxis) {
3488 MOZ_ASSERT(aSubgridFrame->ParentGridContainerForSubgrid() ==
3489 aUsedTrackSizesFrame);
3490 aResult.SetLength(aSubgridAxis == eLogicalAxisInline ? aSubgrid->mGridColEnd
3491 : aSubgrid->mGridRowEnd);
3492 auto parentAxis =
3493 aSubgrid->mIsOrthogonal ? GetOrthogonalAxis(aSubgridAxis) : aSubgridAxis;
3494 const auto& parentSizes = aUsedTrackSizes->mSizes[parentAxis];
3495 MOZ_ASSERT(aUsedTrackSizes->mCanResolveLineRangeSize[parentAxis]);
3496 if (parentSizes.IsEmpty()) {
3497 return;
3499 const auto& range = aSubgrid->mArea.LineRangeForAxis(parentAxis);
3500 const auto cbwm = aUsedTrackSizesFrame->GetWritingMode();
3501 const auto wm = aSubgridFrame->GetWritingMode();
3502 // Recompute the MBP to resolve percentages against the resolved track sizes.
3503 if (parentAxis == eLogicalAxisInline) {
3504 // Find the subgrid's grid item frame in its parent grid container. This
3505 // is usually the same as aSubgridFrame but it may also have a ScrollFrame,
3506 // FieldSetFrame etc. We just loop until we see the first ancestor
3507 // GridContainerFrame and pick the last frame we saw before that.
3508 // Note that all subgrids are inside a parent (sub)grid container.
3509 const nsIFrame* outerGridItemFrame = aSubgridFrame;
3510 for (nsIFrame* parent = aSubgridFrame->GetParent();
3511 parent != aUsedTrackSizesFrame; parent = parent->GetParent()) {
3512 MOZ_ASSERT(!parent->IsGridContainerFrame());
3513 outerGridItemFrame = parent;
3515 auto sizeInAxis = range.ToLength(aUsedTrackSizes->mSizes[parentAxis]);
3516 LogicalSize pmPercentageBasis =
3517 aSubgrid->mIsOrthogonal ? LogicalSize(wm, nscoord(0), sizeInAxis)
3518 : LogicalSize(wm, sizeInAxis, nscoord(0));
3519 GridItemInfo info(const_cast<nsIFrame*>(outerGridItemFrame),
3520 aSubgrid->mArea);
3521 SubgridComputeMarginBorderPadding(info, pmPercentageBasis);
3523 const LogicalMargin& mbp = aSubgrid->mMarginBorderPadding;
3524 nscoord startMBP;
3525 nscoord endMBP;
3526 if (MOZ_LIKELY(cbwm.ParallelAxisStartsOnSameSide(parentAxis, wm))) {
3527 startMBP = mbp.Start(parentAxis, cbwm);
3528 endMBP = mbp.End(parentAxis, cbwm);
3529 uint32_t i = range.mStart;
3530 nscoord startPos = parentSizes[i].mPosition + startMBP;
3531 for (auto& sz : aResult) {
3532 sz = parentSizes[i++];
3533 sz.mPosition -= startPos;
3535 } else {
3536 startMBP = mbp.End(parentAxis, cbwm);
3537 endMBP = mbp.Start(parentAxis, cbwm);
3538 uint32_t i = range.mEnd - 1;
3539 const auto& parentEnd = parentSizes[i];
3540 nscoord parentEndPos = parentEnd.mPosition + parentEnd.mBase - startMBP;
3541 for (auto& sz : aResult) {
3542 sz = parentSizes[i--];
3543 sz.mPosition = parentEndPos - (sz.mPosition + sz.mBase);
3546 auto& startTrack = aResult[0];
3547 startTrack.mPosition = 0;
3548 startTrack.mBase -= startMBP;
3549 if (MOZ_UNLIKELY(startTrack.mBase < nscoord(0))) {
3550 // Our MBP doesn't fit in the start track. Adjust the track position
3551 // to maintain track alignment with our parent.
3552 startTrack.mPosition = startTrack.mBase;
3553 startTrack.mBase = nscoord(0);
3555 auto& endTrack = aResult.LastElement();
3556 endTrack.mBase -= endMBP;
3557 if (MOZ_UNLIKELY(endTrack.mBase < nscoord(0))) {
3558 endTrack.mBase = nscoord(0);
3562 void nsGridContainerFrame::UsedTrackSizes::ResolveTrackSizesForAxis(
3563 nsGridContainerFrame* aFrame, LogicalAxis aAxis, gfxContext& aRC) {
3564 if (mCanResolveLineRangeSize[aAxis]) {
3565 return;
3567 if (!aFrame->IsSubgrid()) {
3568 // We can't resolve sizes in this axis at this point. aFrame is the top grid
3569 // container, which will store its final track sizes later once they're
3570 // resolved in this axis (in GridReflowInput::CalculateTrackSizesForAxis).
3571 // The single caller of this method only needs track sizes for
3572 // calculating a CB size and it will treat it as indefinite when
3573 // this happens.
3574 return;
3576 auto* parent = aFrame->ParentGridContainerForSubgrid();
3577 auto* parentSizes = parent->GetUsedTrackSizes();
3578 if (!parentSizes) {
3579 parentSizes = new UsedTrackSizes();
3580 parent->SetProperty(UsedTrackSizes::Prop(), parentSizes);
3582 auto* subgrid = aFrame->GetProperty(Subgrid::Prop());
3583 const auto parentAxis =
3584 subgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
3585 parentSizes->ResolveTrackSizesForAxis(parent, parentAxis, aRC);
3586 if (!parentSizes->mCanResolveLineRangeSize[parentAxis]) {
3587 if (aFrame->IsSubgrid(aAxis)) {
3588 ResolveSubgridTrackSizesForAxis(aFrame, aAxis, subgrid, aRC,
3589 NS_UNCONSTRAINEDSIZE);
3591 return;
3593 if (aFrame->IsSubgrid(aAxis)) {
3594 CopyUsedTrackSizes(mSizes[aAxis], parent, parentSizes, aFrame, subgrid,
3595 aAxis);
3596 mCanResolveLineRangeSize[aAxis] = true;
3597 } else {
3598 const auto& range = subgrid->mArea.LineRangeForAxis(parentAxis);
3599 nscoord contentBoxSize = range.ToLength(parentSizes->mSizes[parentAxis]);
3600 auto parentWM = aFrame->GetParent()->GetWritingMode();
3601 contentBoxSize -=
3602 subgrid->mMarginBorderPadding.StartEnd(parentAxis, parentWM);
3603 contentBoxSize = std::max(nscoord(0), contentBoxSize);
3604 ResolveSubgridTrackSizesForAxis(aFrame, aAxis, subgrid, aRC,
3605 contentBoxSize);
3609 void nsGridContainerFrame::UsedTrackSizes::ResolveSubgridTrackSizesForAxis(
3610 nsGridContainerFrame* aFrame, LogicalAxis aAxis, Subgrid* aSubgrid,
3611 gfxContext& aRC, nscoord aContentBoxSize) {
3612 GridReflowInput state(aFrame, aRC);
3613 state.mGridItems = aSubgrid->mGridItems.Clone();
3614 Grid grid;
3615 grid.mGridColEnd = aSubgrid->mGridColEnd;
3616 grid.mGridRowEnd = aSubgrid->mGridRowEnd;
3617 state.CalculateTrackSizesForAxis(aAxis, grid, aContentBoxSize,
3618 SizingConstraint::NoConstraint);
3619 const auto& tracks = aAxis == eLogicalAxisInline ? state.mCols : state.mRows;
3620 mSizes[aAxis].Assign(tracks.mSizes);
3621 mCanResolveLineRangeSize[aAxis] = tracks.mCanResolveLineRangeSize;
3622 MOZ_ASSERT(mCanResolveLineRangeSize[aAxis]);
3625 void nsGridContainerFrame::GridReflowInput::CalculateTrackSizesForAxis(
3626 LogicalAxis aAxis, const Grid& aGrid, nscoord aContentBoxSize,
3627 SizingConstraint aConstraint) {
3628 auto& tracks = aAxis == eLogicalAxisInline ? mCols : mRows;
3629 const auto& sizingFunctions =
3630 aAxis == eLogicalAxisInline ? mColFunctions : mRowFunctions;
3631 const auto& gapStyle = aAxis == eLogicalAxisInline ? mGridStyle->mColumnGap
3632 : mGridStyle->mRowGap;
3633 if (tracks.mIsMasonry) {
3634 // See comment on nsGridContainerFrame::MasonryLayout().
3635 tracks.Initialize(sizingFunctions, gapStyle, 2, aContentBoxSize);
3636 tracks.mCanResolveLineRangeSize = true;
3637 return;
3639 uint32_t gridEnd =
3640 aAxis == eLogicalAxisInline ? aGrid.mGridColEnd : aGrid.mGridRowEnd;
3641 Maybe<TrackSizingFunctions> fallbackTrackSizing;
3643 bool useParentGaps = false;
3644 const bool isSubgriddedAxis = mFrame->IsSubgrid(aAxis);
3645 if (MOZ_LIKELY(!isSubgriddedAxis)) {
3646 tracks.Initialize(sizingFunctions, gapStyle, gridEnd, aContentBoxSize);
3647 } else {
3648 tracks.mGridGap =
3649 nsLayoutUtils::ResolveGapToLength(gapStyle, aContentBoxSize);
3650 tracks.mContentBoxSize = aContentBoxSize;
3651 const auto* subgrid = mFrame->GetProperty(Subgrid::Prop());
3652 tracks.mSizes.SetLength(gridEnd);
3653 auto* parent = mFrame->ParentGridContainerForSubgrid();
3654 auto parentAxis = subgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
3655 const auto* parentSizes = parent->GetUsedTrackSizes();
3656 if (parentSizes && parentSizes->mCanResolveLineRangeSize[parentAxis]) {
3657 CopyUsedTrackSizes(tracks.mSizes, parent, parentSizes, mFrame, subgrid,
3658 aAxis);
3659 useParentGaps = gapStyle.IsNormal();
3660 } else {
3661 fallbackTrackSizing.emplace(TrackSizingFunctions::ForSubgridFallback(
3662 mFrame, subgrid, parent, parentAxis));
3663 tracks.Initialize(*fallbackTrackSizing, gapStyle, gridEnd,
3664 aContentBoxSize);
3668 // We run the Track Sizing Algorithm in non-subgridded axes, and in some
3669 // cases in a subgridded axis when our parent track sizes aren't resolved yet.
3670 if (MOZ_LIKELY(!isSubgriddedAxis) || fallbackTrackSizing.isSome()) {
3671 const size_t origGridItemCount = mGridItems.Length();
3672 if (mFrame->HasSubgridItems(aAxis)) {
3673 CollectSubgridItemsForAxis(aAxis, mGridItems);
3675 tracks.CalculateSizes(
3676 *this, mGridItems,
3677 fallbackTrackSizing ? *fallbackTrackSizing : sizingFunctions,
3678 aContentBoxSize,
3679 aAxis == eLogicalAxisInline ? &GridArea::mCols : &GridArea::mRows,
3680 aConstraint);
3681 // XXXmats we're losing the baseline state of subgrid descendants that
3682 // CollectSubgridItemsForAxis added here. We need to propagate that
3683 // state into the subgrid's Reflow somehow...
3684 mGridItems.TruncateLength(origGridItemCount);
3687 if (aContentBoxSize != NS_UNCONSTRAINEDSIZE) {
3688 auto alignment = mGridStyle->UsedContentAlignment(tracks.mAxis);
3689 tracks.AlignJustifyContent(mGridStyle, alignment, mWM, aContentBoxSize,
3690 isSubgriddedAxis);
3691 } else if (!useParentGaps) {
3692 const nscoord gridGap = tracks.mGridGap;
3693 nscoord pos = 0;
3694 for (TrackSize& sz : tracks.mSizes) {
3695 sz.mPosition = pos;
3696 pos += sz.mBase + gridGap;
3700 if (aConstraint == SizingConstraint::NoConstraint &&
3701 (mFrame->HasSubgridItems() || mFrame->IsSubgrid())) {
3702 mFrame->StoreUsedTrackSizes(aAxis, tracks.mSizes);
3705 // positions and sizes are now final
3706 tracks.mCanResolveLineRangeSize = true;
3709 void nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
3710 const Grid& aGrid, const LogicalSize& aContentBox,
3711 SizingConstraint aConstraint) {
3712 CalculateTrackSizesForAxis(eLogicalAxisInline, aGrid, aContentBox.ISize(mWM),
3713 aConstraint);
3714 CalculateTrackSizesForAxis(eLogicalAxisBlock, aGrid, aContentBox.BSize(mWM),
3715 aConstraint);
3718 // Align an item's margin box in its aAxis inside aCBSize.
3719 static void AlignJustifySelf(StyleAlignFlags aAlignment, LogicalAxis aAxis,
3720 AlignJustifyFlags aFlags, nscoord aBaselineAdjust,
3721 nscoord aCBSize, const ReflowInput& aRI,
3722 const LogicalSize& aChildSize,
3723 LogicalPoint* aPos) {
3724 MOZ_ASSERT(aAlignment != StyleAlignFlags::AUTO,
3725 "unexpected 'auto' "
3726 "computed value for normal flow grid item");
3728 // NOTE: this is the resulting frame offset (border box).
3729 nscoord offset = CSSAlignUtils::AlignJustifySelf(
3730 aAlignment, aAxis, aFlags, aBaselineAdjust, aCBSize, aRI, aChildSize);
3732 // Set the position (aPos) for the requested alignment.
3733 if (offset != 0) {
3734 WritingMode wm = aRI.GetWritingMode();
3735 nscoord& pos = aAxis == eLogicalAxisBlock ? aPos->B(wm) : aPos->I(wm);
3736 pos += MOZ_LIKELY(aFlags & AlignJustifyFlags::SameSide) ? offset : -offset;
3740 static void AlignSelf(const nsGridContainerFrame::GridItemInfo& aGridItem,
3741 StyleAlignFlags aAlignSelf, nscoord aCBSize,
3742 const WritingMode aCBWM, const ReflowInput& aRI,
3743 const LogicalSize& aSize, AlignJustifyFlags aFlags,
3744 LogicalPoint* aPos) {
3745 AlignJustifyFlags flags = aFlags;
3746 if (aAlignSelf & StyleAlignFlags::SAFE) {
3747 flags |= AlignJustifyFlags::OverflowSafe;
3749 aAlignSelf &= ~StyleAlignFlags::FLAG_BITS;
3751 WritingMode childWM = aRI.GetWritingMode();
3752 if (aCBWM.ParallelAxisStartsOnSameSide(eLogicalAxisBlock, childWM)) {
3753 flags |= AlignJustifyFlags::SameSide;
3756 // Grid's 'align-self' axis is never parallel to the container's inline axis.
3757 if (aAlignSelf == StyleAlignFlags::LEFT ||
3758 aAlignSelf == StyleAlignFlags::RIGHT) {
3759 aAlignSelf = StyleAlignFlags::START;
3761 if (MOZ_LIKELY(aAlignSelf == StyleAlignFlags::NORMAL)) {
3762 aAlignSelf = StyleAlignFlags::STRETCH;
3765 nscoord baselineAdjust = 0;
3766 if (aAlignSelf == StyleAlignFlags::BASELINE ||
3767 aAlignSelf == StyleAlignFlags::LAST_BASELINE) {
3768 aAlignSelf = aGridItem.GetSelfBaseline(aAlignSelf, eLogicalAxisBlock,
3769 &baselineAdjust);
3770 // Adjust the baseline alignment value if the baseline affects the opposite
3771 // side of what AlignJustifySelf expects.
3772 auto state = aGridItem.mState[eLogicalAxisBlock];
3773 if (aAlignSelf == StyleAlignFlags::LAST_BASELINE &&
3774 !GridItemInfo::BaselineAlignmentAffectsEndSide(state)) {
3775 aAlignSelf = StyleAlignFlags::BASELINE;
3776 } else if (aAlignSelf == StyleAlignFlags::BASELINE &&
3777 GridItemInfo::BaselineAlignmentAffectsEndSide(state)) {
3778 aAlignSelf = StyleAlignFlags::LAST_BASELINE;
3782 bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
3783 LogicalAxis axis = isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock;
3784 AlignJustifySelf(aAlignSelf, axis, flags, baselineAdjust, aCBSize, aRI, aSize,
3785 aPos);
3788 static void JustifySelf(const nsGridContainerFrame::GridItemInfo& aGridItem,
3789 StyleAlignFlags aJustifySelf, nscoord aCBSize,
3790 const WritingMode aCBWM, const ReflowInput& aRI,
3791 const LogicalSize& aSize, AlignJustifyFlags aFlags,
3792 LogicalPoint* aPos) {
3793 AlignJustifyFlags flags = aFlags;
3794 if (aJustifySelf & StyleAlignFlags::SAFE) {
3795 flags |= AlignJustifyFlags::OverflowSafe;
3797 aJustifySelf &= ~StyleAlignFlags::FLAG_BITS;
3799 WritingMode childWM = aRI.GetWritingMode();
3800 if (aCBWM.ParallelAxisStartsOnSameSide(eLogicalAxisInline, childWM)) {
3801 flags |= AlignJustifyFlags::SameSide;
3804 if (MOZ_LIKELY(aJustifySelf == StyleAlignFlags::NORMAL)) {
3805 aJustifySelf = StyleAlignFlags::STRETCH;
3808 nscoord baselineAdjust = 0;
3809 // Grid's 'justify-self' axis is always parallel to the container's inline
3810 // axis, so justify-self:left|right always applies.
3811 if (aJustifySelf == StyleAlignFlags::LEFT) {
3812 aJustifySelf =
3813 aCBWM.IsBidiLTR() ? StyleAlignFlags::START : StyleAlignFlags::END;
3814 } else if (aJustifySelf == StyleAlignFlags::RIGHT) {
3815 aJustifySelf =
3816 aCBWM.IsBidiLTR() ? StyleAlignFlags::END : StyleAlignFlags::START;
3817 } else if (aJustifySelf == StyleAlignFlags::BASELINE ||
3818 aJustifySelf == StyleAlignFlags::LAST_BASELINE) {
3819 aJustifySelf = aGridItem.GetSelfBaseline(aJustifySelf, eLogicalAxisInline,
3820 &baselineAdjust);
3821 // Adjust the baseline alignment value if the baseline affects the opposite
3822 // side of what AlignJustifySelf expects.
3823 auto state = aGridItem.mState[eLogicalAxisInline];
3824 if (aJustifySelf == StyleAlignFlags::LAST_BASELINE &&
3825 !GridItemInfo::BaselineAlignmentAffectsEndSide(state)) {
3826 aJustifySelf = StyleAlignFlags::BASELINE;
3827 } else if (aJustifySelf == StyleAlignFlags::BASELINE &&
3828 GridItemInfo::BaselineAlignmentAffectsEndSide(state)) {
3829 aJustifySelf = StyleAlignFlags::LAST_BASELINE;
3833 bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
3834 LogicalAxis axis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
3835 AlignJustifySelf(aJustifySelf, axis, flags, baselineAdjust, aCBSize, aRI,
3836 aSize, aPos);
3839 static StyleAlignFlags GetAlignJustifyValue(StyleAlignFlags aAlignment,
3840 const WritingMode aWM,
3841 const bool aIsAlign,
3842 bool* aOverflowSafe) {
3843 *aOverflowSafe = bool(aAlignment & StyleAlignFlags::SAFE);
3844 aAlignment &= ~StyleAlignFlags::FLAG_BITS;
3846 // Map some alignment values to 'start' / 'end'.
3847 if (aAlignment == StyleAlignFlags::LEFT ||
3848 aAlignment == StyleAlignFlags::RIGHT) {
3849 if (aIsAlign) {
3850 // Grid's 'align-content' axis is never parallel to the inline axis.
3851 return StyleAlignFlags::START;
3853 bool isStart = aWM.IsBidiLTR() == (aAlignment == StyleAlignFlags::LEFT);
3854 return isStart ? StyleAlignFlags::START : StyleAlignFlags::END;
3856 if (aAlignment == StyleAlignFlags::FLEX_START) {
3857 return StyleAlignFlags::START; // same as 'start' for Grid
3859 if (aAlignment == StyleAlignFlags::FLEX_END) {
3860 return StyleAlignFlags::END; // same as 'end' for Grid
3862 return aAlignment;
3865 static Maybe<StyleAlignFlags> GetAlignJustifyFallbackIfAny(
3866 const StyleContentDistribution& aDistribution, const WritingMode aWM,
3867 const bool aIsAlign, bool* aOverflowSafe) {
3868 // TODO: Eventually this should look at aDistribution's fallback alignment,
3869 // see https://github.com/w3c/csswg-drafts/issues/1002.
3870 if (aDistribution.primary == StyleAlignFlags::STRETCH ||
3871 aDistribution.primary == StyleAlignFlags::SPACE_BETWEEN) {
3872 return Some(StyleAlignFlags::START);
3874 if (aDistribution.primary == StyleAlignFlags::SPACE_AROUND ||
3875 aDistribution.primary == StyleAlignFlags::SPACE_EVENLY) {
3876 return Some(StyleAlignFlags::CENTER);
3878 return Nothing();
3881 //----------------------------------------------------------------------
3883 // Frame class boilerplate
3884 // =======================
3886 NS_QUERYFRAME_HEAD(nsGridContainerFrame)
3887 NS_QUERYFRAME_ENTRY(nsGridContainerFrame)
3888 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
3890 NS_IMPL_FRAMEARENA_HELPERS(nsGridContainerFrame)
3892 nsContainerFrame* NS_NewGridContainerFrame(PresShell* aPresShell,
3893 ComputedStyle* aStyle) {
3894 return new (aPresShell)
3895 nsGridContainerFrame(aStyle, aPresShell->GetPresContext());
3898 //----------------------------------------------------------------------
3900 // nsGridContainerFrame Method Implementations
3901 // ===========================================
3903 /*static*/ const nsRect& nsGridContainerFrame::GridItemCB(nsIFrame* aChild) {
3904 MOZ_ASSERT(aChild->IsAbsolutelyPositioned());
3905 nsRect* cb = aChild->GetProperty(GridItemContainingBlockRect());
3906 MOZ_ASSERT(cb,
3907 "this method must only be called on grid items, and the grid "
3908 "container should've reflowed this item by now and set up cb");
3909 return *cb;
3912 void nsGridContainerFrame::AddImplicitNamedAreas(
3913 Span<LineNameList> aLineNameLists) {
3914 // http://dev.w3.org/csswg/css-grid/#implicit-named-areas
3915 // Note: recording these names for fast lookup later is just an optimization.
3916 const uint32_t len = std::min(aLineNameLists.Length(), size_t(kMaxLine));
3917 nsTHashSet<nsString> currentStarts;
3918 ImplicitNamedAreas* areas = GetImplicitNamedAreas();
3919 for (uint32_t i = 0; i < len; ++i) {
3920 for (const auto& nameIdent : aLineNameLists[i].AsSpan()) {
3921 nsAtom* name = nameIdent.AsAtom();
3922 uint32_t indexOfSuffix;
3923 if (Grid::IsNameWithStartSuffix(name, &indexOfSuffix) ||
3924 Grid::IsNameWithEndSuffix(name, &indexOfSuffix)) {
3925 // Extract the name that was found earlier.
3926 nsDependentSubstring areaName(nsDependentAtomString(name), 0,
3927 indexOfSuffix);
3929 // Lazily create the ImplicitNamedAreas.
3930 if (!areas) {
3931 areas = new ImplicitNamedAreas;
3932 SetProperty(ImplicitNamedAreasProperty(), areas);
3935 RefPtr<nsAtom> name = NS_Atomize(areaName);
3936 auto addPtr = areas->lookupForAdd(name);
3937 if (!addPtr) {
3938 if (!areas->add(
3939 addPtr, name,
3940 NamedArea{StyleAtom(do_AddRef(name)), {0, 0}, {0, 0}})) {
3941 MOZ_CRASH("OOM while adding grid name lists");
3949 void nsGridContainerFrame::InitImplicitNamedAreas(
3950 const nsStylePosition* aStyle) {
3951 ImplicitNamedAreas* areas = GetImplicitNamedAreas();
3952 if (areas) {
3953 // Clear it, but reuse the hashtable itself for now. We'll remove it
3954 // below if it isn't needed anymore.
3955 areas->clear();
3957 auto Add = [&](const GridTemplate& aTemplate, bool aIsSubgrid) {
3958 AddImplicitNamedAreas(aTemplate.LineNameLists(aIsSubgrid));
3959 for (auto& value : aTemplate.TrackListValues()) {
3960 if (value.IsTrackRepeat()) {
3961 AddImplicitNamedAreas(value.AsTrackRepeat().line_names.AsSpan());
3965 Add(aStyle->mGridTemplateColumns, IsSubgrid(eLogicalAxisInline));
3966 Add(aStyle->mGridTemplateRows, IsSubgrid(eLogicalAxisBlock));
3967 if (areas && areas->count() == 0) {
3968 RemoveProperty(ImplicitNamedAreasProperty());
3972 int32_t nsGridContainerFrame::Grid::ResolveLine(
3973 const StyleGridLine& aLine, int32_t aNth, uint32_t aFromIndex,
3974 const LineNameMap& aNameMap, LogicalSide aSide, uint32_t aExplicitGridEnd,
3975 const nsStylePosition* aStyle) {
3976 MOZ_ASSERT(!aLine.IsAuto());
3977 int32_t line = 0;
3978 if (aLine.LineName()->IsEmpty()) {
3979 MOZ_ASSERT(aNth != 0, "css-grid 9.2: <integer> must not be zero.");
3980 line = int32_t(aFromIndex) + aNth;
3981 } else {
3982 if (aNth == 0) {
3983 // <integer> was omitted; treat it as 1.
3984 aNth = 1;
3986 bool isNameOnly = !aLine.is_span && aLine.line_num == 0;
3987 if (isNameOnly) {
3988 AutoTArray<uint32_t, 16> implicitLines;
3989 aNameMap.FindNamedAreas(aLine.ident.AsAtom(), aSide, implicitLines);
3990 if (!implicitLines.IsEmpty() ||
3991 aNameMap.HasImplicitNamedArea(aLine.LineName())) {
3992 // aName is a named area - look for explicit lines named
3993 // <name>-start/-end depending on which side we're resolving.
3994 // http://dev.w3.org/csswg/css-grid/#grid-placement-slot
3995 nsAutoString lineName(nsDependentAtomString(aLine.LineName()));
3996 if (IsStart(aSide)) {
3997 lineName.AppendLiteral("-start");
3998 } else {
3999 lineName.AppendLiteral("-end");
4001 RefPtr<nsAtom> name = NS_Atomize(lineName);
4002 line = aNameMap.FindNamedLine(name, &aNth, aFromIndex, implicitLines);
4006 if (line == 0) {
4007 // If LineName() ends in -start/-end, try the prefix as a named area.
4008 AutoTArray<uint32_t, 16> implicitLines;
4009 uint32_t index;
4010 bool useStart = IsNameWithStartSuffix(aLine.LineName(), &index);
4011 if (useStart || IsNameWithEndSuffix(aLine.LineName(), &index)) {
4012 auto side = MakeLogicalSide(
4013 GetAxis(aSide), useStart ? eLogicalEdgeStart : eLogicalEdgeEnd);
4014 RefPtr<nsAtom> name = NS_Atomize(nsDependentSubstring(
4015 nsDependentAtomString(aLine.LineName()), 0, index));
4016 aNameMap.FindNamedAreas(name, side, implicitLines);
4018 line = aNameMap.FindNamedLine(aLine.LineName(), &aNth, aFromIndex,
4019 implicitLines);
4022 if (line == 0) {
4023 MOZ_ASSERT(aNth != 0, "we found all N named lines but 'line' is zero!");
4024 int32_t edgeLine;
4025 if (aLine.is_span) {
4026 // http://dev.w3.org/csswg/css-grid/#grid-placement-span-int
4027 // 'span <custom-ident> N'
4028 edgeLine = IsStart(aSide) ? 1 : aExplicitGridEnd;
4029 } else {
4030 // http://dev.w3.org/csswg/css-grid/#grid-placement-int
4031 // '<custom-ident> N'
4032 edgeLine = aNth < 0 ? 1 : aExplicitGridEnd;
4034 // "If not enough lines with that name exist, all lines in the implicit
4035 // grid are assumed to have that name..."
4036 line = edgeLine + aNth;
4039 return clamped(line, aNameMap.mClampMinLine, aNameMap.mClampMaxLine);
4042 nsGridContainerFrame::Grid::LinePair
4043 nsGridContainerFrame::Grid::ResolveLineRangeHelper(
4044 const StyleGridLine& aStart, const StyleGridLine& aEnd,
4045 const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
4046 const nsStylePosition* aStyle) {
4047 MOZ_ASSERT(int32_t(kAutoLine) > kMaxLine);
4049 if (aStart.is_span) {
4050 if (aEnd.is_span || aEnd.IsAuto()) {
4051 // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
4052 if (aStart.LineName()->IsEmpty()) {
4053 // span <integer> / span *
4054 // span <integer> / auto
4055 return LinePair(kAutoLine, aStart.line_num);
4057 // span <custom-ident> / span *
4058 // span <custom-ident> / auto
4059 return LinePair(kAutoLine, 1); // XXX subgrid explicit size instead of 1?
4062 uint32_t from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0;
4063 auto end = ResolveLine(aEnd, aEnd.line_num, from, aNameMap,
4064 MakeLogicalSide(aAxis, eLogicalEdgeEnd),
4065 aExplicitGridEnd, aStyle);
4066 int32_t span = aStart.line_num == 0 ? 1 : aStart.line_num;
4067 if (end <= 1) {
4068 // The end is at or before the first explicit line, thus all lines before
4069 // it match <custom-ident> since they're implicit.
4070 int32_t start = std::max(end - span, aNameMap.mClampMinLine);
4071 return LinePair(start, end);
4073 auto start = ResolveLine(aStart, -span, end, aNameMap,
4074 MakeLogicalSide(aAxis, eLogicalEdgeStart),
4075 aExplicitGridEnd, aStyle);
4076 return LinePair(start, end);
4079 int32_t start = kAutoLine;
4080 if (aStart.IsAuto()) {
4081 if (aEnd.IsAuto()) {
4082 // auto / auto
4083 return LinePair(start, 1); // XXX subgrid explicit size instead of 1?
4085 if (aEnd.is_span) {
4086 if (aEnd.LineName()->IsEmpty()) {
4087 // auto / span <integer>
4088 MOZ_ASSERT(aEnd.line_num != 0);
4089 return LinePair(start, aEnd.line_num);
4091 // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
4092 // auto / span <custom-ident>
4093 return LinePair(start, 1); // XXX subgrid explicit size instead of 1?
4095 } else {
4096 uint32_t from = aStart.line_num < 0 ? aExplicitGridEnd + 1 : 0;
4097 start = ResolveLine(aStart, aStart.line_num, from, aNameMap,
4098 MakeLogicalSide(aAxis, eLogicalEdgeStart),
4099 aExplicitGridEnd, aStyle);
4100 if (aEnd.IsAuto()) {
4101 // A "definite line / auto" should resolve the auto to 'span 1'.
4102 // The error handling in ResolveLineRange will make that happen and also
4103 // clamp the end line correctly if we return "start / start".
4104 return LinePair(start, start);
4108 uint32_t from;
4109 int32_t nth = aEnd.line_num == 0 ? 1 : aEnd.line_num;
4110 if (aEnd.is_span) {
4111 if (MOZ_UNLIKELY(start < 0)) {
4112 if (aEnd.LineName()->IsEmpty()) {
4113 return LinePair(start, start + nth);
4115 from = 0;
4116 } else {
4117 if (start >= int32_t(aExplicitGridEnd)) {
4118 // The start is at or after the last explicit line, thus all lines
4119 // after it match <custom-ident> since they're implicit.
4120 return LinePair(start, std::min(start + nth, aNameMap.mClampMaxLine));
4122 from = start;
4124 } else {
4125 from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0;
4127 auto end = ResolveLine(aEnd, nth, from, aNameMap,
4128 MakeLogicalSide(aAxis, eLogicalEdgeEnd),
4129 aExplicitGridEnd, aStyle);
4130 if (start == int32_t(kAutoLine)) {
4131 // auto / definite line
4132 start = std::max(aNameMap.mClampMinLine, end - 1);
4134 return LinePair(start, end);
4137 nsGridContainerFrame::LineRange nsGridContainerFrame::Grid::ResolveLineRange(
4138 const StyleGridLine& aStart, const StyleGridLine& aEnd,
4139 const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
4140 const nsStylePosition* aStyle) {
4141 LinePair r = ResolveLineRangeHelper(aStart, aEnd, aNameMap, aAxis,
4142 aExplicitGridEnd, aStyle);
4143 MOZ_ASSERT(r.second != int32_t(kAutoLine));
4145 if (r.first == int32_t(kAutoLine)) {
4146 // r.second is a span, clamp it to aNameMap.mClampMaxLine - 1 so that
4147 // the returned range has a HypotheticalEnd <= aNameMap.mClampMaxLine.
4148 // http://dev.w3.org/csswg/css-grid/#overlarge-grids
4149 r.second = std::min(r.second, aNameMap.mClampMaxLine - 1);
4150 } else {
4151 // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
4152 if (r.first > r.second) {
4153 std::swap(r.first, r.second);
4154 } else if (r.first == r.second) {
4155 if (MOZ_UNLIKELY(r.first == aNameMap.mClampMaxLine)) {
4156 r.first = aNameMap.mClampMaxLine - 1;
4158 r.second = r.first + 1; // XXX subgrid explicit size instead of 1?
4161 return LineRange(r.first, r.second);
4164 nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceDefinite(
4165 nsIFrame* aChild, const LineNameMap& aColLineNameMap,
4166 const LineNameMap& aRowLineNameMap, const nsStylePosition* aStyle) {
4167 const nsStylePosition* itemStyle = aChild->StylePosition();
4168 return GridArea(
4169 ResolveLineRange(itemStyle->mGridColumnStart, itemStyle->mGridColumnEnd,
4170 aColLineNameMap, eLogicalAxisInline, mExplicitGridColEnd,
4171 aStyle),
4172 ResolveLineRange(itemStyle->mGridRowStart, itemStyle->mGridRowEnd,
4173 aRowLineNameMap, eLogicalAxisBlock, mExplicitGridRowEnd,
4174 aStyle));
4177 nsGridContainerFrame::LineRange
4178 nsGridContainerFrame::Grid::ResolveAbsPosLineRange(
4179 const StyleGridLine& aStart, const StyleGridLine& aEnd,
4180 const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
4181 int32_t aGridStart, int32_t aGridEnd, const nsStylePosition* aStyle) {
4182 if (aStart.IsAuto()) {
4183 if (aEnd.IsAuto()) {
4184 return LineRange(kAutoLine, kAutoLine);
4186 uint32_t from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0;
4187 int32_t end = ResolveLine(aEnd, aEnd.line_num, from, aNameMap,
4188 MakeLogicalSide(aAxis, eLogicalEdgeEnd),
4189 aExplicitGridEnd, aStyle);
4190 if (aEnd.is_span) {
4191 ++end;
4193 // A line outside the existing grid is treated as 'auto' for abs.pos (10.1).
4194 end = AutoIfOutside(end, aGridStart, aGridEnd);
4195 return LineRange(kAutoLine, end);
4198 if (aEnd.IsAuto()) {
4199 uint32_t from = aStart.line_num < 0 ? aExplicitGridEnd + 1 : 0;
4200 int32_t start = ResolveLine(aStart, aStart.line_num, from, aNameMap,
4201 MakeLogicalSide(aAxis, eLogicalEdgeStart),
4202 aExplicitGridEnd, aStyle);
4203 if (aStart.is_span) {
4204 start = std::max(aGridEnd - start, aGridStart);
4206 start = AutoIfOutside(start, aGridStart, aGridEnd);
4207 return LineRange(start, kAutoLine);
4210 LineRange r =
4211 ResolveLineRange(aStart, aEnd, aNameMap, aAxis, aExplicitGridEnd, aStyle);
4212 if (r.IsAuto()) {
4213 MOZ_ASSERT(aStart.is_span && aEnd.is_span,
4214 "span / span is the only case "
4215 "leading to IsAuto here -- we dealt with the other cases above");
4216 // The second span was ignored per 9.2.1. For abs.pos., 10.1 says that this
4217 // case should result in "auto / auto" unlike normal flow grid items.
4218 return LineRange(kAutoLine, kAutoLine);
4221 return LineRange(AutoIfOutside(r.mUntranslatedStart, aGridStart, aGridEnd),
4222 AutoIfOutside(r.mUntranslatedEnd, aGridStart, aGridEnd));
4225 nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceAbsPos(
4226 nsIFrame* aChild, const LineNameMap& aColLineNameMap,
4227 const LineNameMap& aRowLineNameMap, const nsStylePosition* aStyle) {
4228 const nsStylePosition* itemStyle = aChild->StylePosition();
4229 int32_t gridColStart = 1 - mExplicitGridOffsetCol;
4230 int32_t gridRowStart = 1 - mExplicitGridOffsetRow;
4231 return GridArea(ResolveAbsPosLineRange(
4232 itemStyle->mGridColumnStart, itemStyle->mGridColumnEnd,
4233 aColLineNameMap, eLogicalAxisInline, mExplicitGridColEnd,
4234 gridColStart, mGridColEnd, aStyle),
4235 ResolveAbsPosLineRange(
4236 itemStyle->mGridRowStart, itemStyle->mGridRowEnd,
4237 aRowLineNameMap, eLogicalAxisBlock, mExplicitGridRowEnd,
4238 gridRowStart, mGridRowEnd, aStyle));
4241 uint32_t nsGridContainerFrame::Grid::FindAutoCol(uint32_t aStartCol,
4242 uint32_t aLockedRow,
4243 const GridArea* aArea) const {
4244 const uint32_t extent = aArea->mCols.Extent();
4245 const uint32_t iStart = aLockedRow;
4246 const uint32_t iEnd = iStart + aArea->mRows.Extent();
4247 uint32_t candidate = aStartCol;
4248 for (uint32_t i = iStart; i < iEnd;) {
4249 if (i >= mCellMap.mCells.Length()) {
4250 break;
4252 const nsTArray<CellMap::Cell>& cellsInRow = mCellMap.mCells[i];
4253 const uint32_t len = cellsInRow.Length();
4254 const uint32_t lastCandidate = candidate;
4255 // Find the first gap in the current row that's at least 'extent' wide.
4256 // ('gap' tracks how wide the current column gap is.)
4257 for (uint32_t j = candidate, gap = 0; j < len && gap < extent; ++j) {
4258 if (!cellsInRow[j].mIsOccupied) {
4259 ++gap;
4260 continue;
4262 candidate = j + 1;
4263 gap = 0;
4265 if (lastCandidate < candidate && i != iStart) {
4266 // Couldn't fit 'extent' tracks at 'lastCandidate' here so we must
4267 // restart from the beginning with the new 'candidate'.
4268 i = iStart;
4269 } else {
4270 ++i;
4273 return candidate;
4276 void nsGridContainerFrame::Grid::PlaceAutoCol(uint32_t aStartCol,
4277 GridArea* aArea,
4278 uint32_t aClampMaxColLine) const {
4279 MOZ_ASSERT(aArea->mRows.IsDefinite() && aArea->mCols.IsAuto());
4280 uint32_t col = FindAutoCol(aStartCol, aArea->mRows.mStart, aArea);
4281 aArea->mCols.ResolveAutoPosition(col, aClampMaxColLine);
4282 MOZ_ASSERT(aArea->IsDefinite());
4285 uint32_t nsGridContainerFrame::Grid::FindAutoRow(uint32_t aLockedCol,
4286 uint32_t aStartRow,
4287 const GridArea* aArea) const {
4288 const uint32_t extent = aArea->mRows.Extent();
4289 const uint32_t jStart = aLockedCol;
4290 const uint32_t jEnd = jStart + aArea->mCols.Extent();
4291 const uint32_t iEnd = mCellMap.mCells.Length();
4292 uint32_t candidate = aStartRow;
4293 // Find the first gap in the rows that's at least 'extent' tall.
4294 // ('gap' tracks how tall the current row gap is.)
4295 for (uint32_t i = candidate, gap = 0; i < iEnd && gap < extent; ++i) {
4296 ++gap; // tentative, but we may reset it below if a column is occupied
4297 const nsTArray<CellMap::Cell>& cellsInRow = mCellMap.mCells[i];
4298 const uint32_t clampedJEnd = std::min<uint32_t>(jEnd, cellsInRow.Length());
4299 // Check if the current row is unoccupied from jStart to jEnd.
4300 for (uint32_t j = jStart; j < clampedJEnd; ++j) {
4301 if (cellsInRow[j].mIsOccupied) {
4302 // Couldn't fit 'extent' rows at 'candidate' here; we hit something
4303 // at row 'i'. So, try the row after 'i' as our next candidate.
4304 candidate = i + 1;
4305 gap = 0;
4306 break;
4310 return candidate;
4313 void nsGridContainerFrame::Grid::PlaceAutoRow(uint32_t aStartRow,
4314 GridArea* aArea,
4315 uint32_t aClampMaxRowLine) const {
4316 MOZ_ASSERT(aArea->mCols.IsDefinite() && aArea->mRows.IsAuto());
4317 uint32_t row = FindAutoRow(aArea->mCols.mStart, aStartRow, aArea);
4318 aArea->mRows.ResolveAutoPosition(row, aClampMaxRowLine);
4319 MOZ_ASSERT(aArea->IsDefinite());
4322 void nsGridContainerFrame::Grid::PlaceAutoAutoInRowOrder(
4323 uint32_t aStartCol, uint32_t aStartRow, GridArea* aArea,
4324 uint32_t aClampMaxColLine, uint32_t aClampMaxRowLine) const {
4325 MOZ_ASSERT(aArea->mCols.IsAuto() && aArea->mRows.IsAuto());
4326 const uint32_t colExtent = aArea->mCols.Extent();
4327 const uint32_t gridRowEnd = mGridRowEnd;
4328 const uint32_t gridColEnd = mGridColEnd;
4329 uint32_t col = aStartCol;
4330 uint32_t row = aStartRow;
4331 for (; row < gridRowEnd; ++row) {
4332 col = FindAutoCol(col, row, aArea);
4333 if (col + colExtent <= gridColEnd) {
4334 break;
4336 col = 0;
4338 MOZ_ASSERT(row < gridRowEnd || col == 0,
4339 "expected column 0 for placing in a new row");
4340 aArea->mCols.ResolveAutoPosition(col, aClampMaxColLine);
4341 aArea->mRows.ResolveAutoPosition(row, aClampMaxRowLine);
4342 MOZ_ASSERT(aArea->IsDefinite());
4345 void nsGridContainerFrame::Grid::PlaceAutoAutoInColOrder(
4346 uint32_t aStartCol, uint32_t aStartRow, GridArea* aArea,
4347 uint32_t aClampMaxColLine, uint32_t aClampMaxRowLine) const {
4348 MOZ_ASSERT(aArea->mCols.IsAuto() && aArea->mRows.IsAuto());
4349 const uint32_t rowExtent = aArea->mRows.Extent();
4350 const uint32_t gridRowEnd = mGridRowEnd;
4351 const uint32_t gridColEnd = mGridColEnd;
4352 uint32_t col = aStartCol;
4353 uint32_t row = aStartRow;
4354 for (; col < gridColEnd; ++col) {
4355 row = FindAutoRow(col, row, aArea);
4356 if (row + rowExtent <= gridRowEnd) {
4357 break;
4359 row = 0;
4361 MOZ_ASSERT(col < gridColEnd || row == 0,
4362 "expected row 0 for placing in a new column");
4363 aArea->mCols.ResolveAutoPosition(col, aClampMaxColLine);
4364 aArea->mRows.ResolveAutoPosition(row, aClampMaxRowLine);
4365 MOZ_ASSERT(aArea->IsDefinite());
4368 template <typename IsEmptyFuncT>
4369 Maybe<nsTArray<uint32_t>>
4370 nsGridContainerFrame::Grid::CalculateAdjustForAutoFitElements(
4371 uint32_t* const aOutNumEmptyLines, TrackSizingFunctions& aSizingFunctions,
4372 uint32_t aNumGridLines, IsEmptyFuncT aIsEmptyFunc) {
4373 Maybe<nsTArray<uint32_t>> trackAdjust;
4374 uint32_t& numEmptyLines = *aOutNumEmptyLines;
4375 numEmptyLines = 0;
4376 if (aSizingFunctions.NumRepeatTracks() > 0) {
4377 MOZ_ASSERT(aSizingFunctions.mHasRepeatAuto);
4378 // Since this loop is concerned with just the repeat tracks, we
4379 // iterate from 0..NumRepeatTracks() which is the natural range of
4380 // mRemoveRepeatTracks. This means we have to add
4381 // (mExplicitGridOffset + mRepeatAutoStart) to get a zero-based
4382 // index for arrays like mCellMap/aIsEmptyFunc and trackAdjust. We'll then
4383 // fill out the trackAdjust array for all the remaining lines.
4384 const uint32_t repeatStart = (aSizingFunctions.mExplicitGridOffset +
4385 aSizingFunctions.mRepeatAutoStart);
4386 const uint32_t numRepeats = aSizingFunctions.NumRepeatTracks();
4387 for (uint32_t i = 0; i < numRepeats; ++i) {
4388 if (numEmptyLines) {
4389 MOZ_ASSERT(trackAdjust.isSome());
4390 (*trackAdjust)[repeatStart + i] = numEmptyLines;
4392 if (aIsEmptyFunc(repeatStart + i)) {
4393 ++numEmptyLines;
4394 if (trackAdjust.isNothing()) {
4395 trackAdjust.emplace(aNumGridLines);
4396 trackAdjust->SetLength(aNumGridLines);
4397 PodZero(trackAdjust->Elements(), trackAdjust->Length());
4400 aSizingFunctions.mRemovedRepeatTracks[i] = true;
4403 // Fill out the trackAdjust array for all the tracks after the repeats.
4404 if (numEmptyLines) {
4405 for (uint32_t line = repeatStart + numRepeats; line < aNumGridLines;
4406 ++line) {
4407 (*trackAdjust)[line] = numEmptyLines;
4412 return trackAdjust;
4415 void nsGridContainerFrame::Grid::SubgridPlaceGridItems(
4416 GridReflowInput& aParentState, Grid* aParentGrid,
4417 const GridItemInfo& aGridItem) {
4418 MOZ_ASSERT(aGridItem.mArea.IsDefinite() ||
4419 aGridItem.mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
4420 "the subgrid's lines should be resolved by now");
4421 if (aGridItem.IsSubgrid(eLogicalAxisInline)) {
4422 aParentState.mFrame->AddStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM);
4424 if (aGridItem.IsSubgrid(eLogicalAxisBlock)) {
4425 aParentState.mFrame->AddStateBits(NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
4427 auto* childGrid = aGridItem.SubgridFrame();
4428 const auto* pos = childGrid->StylePosition();
4429 childGrid->NormalizeChildLists();
4430 GridReflowInput state(childGrid, aParentState.mRenderingContext);
4431 childGrid->InitImplicitNamedAreas(pos);
4433 const bool isOrthogonal = aParentState.mWM.IsOrthogonalTo(state.mWM);
4434 // Record the subgrid's GridArea in a frame property.
4435 auto* subgrid = childGrid->GetProperty(Subgrid::Prop());
4436 if (!subgrid) {
4437 subgrid = new Subgrid(aGridItem.mArea, isOrthogonal, aParentState.mWM);
4438 childGrid->SetProperty(Subgrid::Prop(), subgrid);
4439 } else {
4440 subgrid->mArea = aGridItem.mArea;
4441 subgrid->mIsOrthogonal = isOrthogonal;
4442 subgrid->mGridItems.Clear();
4443 subgrid->mAbsPosItems.Clear();
4446 // Abs.pos. subgrids may have kAutoLine in their area. Map those to the edge
4447 // line in the parent's grid (zero-based line numbers).
4448 if (MOZ_UNLIKELY(subgrid->mArea.mCols.mStart == kAutoLine)) {
4449 subgrid->mArea.mCols.mStart = 0;
4451 if (MOZ_UNLIKELY(subgrid->mArea.mCols.mEnd == kAutoLine)) {
4452 subgrid->mArea.mCols.mEnd = aParentGrid->mGridColEnd - 1;
4454 if (MOZ_UNLIKELY(subgrid->mArea.mRows.mStart == kAutoLine)) {
4455 subgrid->mArea.mRows.mStart = 0;
4457 if (MOZ_UNLIKELY(subgrid->mArea.mRows.mEnd == kAutoLine)) {
4458 subgrid->mArea.mRows.mEnd = aParentGrid->mGridRowEnd - 1;
4461 MOZ_ASSERT((subgrid->mArea.mCols.Extent() > 0 &&
4462 subgrid->mArea.mRows.Extent() > 0) ||
4463 state.mGridItems.IsEmpty(),
4464 "subgrid needs at least one track for its items");
4466 // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
4467 // https://drafts.csswg.org/css-grid/#auto-repeat
4468 // They're only used for auto-repeat in a non-subgridded axis so we skip
4469 // computing them otherwise.
4470 RepeatTrackSizingInput repeatSizing(state.mWM);
4471 if (!childGrid->IsColSubgrid() && state.mColFunctions.mHasRepeatAuto) {
4472 repeatSizing.InitFromStyle(eLogicalAxisInline, state.mWM,
4473 state.mFrame->Style());
4475 if (!childGrid->IsRowSubgrid() && state.mRowFunctions.mHasRepeatAuto) {
4476 repeatSizing.InitFromStyle(eLogicalAxisBlock, state.mWM,
4477 state.mFrame->Style());
4480 PlaceGridItems(state, repeatSizing);
4482 subgrid->mGridItems = std::move(state.mGridItems);
4483 subgrid->mAbsPosItems = std::move(state.mAbsPosItems);
4484 subgrid->mGridColEnd = mGridColEnd;
4485 subgrid->mGridRowEnd = mGridRowEnd;
4488 void nsGridContainerFrame::Grid::PlaceGridItems(
4489 GridReflowInput& aState, const RepeatTrackSizingInput& aSizes) {
4490 MOZ_ASSERT(mCellMap.mCells.IsEmpty(), "unexpected entries in cell map");
4492 mAreas = aState.mFrame->GetImplicitNamedAreas();
4494 if (aState.mFrame->HasSubgridItems() || aState.mFrame->IsSubgrid()) {
4495 if (auto* uts = aState.mFrame->GetUsedTrackSizes()) {
4496 uts->mCanResolveLineRangeSize = {false, false};
4497 uts->mSizes[eLogicalAxisInline].ClearAndRetainStorage();
4498 uts->mSizes[eLogicalAxisBlock].ClearAndRetainStorage();
4502 // SubgridPlaceGridItems will set these if we find any subgrid items.
4503 aState.mFrame->RemoveStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM |
4504 NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
4506 // http://dev.w3.org/csswg/css-grid/#grid-definition
4507 // Initialize the end lines of the Explicit Grid (mExplicitGridCol[Row]End).
4508 // This is determined by the larger of the number of rows/columns defined
4509 // by 'grid-template-areas' and the 'grid-template-rows'/'-columns', plus one.
4510 // Also initialize the Implicit Grid (mGridCol[Row]End) to the same values.
4511 // Note that this is for a grid with a 1,1 origin. We'll change that
4512 // to a 0,0 based grid after placing definite lines.
4513 const nsStylePosition* const gridStyle = aState.mGridStyle;
4514 const auto* areas = gridStyle->mGridTemplateAreas.IsNone()
4515 ? nullptr
4516 : &*gridStyle->mGridTemplateAreas.AsAreas();
4517 const LineNameMap* parentLineNameMap = nullptr;
4518 const LineRange* subgridRange = nullptr;
4519 bool subgridAxisIsSameDirection = true;
4520 if (!aState.mFrame->IsColSubgrid()) {
4521 aState.mColFunctions.InitRepeatTracks(
4522 gridStyle->mColumnGap, aSizes.mMin.ISize(aState.mWM),
4523 aSizes.mSize.ISize(aState.mWM), aSizes.mMax.ISize(aState.mWM));
4524 uint32_t areaCols = areas ? areas->width + 1 : 1;
4525 mExplicitGridColEnd = aState.mColFunctions.ComputeExplicitGridEnd(areaCols);
4526 } else {
4527 const auto* subgrid = aState.mFrame->GetProperty(Subgrid::Prop());
4528 subgridRange = &subgrid->SubgridCols();
4529 uint32_t extent = subgridRange->Extent();
4530 mExplicitGridColEnd = extent + 1; // the grid is 1-based at this point
4531 parentLineNameMap =
4532 ParentLineMapForAxis(subgrid->mIsOrthogonal, eLogicalAxisInline);
4533 auto parentWM =
4534 aState.mFrame->ParentGridContainerForSubgrid()->GetWritingMode();
4535 subgridAxisIsSameDirection =
4536 aState.mWM.ParallelAxisStartsOnSameSide(eLogicalAxisInline, parentWM);
4538 mGridColEnd = mExplicitGridColEnd;
4539 LineNameMap colLineNameMap(gridStyle, mAreas, aState.mColFunctions,
4540 parentLineNameMap, subgridRange,
4541 subgridAxisIsSameDirection);
4543 if (!aState.mFrame->IsRowSubgrid()) {
4544 aState.mRowFunctions.InitRepeatTracks(
4545 gridStyle->mRowGap, aSizes.mMin.BSize(aState.mWM),
4546 aSizes.mSize.BSize(aState.mWM), aSizes.mMax.BSize(aState.mWM));
4547 uint32_t areaRows = areas ? areas->strings.Length() + 1 : 1;
4548 mExplicitGridRowEnd = aState.mRowFunctions.ComputeExplicitGridEnd(areaRows);
4549 parentLineNameMap = nullptr;
4550 subgridRange = nullptr;
4551 } else {
4552 const auto* subgrid = aState.mFrame->GetProperty(Subgrid::Prop());
4553 subgridRange = &subgrid->SubgridRows();
4554 uint32_t extent = subgridRange->Extent();
4555 mExplicitGridRowEnd = extent + 1; // the grid is 1-based at this point
4556 parentLineNameMap =
4557 ParentLineMapForAxis(subgrid->mIsOrthogonal, eLogicalAxisBlock);
4558 auto parentWM =
4559 aState.mFrame->ParentGridContainerForSubgrid()->GetWritingMode();
4560 subgridAxisIsSameDirection =
4561 aState.mWM.ParallelAxisStartsOnSameSide(eLogicalAxisBlock, parentWM);
4563 mGridRowEnd = mExplicitGridRowEnd;
4564 LineNameMap rowLineNameMap(gridStyle, mAreas, aState.mRowFunctions,
4565 parentLineNameMap, subgridRange,
4566 subgridAxisIsSameDirection);
4568 const bool isSubgridOrItemInSubgrid =
4569 aState.mFrame->IsSubgrid() || !!mParentGrid;
4570 auto SetSubgridChildEdgeBits =
4571 [this, isSubgridOrItemInSubgrid](GridItemInfo& aItem) -> void {
4572 if (isSubgridOrItemInSubgrid) {
4573 const auto& area = aItem.mArea;
4574 if (area.mCols.mStart == 0) {
4575 aItem.mState[eLogicalAxisInline] |= ItemState::eStartEdge;
4577 if (area.mCols.mEnd == mGridColEnd) {
4578 aItem.mState[eLogicalAxisInline] |= ItemState::eEndEdge;
4580 if (area.mRows.mStart == 0) {
4581 aItem.mState[eLogicalAxisBlock] |= ItemState::eStartEdge;
4583 if (area.mRows.mEnd == mGridRowEnd) {
4584 aItem.mState[eLogicalAxisBlock] |= ItemState::eEndEdge;
4589 SetLineMaps(&colLineNameMap, &rowLineNameMap);
4591 // http://dev.w3.org/csswg/css-grid/#line-placement
4592 // Resolve definite positions per spec chap 9.2.
4593 int32_t minCol = 1;
4594 int32_t minRow = 1;
4595 aState.mGridItems.ClearAndRetainStorage();
4596 aState.mIter.Reset();
4597 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
4598 nsIFrame* child = *aState.mIter;
4599 GridItemInfo* info = aState.mGridItems.AppendElement(GridItemInfo(
4600 child,
4601 PlaceDefinite(child, colLineNameMap, rowLineNameMap, gridStyle)));
4602 MOZ_ASSERT(aState.mIter.ItemIndex() == aState.mGridItems.Length() - 1,
4603 "ItemIndex() is broken");
4604 GridArea& area = info->mArea;
4605 if (area.mCols.IsDefinite()) {
4606 minCol = std::min(minCol, area.mCols.mUntranslatedStart);
4608 if (area.mRows.IsDefinite()) {
4609 minRow = std::min(minRow, area.mRows.mUntranslatedStart);
4613 // Translate the whole grid so that the top-/left-most area is at 0,0.
4614 mExplicitGridOffsetCol = 1 - minCol; // minCol/Row is always <= 1, see above
4615 mExplicitGridOffsetRow = 1 - minRow;
4616 aState.mColFunctions.mExplicitGridOffset = mExplicitGridOffsetCol;
4617 aState.mRowFunctions.mExplicitGridOffset = mExplicitGridOffsetRow;
4618 const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
4619 const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
4620 const bool isRowMasonry = aState.mFrame->IsMasonry(eLogicalAxisBlock);
4621 const bool isColMasonry = aState.mFrame->IsMasonry(eLogicalAxisInline);
4622 const bool isMasonry = isColMasonry || isRowMasonry;
4623 mGridColEnd += offsetToColZero;
4624 mGridRowEnd += offsetToRowZero;
4625 const uint32_t gridAxisTrackCount = isRowMasonry ? mGridColEnd : mGridRowEnd;
4626 aState.mIter.Reset();
4627 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
4628 auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
4629 GridArea& area = item.mArea;
4630 if (area.mCols.IsDefinite()) {
4631 area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
4632 area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
4634 if (area.mRows.IsDefinite()) {
4635 area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
4636 area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
4638 if (area.IsDefinite()) {
4639 if (isMasonry) {
4640 item.MaybeInhibitSubgridInMasonry(aState.mFrame, gridAxisTrackCount);
4642 if (item.IsSubgrid()) {
4643 Grid grid(this);
4644 grid.SubgridPlaceGridItems(aState, this, item);
4646 mCellMap.Fill(area);
4647 InflateGridFor(area);
4648 SetSubgridChildEdgeBits(item);
4652 // http://dev.w3.org/csswg/css-grid/#auto-placement-algo
4653 // Step 1, place 'auto' items that have one definite position -
4654 // definite row (column) for grid-auto-flow:row (column).
4655 auto flowStyle = gridStyle->mGridAutoFlow;
4656 const bool isRowOrder =
4657 isMasonry ? isRowMasonry : !!(flowStyle & StyleGridAutoFlow::ROW);
4658 const bool isSparse = !(flowStyle & StyleGridAutoFlow::DENSE);
4659 uint32_t clampMaxColLine = colLineNameMap.mClampMaxLine + offsetToColZero;
4660 uint32_t clampMaxRowLine = rowLineNameMap.mClampMaxLine + offsetToRowZero;
4661 // We need 1 cursor per row (or column) if placement is sparse.
4663 Maybe<nsTHashMap<nsUint32HashKey, uint32_t>> cursors;
4664 if (isSparse) {
4665 cursors.emplace();
4667 auto placeAutoMinorFunc =
4668 isRowOrder ? &Grid::PlaceAutoCol : &Grid::PlaceAutoRow;
4669 uint32_t clampMaxLine = isRowOrder ? clampMaxColLine : clampMaxRowLine;
4670 aState.mIter.Reset();
4671 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
4672 auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
4673 GridArea& area = item.mArea;
4674 LineRange& major = isRowOrder ? area.mRows : area.mCols;
4675 LineRange& minor = isRowOrder ? area.mCols : area.mRows;
4676 if (major.IsDefinite() && minor.IsAuto()) {
4677 // Items with 'auto' in the minor dimension only.
4678 const uint32_t cursor = isSparse ? cursors->Get(major.mStart) : 0;
4679 (this->*placeAutoMinorFunc)(cursor, &area, clampMaxLine);
4680 if (isMasonry) {
4681 item.MaybeInhibitSubgridInMasonry(aState.mFrame, gridAxisTrackCount);
4683 if (item.IsSubgrid()) {
4684 Grid grid(this);
4685 grid.SubgridPlaceGridItems(aState, this, item);
4687 mCellMap.Fill(area);
4688 SetSubgridChildEdgeBits(item);
4689 if (isSparse) {
4690 cursors->InsertOrUpdate(major.mStart, minor.mEnd);
4693 InflateGridFor(area); // Step 2, inflating for auto items too
4697 // XXX NOTE possible spec issue.
4698 // XXX It's unclear if the remaining major-dimension auto and
4699 // XXX auto in both dimensions should use the same cursor or not,
4700 // XXX https://www.w3.org/Bugs/Public/show_bug.cgi?id=16044
4701 // XXX seems to indicate it shouldn't.
4702 // XXX http://dev.w3.org/csswg/css-grid/#auto-placement-cursor
4703 // XXX now says it should (but didn't in earlier versions)
4705 // Step 3, place the remaining grid items
4706 uint32_t cursorMajor = 0; // for 'dense' these two cursors will stay at 0,0
4707 uint32_t cursorMinor = 0;
4708 auto placeAutoMajorFunc =
4709 isRowOrder ? &Grid::PlaceAutoRow : &Grid::PlaceAutoCol;
4710 uint32_t clampMaxMajorLine = isRowOrder ? clampMaxRowLine : clampMaxColLine;
4711 aState.mIter.Reset();
4712 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
4713 auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
4714 GridArea& area = item.mArea;
4715 MOZ_ASSERT(*aState.mIter == item.mFrame,
4716 "iterator out of sync with aState.mGridItems");
4717 LineRange& major = isRowOrder ? area.mRows : area.mCols;
4718 LineRange& minor = isRowOrder ? area.mCols : area.mRows;
4719 if (major.IsAuto()) {
4720 if (minor.IsDefinite()) {
4721 // Items with 'auto' in the major dimension only.
4722 if (isSparse) {
4723 if (minor.mStart < cursorMinor) {
4724 ++cursorMajor;
4726 cursorMinor = minor.mStart;
4728 (this->*placeAutoMajorFunc)(cursorMajor, &area, clampMaxMajorLine);
4729 if (isSparse) {
4730 cursorMajor = major.mStart;
4732 } else {
4733 // Items with 'auto' in both dimensions.
4734 if (isRowOrder) {
4735 PlaceAutoAutoInRowOrder(cursorMinor, cursorMajor, &area,
4736 clampMaxColLine, clampMaxRowLine);
4737 } else {
4738 PlaceAutoAutoInColOrder(cursorMajor, cursorMinor, &area,
4739 clampMaxColLine, clampMaxRowLine);
4741 if (isSparse) {
4742 cursorMajor = major.mStart;
4743 cursorMinor = minor.mEnd;
4744 #ifdef DEBUG
4745 uint32_t gridMajorEnd = isRowOrder ? mGridRowEnd : mGridColEnd;
4746 uint32_t gridMinorEnd = isRowOrder ? mGridColEnd : mGridRowEnd;
4747 MOZ_ASSERT(cursorMajor <= gridMajorEnd,
4748 "we shouldn't need to place items further than 1 track "
4749 "past the current end of the grid, in major dimension");
4750 MOZ_ASSERT(cursorMinor <= gridMinorEnd,
4751 "we shouldn't add implicit minor tracks for auto/auto");
4752 #endif
4755 if (isMasonry) {
4756 item.MaybeInhibitSubgridInMasonry(aState.mFrame, gridAxisTrackCount);
4758 if (item.IsSubgrid()) {
4759 Grid grid(this);
4760 grid.SubgridPlaceGridItems(aState, this, item);
4762 mCellMap.Fill(area);
4763 InflateGridFor(area);
4764 SetSubgridChildEdgeBits(item);
4765 // XXXmats it might be possible to optimize this a bit for masonry layout
4766 // if this item was placed in the 2nd row && !isSparse, or the 1st row
4767 // is full. Still gotta inflate the grid for all items though to make
4768 // the grid large enough...
4772 // Force all items into the 1st/2nd track and have span 1 in the masonry axis.
4773 // (See comment on nsGridContainerFrame::MasonryLayout().)
4774 if (isMasonry) {
4775 auto masonryAxis = isRowMasonry ? eLogicalAxisBlock : eLogicalAxisInline;
4776 aState.mIter.Reset();
4777 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
4778 auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
4779 auto& masonryRange = item.mArea.LineRangeForAxis(masonryAxis);
4780 masonryRange.mStart = std::min(masonryRange.mStart, 1U);
4781 masonryRange.mEnd = masonryRange.mStart + 1U;
4785 if (aState.mFrame->IsAbsoluteContainer()) {
4786 // 9.4 Absolutely-positioned Grid Items
4787 // http://dev.w3.org/csswg/css-grid/#abspos-items
4788 // We only resolve definite lines here; we'll align auto positions to the
4789 // grid container later during reflow.
4790 nsFrameList children(
4791 aState.mFrame->GetChildList(aState.mFrame->GetAbsoluteListID()));
4792 const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
4793 const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
4794 // Untranslate the grid again temporarily while resolving abs.pos. lines.
4795 AutoRestore<uint32_t> zeroOffsetGridColEnd(mGridColEnd);
4796 AutoRestore<uint32_t> zeroOffsetGridRowEnd(mGridRowEnd);
4797 mGridColEnd -= offsetToColZero;
4798 mGridRowEnd -= offsetToRowZero;
4799 aState.mAbsPosItems.ClearAndRetainStorage();
4800 size_t i = 0;
4801 for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next(), ++i) {
4802 nsIFrame* child = e.get();
4803 GridItemInfo* info = aState.mAbsPosItems.AppendElement(GridItemInfo(
4804 child,
4805 PlaceAbsPos(child, colLineNameMap, rowLineNameMap, gridStyle)));
4806 GridArea& area = info->mArea;
4807 if (area.mCols.mUntranslatedStart != int32_t(kAutoLine)) {
4808 area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
4809 if (isColMasonry) {
4810 // XXXmats clamp any non-auto line to 0 or 1. This is intended to
4811 // allow authors to address the start/end of the masonry box.
4812 // This is experimental at this point though and needs author feedback
4813 // and spec work to sort out what is desired and how it should work.
4814 // See https://github.com/w3c/csswg-drafts/issues/4650
4815 area.mCols.mStart = std::min(area.mCols.mStart, 1U);
4818 if (area.mCols.mUntranslatedEnd != int32_t(kAutoLine)) {
4819 area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
4820 if (isColMasonry) {
4821 // ditto
4822 area.mCols.mEnd = std::min(area.mCols.mEnd, 1U);
4825 if (area.mRows.mUntranslatedStart != int32_t(kAutoLine)) {
4826 area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
4827 if (isRowMasonry) {
4828 // ditto
4829 area.mRows.mStart = std::min(area.mRows.mStart, 1U);
4832 if (area.mRows.mUntranslatedEnd != int32_t(kAutoLine)) {
4833 area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
4834 if (isRowMasonry) {
4835 // ditto
4836 area.mRows.mEnd = std::min(area.mRows.mEnd, 1U);
4839 if (isMasonry) {
4840 info->MaybeInhibitSubgridInMasonry(aState.mFrame, gridAxisTrackCount);
4843 // An abs.pos. subgrid with placement auto/1 or -1/auto technically
4844 // doesn't span any parent tracks. Inhibit subgridding in this case.
4845 if (info->IsSubgrid(eLogicalAxisInline)) {
4846 if (info->mArea.mCols.mStart == zeroOffsetGridColEnd.SavedValue() ||
4847 info->mArea.mCols.mEnd == 0) {
4848 info->InhibitSubgrid(aState.mFrame, eLogicalAxisInline);
4851 if (info->IsSubgrid(eLogicalAxisBlock)) {
4852 if (info->mArea.mRows.mStart == zeroOffsetGridRowEnd.SavedValue() ||
4853 info->mArea.mRows.mEnd == 0) {
4854 info->InhibitSubgrid(aState.mFrame, eLogicalAxisBlock);
4858 if (info->IsSubgrid()) {
4859 Grid grid(this);
4860 grid.SubgridPlaceGridItems(aState, this, *info);
4865 // Count empty 'auto-fit' tracks in the repeat() range.
4866 // |colAdjust| will have a count for each line in the grid of how many
4867 // tracks were empty between the start of the grid and that line.
4869 Maybe<nsTArray<uint32_t>> colAdjust;
4870 uint32_t numEmptyCols = 0;
4871 if (aState.mColFunctions.mHasRepeatAuto &&
4872 gridStyle->mGridTemplateColumns.GetRepeatAutoValue()->count.IsAutoFit()) {
4873 const auto& cellMap = mCellMap;
4874 colAdjust = CalculateAdjustForAutoFitElements(
4875 &numEmptyCols, aState.mColFunctions, mGridColEnd + 1,
4876 [&cellMap](uint32_t i) -> bool { return cellMap.IsEmptyCol(i); });
4879 // Do similar work for the row tracks, with the same logic.
4880 Maybe<nsTArray<uint32_t>> rowAdjust;
4881 uint32_t numEmptyRows = 0;
4882 if (aState.mRowFunctions.mHasRepeatAuto &&
4883 gridStyle->mGridTemplateRows.GetRepeatAutoValue()->count.IsAutoFit()) {
4884 const auto& cellMap = mCellMap;
4885 rowAdjust = CalculateAdjustForAutoFitElements(
4886 &numEmptyRows, aState.mRowFunctions, mGridRowEnd + 1,
4887 [&cellMap](uint32_t i) -> bool { return cellMap.IsEmptyRow(i); });
4889 MOZ_ASSERT((numEmptyCols > 0) == colAdjust.isSome());
4890 MOZ_ASSERT((numEmptyRows > 0) == rowAdjust.isSome());
4891 // Remove the empty 'auto-fit' tracks we found above, if any.
4892 if (numEmptyCols || numEmptyRows) {
4893 // Adjust the line numbers in the grid areas.
4894 for (auto& item : aState.mGridItems) {
4895 if (numEmptyCols) {
4896 item.AdjustForRemovedTracks(eLogicalAxisInline, *colAdjust);
4898 if (numEmptyRows) {
4899 item.AdjustForRemovedTracks(eLogicalAxisBlock, *rowAdjust);
4902 for (auto& item : aState.mAbsPosItems) {
4903 if (numEmptyCols) {
4904 item.AdjustForRemovedTracks(eLogicalAxisInline, *colAdjust);
4906 if (numEmptyRows) {
4907 item.AdjustForRemovedTracks(eLogicalAxisBlock, *rowAdjust);
4910 // Adjust the grid size.
4911 mGridColEnd -= numEmptyCols;
4912 mExplicitGridColEnd -= numEmptyCols;
4913 mGridRowEnd -= numEmptyRows;
4914 mExplicitGridRowEnd -= numEmptyRows;
4915 // Adjust the track mapping to unmap the removed tracks.
4916 auto colRepeatCount = aState.mColFunctions.NumRepeatTracks();
4917 aState.mColFunctions.SetNumRepeatTracks(colRepeatCount - numEmptyCols);
4918 auto rowRepeatCount = aState.mRowFunctions.NumRepeatTracks();
4919 aState.mRowFunctions.SetNumRepeatTracks(rowRepeatCount - numEmptyRows);
4922 // Update the line boundaries of the implicit grid areas, if needed.
4923 if (mAreas && aState.mFrame->ShouldGenerateComputedInfo()) {
4924 for (auto iter = mAreas->iter(); !iter.done(); iter.next()) {
4925 auto& areaInfo = iter.get().value();
4927 // Resolve the lines for the area. We use the name of the area as the
4928 // name of the lines, knowing that the line placement algorithm will
4929 // add the -start and -end suffixes as appropriate for layout.
4930 StyleGridLine lineStartAndEnd;
4931 lineStartAndEnd.ident = areaInfo.name;
4933 LineRange columnLines =
4934 ResolveLineRange(lineStartAndEnd, lineStartAndEnd, colLineNameMap,
4935 eLogicalAxisInline, mExplicitGridColEnd, gridStyle);
4937 LineRange rowLines =
4938 ResolveLineRange(lineStartAndEnd, lineStartAndEnd, rowLineNameMap,
4939 eLogicalAxisBlock, mExplicitGridRowEnd, gridStyle);
4941 // Put the resolved line indices back into the area structure.
4942 areaInfo.columns.start = columnLines.mStart + mExplicitGridOffsetCol;
4943 areaInfo.columns.end = columnLines.mEnd + mExplicitGridOffsetCol;
4944 areaInfo.rows.start = rowLines.mStart + mExplicitGridOffsetRow;
4945 areaInfo.rows.end = rowLines.mEnd + mExplicitGridOffsetRow;
4950 void nsGridContainerFrame::Tracks::Initialize(
4951 const TrackSizingFunctions& aFunctions,
4952 const NonNegativeLengthPercentageOrNormal& aGridGap, uint32_t aNumTracks,
4953 nscoord aContentBoxSize) {
4954 mSizes.SetLength(aNumTracks);
4955 PodZero(mSizes.Elements(), mSizes.Length());
4956 for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
4957 auto& sz = mSizes[i];
4958 mStateUnion |= sz.Initialize(aContentBoxSize, aFunctions.SizingFor(i));
4959 if (mIsMasonry) {
4960 sz.mBase = aContentBoxSize;
4961 sz.mLimit = aContentBoxSize;
4964 mGridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aContentBoxSize);
4965 mContentBoxSize = aContentBoxSize;
4969 * Reflow aChild in the given aAvailableSize.
4971 static nscoord MeasuringReflow(nsIFrame* aChild,
4972 const ReflowInput* aReflowInput, gfxContext* aRC,
4973 const LogicalSize& aAvailableSize,
4974 const LogicalSize& aCBSize,
4975 nscoord aIMinSizeClamp = NS_MAXSIZE,
4976 nscoord aBMinSizeClamp = NS_MAXSIZE) {
4977 nsContainerFrame* parent = aChild->GetParent();
4978 nsPresContext* pc = aChild->PresContext();
4979 Maybe<ReflowInput> dummyParentState;
4980 const ReflowInput* rs = aReflowInput;
4981 if (!aReflowInput) {
4982 MOZ_ASSERT(!parent->HasAnyStateBits(NS_FRAME_IN_REFLOW));
4983 dummyParentState.emplace(
4984 pc, parent, aRC,
4985 LogicalSize(parent->GetWritingMode(), 0, NS_UNCONSTRAINEDSIZE),
4986 ReflowInput::InitFlag::DummyParentReflowInput);
4987 rs = dummyParentState.ptr();
4989 #ifdef DEBUG
4990 // This will suppress various ABSURD_SIZE warnings for this reflow.
4991 parent->SetProperty(nsContainerFrame::DebugReflowingWithInfiniteISize(),
4992 true);
4993 #endif
4994 auto wm = aChild->GetWritingMode();
4995 ComputeSizeFlags csFlags = ComputeSizeFlag::UseAutoBSize;
4996 if (aAvailableSize.ISize(wm) == INFINITE_ISIZE_COORD) {
4997 csFlags += ComputeSizeFlag::ShrinkWrap;
4999 if (aIMinSizeClamp != NS_MAXSIZE) {
5000 csFlags += ComputeSizeFlag::IClampMarginBoxMinSize;
5002 if (aBMinSizeClamp != NS_MAXSIZE) {
5003 csFlags += ComputeSizeFlag::BClampMarginBoxMinSize;
5004 aChild->SetProperty(nsIFrame::BClampMarginBoxMinSizeProperty(),
5005 aBMinSizeClamp);
5006 } else {
5007 aChild->RemoveProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
5009 ReflowInput childRI(pc, *rs, aChild, aAvailableSize, Some(aCBSize), {}, {},
5010 csFlags);
5012 // Because we pass ComputeSizeFlag::UseAutoBSize, and the
5013 // previous reflow of the child might not have, set the child's
5014 // block-resize flag to true.
5015 // FIXME (perf): It would be faster to do this only if the previous
5016 // reflow of the child was not a measuring reflow, and only if the
5017 // child does some of the things that are affected by
5018 // ComputeSizeFlag::UseAutoBSize.
5019 childRI.SetBResize(true);
5020 // Not 100% sure this is needed, but be conservative for now:
5021 childRI.mFlags.mIsBResizeForPercentages = true;
5023 ReflowOutput childSize(childRI);
5024 nsReflowStatus childStatus;
5025 const nsIFrame::ReflowChildFlags flags =
5026 nsIFrame::ReflowChildFlags::NoMoveFrame |
5027 nsIFrame::ReflowChildFlags::NoSizeView |
5028 nsIFrame::ReflowChildFlags::NoDeleteNextInFlowChild;
5030 if (StaticPrefs::layout_css_grid_item_baxis_measurement_enabled()) {
5031 bool found;
5032 GridItemCachedBAxisMeasurement cachedMeasurement =
5033 aChild->GetProperty(GridItemCachedBAxisMeasurement::Prop(), &found);
5034 if (found && cachedMeasurement.IsValidFor(aChild, aCBSize)) {
5035 childSize.BSize(wm) = cachedMeasurement.BSize();
5036 childSize.ISize(wm) = aChild->ISize(wm);
5037 nsContainerFrame::FinishReflowChild(aChild, pc, childSize, &childRI, wm,
5038 LogicalPoint(wm), nsSize(), flags);
5039 GRID_LOG(
5040 "[perf] MeasuringReflow accepted cached value=%d, child=%p, "
5041 "aCBSize.ISize=%d",
5042 cachedMeasurement.BSize(), aChild,
5043 aCBSize.ISize(aChild->GetWritingMode()));
5044 return cachedMeasurement.BSize();
5048 parent->ReflowChild(aChild, pc, childSize, childRI, wm, LogicalPoint(wm),
5049 nsSize(), flags, childStatus);
5050 nsContainerFrame::FinishReflowChild(aChild, pc, childSize, &childRI, wm,
5051 LogicalPoint(wm), nsSize(), flags);
5052 #ifdef DEBUG
5053 parent->RemoveProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
5054 #endif
5056 if (StaticPrefs::layout_css_grid_item_baxis_measurement_enabled()) {
5057 bool found;
5058 GridItemCachedBAxisMeasurement cachedMeasurement =
5059 aChild->GetProperty(GridItemCachedBAxisMeasurement::Prop(), &found);
5060 if (!found &&
5061 GridItemCachedBAxisMeasurement::CanCacheMeasurement(aChild, aCBSize)) {
5062 GridItemCachedBAxisMeasurement cachedMeasurement(aChild, aCBSize,
5063 childSize.BSize(wm));
5064 aChild->SetProperty(GridItemCachedBAxisMeasurement::Prop(),
5065 cachedMeasurement);
5066 GRID_LOG(
5067 "[perf] MeasuringReflow created new cached value=%d, child=%p, "
5068 "aCBSize.ISize=%d",
5069 cachedMeasurement.BSize(), aChild,
5070 aCBSize.ISize(aChild->GetWritingMode()));
5071 } else if (found) {
5072 if (GridItemCachedBAxisMeasurement::CanCacheMeasurement(aChild,
5073 aCBSize)) {
5074 cachedMeasurement.Update(aChild, aCBSize, childSize.BSize(wm));
5075 GRID_LOG(
5076 "[perf] MeasuringReflow rejected but updated cached value=%d, "
5077 "child=%p, aCBSize.ISize=%d",
5078 cachedMeasurement.BSize(), aChild,
5079 aCBSize.ISize(aChild->GetWritingMode()));
5080 aChild->SetProperty(GridItemCachedBAxisMeasurement::Prop(),
5081 cachedMeasurement);
5082 } else {
5083 aChild->RemoveProperty(GridItemCachedBAxisMeasurement::Prop());
5084 GRID_LOG(
5085 "[perf] MeasuringReflow rejected and removed cached value, "
5086 "child=%p",
5087 aChild);
5091 return childSize.BSize(wm);
5095 * Reflow aChild in the given aAvailableSize, using aNewContentBoxSize as its
5096 * computed size in aChildAxis.
5098 static void PostReflowStretchChild(
5099 nsIFrame* aChild, const ReflowInput& aReflowInput,
5100 const LogicalSize& aAvailableSize, const LogicalSize& aCBSize,
5101 LogicalAxis aChildAxis, const nscoord aNewContentBoxSize,
5102 nscoord aIMinSizeClamp = NS_MAXSIZE, nscoord aBMinSizeClamp = NS_MAXSIZE) {
5103 nsPresContext* pc = aChild->PresContext();
5104 ComputeSizeFlags csFlags;
5105 if (aIMinSizeClamp != NS_MAXSIZE) {
5106 csFlags += ComputeSizeFlag::IClampMarginBoxMinSize;
5108 if (aBMinSizeClamp != NS_MAXSIZE) {
5109 csFlags += ComputeSizeFlag::BClampMarginBoxMinSize;
5110 aChild->SetProperty(nsIFrame::BClampMarginBoxMinSizeProperty(),
5111 aBMinSizeClamp);
5112 } else {
5113 aChild->RemoveProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
5115 ReflowInput ri(pc, aReflowInput, aChild, aAvailableSize, Some(aCBSize), {},
5116 {}, csFlags);
5117 if (aChildAxis == eLogicalAxisBlock) {
5118 ri.SetComputedBSize(ri.ApplyMinMaxBSize(aNewContentBoxSize));
5119 } else {
5120 ri.SetComputedISize(ri.ApplyMinMaxISize(aNewContentBoxSize));
5122 ReflowOutput childSize(ri);
5123 nsReflowStatus childStatus;
5124 const nsIFrame::ReflowChildFlags flags =
5125 nsIFrame::ReflowChildFlags::NoMoveFrame |
5126 nsIFrame::ReflowChildFlags::NoDeleteNextInFlowChild;
5127 auto wm = aChild->GetWritingMode();
5128 nsContainerFrame* parent = aChild->GetParent();
5129 parent->ReflowChild(aChild, pc, childSize, ri, wm, LogicalPoint(wm), nsSize(),
5130 flags, childStatus);
5131 nsContainerFrame::FinishReflowChild(aChild, pc, childSize, &ri, wm,
5132 LogicalPoint(wm), nsSize(), flags);
5136 * Return the accumulated margin+border+padding in aAxis for aFrame (a subgrid)
5137 * and its ancestor subgrids.
5139 static LogicalMargin SubgridAccumulatedMarginBorderPadding(
5140 nsIFrame* aFrame, const Subgrid* aSubgrid, WritingMode aResultWM,
5141 LogicalAxis aAxis) {
5142 MOZ_ASSERT(aFrame->IsGridContainerFrame());
5143 auto* subgridFrame = static_cast<nsGridContainerFrame*>(aFrame);
5144 LogicalMargin result(aSubgrid->mMarginBorderPadding);
5145 auto* parent = subgridFrame->ParentGridContainerForSubgrid();
5146 auto subgridCBWM = parent->GetWritingMode();
5147 auto childRange = aSubgrid->mArea.LineRangeForAxis(aAxis);
5148 bool skipStartSide = false;
5149 bool skipEndSide = false;
5150 auto axis = aSubgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
5151 // If aFrame's parent is also a subgrid, then add its MBP on the edges that
5152 // are adjacent (i.e. start or end in the same track), recursively.
5153 // ("parent" refers to the grid-frame we're currently adding MBP for,
5154 // and "grandParent" its parent, as we walk up the chain.)
5155 while (parent->IsSubgrid(axis)) {
5156 auto* parentSubgrid = parent->GetProperty(Subgrid::Prop());
5157 auto* grandParent = parent->ParentGridContainerForSubgrid();
5158 auto parentCBWM = grandParent->GetWritingMode();
5159 if (parentCBWM.IsOrthogonalTo(subgridCBWM)) {
5160 axis = GetOrthogonalAxis(axis);
5162 const auto& parentRange = parentSubgrid->mArea.LineRangeForAxis(axis);
5163 bool sameDir = parentCBWM.ParallelAxisStartsOnSameSide(axis, subgridCBWM);
5164 if (sameDir) {
5165 skipStartSide |= childRange.mStart != 0;
5166 skipEndSide |= childRange.mEnd != parentRange.Extent();
5167 } else {
5168 skipEndSide |= childRange.mStart != 0;
5169 skipStartSide |= childRange.mEnd != parentRange.Extent();
5171 if (skipStartSide && skipEndSide) {
5172 break;
5174 auto mbp =
5175 parentSubgrid->mMarginBorderPadding.ConvertTo(subgridCBWM, parentCBWM);
5176 if (skipStartSide) {
5177 mbp.Start(aAxis, subgridCBWM) = nscoord(0);
5179 if (skipEndSide) {
5180 mbp.End(aAxis, subgridCBWM) = nscoord(0);
5182 result += mbp;
5183 parent = grandParent;
5184 childRange = parentRange;
5186 return result.ConvertTo(aResultWM, subgridCBWM);
5190 * Return the [min|max]-content contribution of aChild to its parent (i.e.
5191 * the child's margin-box) in aAxis.
5193 static nscoord ContentContribution(
5194 const GridItemInfo& aGridItem, const GridReflowInput& aState,
5195 gfxContext* aRC, WritingMode aCBWM, LogicalAxis aAxis,
5196 const Maybe<LogicalSize>& aPercentageBasis, IntrinsicISizeType aConstraint,
5197 nscoord aMinSizeClamp = NS_MAXSIZE, uint32_t aFlags = 0) {
5198 nsIFrame* child = aGridItem.mFrame;
5200 nscoord extraMargin = 0;
5201 nsGridContainerFrame::Subgrid* subgrid = nullptr;
5202 if (child->GetParent() != aState.mFrame) {
5203 // |child| is a subgrid descendant, so it contributes its subgrids'
5204 // margin+border+padding for any edge tracks that it spans.
5205 auto* subgridFrame = child->GetParent();
5206 subgrid = subgridFrame->GetProperty(Subgrid::Prop());
5207 const auto itemEdgeBits = aGridItem.mState[aAxis] & ItemState::eEdgeBits;
5208 if (itemEdgeBits) {
5209 LogicalMargin mbp = SubgridAccumulatedMarginBorderPadding(
5210 subgridFrame, subgrid, aCBWM, aAxis);
5211 if (itemEdgeBits & ItemState::eStartEdge) {
5212 extraMargin += mbp.Start(aAxis, aCBWM);
5214 if (itemEdgeBits & ItemState::eEndEdge) {
5215 extraMargin += mbp.End(aAxis, aCBWM);
5218 // It also contributes (half of) the subgrid's gap on its edges (if any)
5219 // subtracted by the non-subgrid ancestor grid container's gap.
5220 // Note that this can also be negative since it's considered a margin.
5221 if (itemEdgeBits != ItemState::eEdgeBits) {
5222 auto subgridAxis = aCBWM.IsOrthogonalTo(subgridFrame->GetWritingMode())
5223 ? GetOrthogonalAxis(aAxis)
5224 : aAxis;
5225 auto& gapStyle = subgridAxis == eLogicalAxisBlock
5226 ? subgridFrame->StylePosition()->mRowGap
5227 : subgridFrame->StylePosition()->mColumnGap;
5228 if (!gapStyle.IsNormal()) {
5229 auto subgridExtent = subgridAxis == eLogicalAxisBlock
5230 ? subgrid->mGridRowEnd
5231 : subgrid->mGridColEnd;
5232 if (subgridExtent > 1) {
5233 nscoord subgridGap =
5234 nsLayoutUtils::ResolveGapToLength(gapStyle, NS_UNCONSTRAINEDSIZE);
5235 auto& tracks =
5236 aAxis == eLogicalAxisBlock ? aState.mRows : aState.mCols;
5237 auto gapDelta = subgridGap - tracks.mGridGap;
5238 if (!itemEdgeBits) {
5239 extraMargin += gapDelta;
5240 } else {
5241 extraMargin += gapDelta / 2;
5248 PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
5249 nscoord size = nsLayoutUtils::IntrinsicForAxis(
5250 axis, aRC, child, aConstraint, aPercentageBasis,
5251 aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED, aMinSizeClamp);
5252 auto childWM = child->GetWritingMode();
5253 const bool isOrthogonal = childWM.IsOrthogonalTo(aCBWM);
5254 auto childAxis = isOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
5255 if (size == NS_INTRINSIC_ISIZE_UNKNOWN && childAxis == eLogicalAxisBlock) {
5256 // We need to reflow the child to find its BSize contribution.
5257 // XXX this will give mostly correct results for now (until bug 1174569).
5258 nscoord availISize = INFINITE_ISIZE_COORD;
5259 nscoord availBSize = NS_UNCONSTRAINEDSIZE;
5260 // The next two variables are MinSizeClamp values in the child's axes.
5261 nscoord iMinSizeClamp = NS_MAXSIZE;
5262 nscoord bMinSizeClamp = NS_MAXSIZE;
5263 LogicalSize cbSize(childWM, 0, NS_UNCONSTRAINEDSIZE);
5264 // Below, we try to resolve the child's grid-area size in its inline-axis
5265 // to use as the CB/Available size in the MeasuringReflow that follows.
5266 if (child->GetParent() != aState.mFrame) {
5267 // This item is a child of a subgrid descendant.
5268 auto* subgridFrame =
5269 static_cast<nsGridContainerFrame*>(child->GetParent());
5270 MOZ_ASSERT(subgridFrame->IsGridContainerFrame());
5271 auto* uts = subgridFrame->GetProperty(UsedTrackSizes::Prop());
5272 if (!uts) {
5273 uts = new UsedTrackSizes();
5274 subgridFrame->SetProperty(UsedTrackSizes::Prop(), uts);
5276 // The grid-item's inline-axis as expressed in the subgrid's WM.
5277 auto subgridAxis = childWM.IsOrthogonalTo(subgridFrame->GetWritingMode())
5278 ? eLogicalAxisBlock
5279 : eLogicalAxisInline;
5280 uts->ResolveTrackSizesForAxis(subgridFrame, subgridAxis, *aRC);
5281 if (uts->mCanResolveLineRangeSize[subgridAxis]) {
5282 auto* subgrid =
5283 subgridFrame->GetProperty(nsGridContainerFrame::Subgrid::Prop());
5284 const GridItemInfo* originalItem = nullptr;
5285 for (const auto& item : subgrid->mGridItems) {
5286 if (item.mFrame == child) {
5287 originalItem = &item;
5288 break;
5291 MOZ_ASSERT(originalItem, "huh?");
5292 const auto& range = originalItem->mArea.LineRangeForAxis(subgridAxis);
5293 nscoord pos, sz;
5294 range.ToPositionAndLength(uts->mSizes[subgridAxis], &pos, &sz);
5295 if (childWM.IsOrthogonalTo(subgridFrame->GetWritingMode())) {
5296 availBSize = sz;
5297 cbSize.BSize(childWM) = sz;
5298 if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
5299 bMinSizeClamp = sz;
5301 } else {
5302 availISize = sz;
5303 cbSize.ISize(childWM) = sz;
5304 if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
5305 iMinSizeClamp = sz;
5309 } else if (aState.mCols.mCanResolveLineRangeSize) {
5310 nscoord sz = aState.mCols.ResolveSize(aGridItem.mArea.mCols);
5311 if (isOrthogonal) {
5312 availBSize = sz;
5313 cbSize.BSize(childWM) = sz;
5314 if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
5315 bMinSizeClamp = sz;
5317 } else {
5318 availISize = sz;
5319 cbSize.ISize(childWM) = sz;
5320 if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
5321 iMinSizeClamp = sz;
5325 if (isOrthogonal == (aAxis == eLogicalAxisInline)) {
5326 bMinSizeClamp = aMinSizeClamp;
5327 } else {
5328 iMinSizeClamp = aMinSizeClamp;
5330 LogicalSize availableSize(childWM, availISize, availBSize);
5331 if (MOZ_UNLIKELY(child->IsXULBoxFrame())) {
5332 auto* pc = child->PresContext();
5333 // For XUL-in-CSS-Grid (e.g. in our frontend code), we defer to XUL's
5334 // GetPrefSize() function (which reports an answer in both axes), instead
5335 // of actually reflowing. It's important to avoid the "measuring + final"
5336 // two-pass reflow for XUL, because some XUL layout code may incorrectly
5337 // optimize away the second reflow in cases where it's really needed.
5338 // XXXdholbert We'll remove this special case in bug 1600542.
5339 ReflowInput childRI(pc, *aState.mReflowInput, child, availableSize,
5340 Some(cbSize));
5342 nsBoxLayoutState state(pc, &aState.mRenderingContext, &childRI,
5343 childRI.mReflowDepth);
5344 nsSize physicalPrefSize = child->GetXULPrefSize(state);
5345 auto prefSize = LogicalSize(childWM, physicalPrefSize);
5346 size = prefSize.BSize(childWM);
5348 // XXXdholbert This won't have percentage margins resolved.
5349 // Hopefully we can just avoid those for XUL-content-in-css-grid?
5350 size += childRI.ComputedLogicalMargin(childWM).BStartEnd(childWM);
5351 } else {
5352 size = ::MeasuringReflow(child, aState.mReflowInput, aRC, availableSize,
5353 cbSize, iMinSizeClamp, bMinSizeClamp);
5354 size += child->GetLogicalUsedMargin(childWM).BStartEnd(childWM);
5356 nscoord overflow = size - aMinSizeClamp;
5357 if (MOZ_UNLIKELY(overflow > 0)) {
5358 nscoord contentSize = child->ContentSize(childWM).BSize(childWM);
5359 nscoord newContentSize = std::max(nscoord(0), contentSize - overflow);
5360 // XXXmats deal with percentages better, see bug 1300369 comment 27.
5361 size -= contentSize - newContentSize;
5364 MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
5365 "baseline offset should be non-negative at this point");
5366 MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
5367 aGridItem.mBaselineOffset[aAxis] == nscoord(0),
5368 "baseline offset should be zero when not baseline-aligned");
5369 size += aGridItem.mBaselineOffset[aAxis];
5370 size += extraMargin;
5371 return std::max(size, 0);
5374 struct CachedIntrinsicSizes {
5375 Maybe<nscoord> mMinSize;
5376 Maybe<nscoord> mMinContentContribution;
5377 Maybe<nscoord> mMaxContentContribution;
5379 // The item's percentage basis for intrinsic sizing purposes.
5380 Maybe<LogicalSize> mPercentageBasis;
5382 // "if the grid item spans only grid tracks that have a fixed max track
5383 // sizing function, its automatic minimum size in that dimension is
5384 // further clamped to less than or equal to the size necessary to fit its
5385 // margin box within the resulting grid area (flooring at zero)"
5386 // https://drafts.csswg.org/css-grid/#min-size-auto
5387 // This is the clamp value to use for that:
5388 nscoord mMinSizeClamp = NS_MAXSIZE;
5391 static nscoord MinContentContribution(const GridItemInfo& aGridItem,
5392 const GridReflowInput& aState,
5393 gfxContext* aRC, WritingMode aCBWM,
5394 LogicalAxis aAxis,
5395 CachedIntrinsicSizes* aCache) {
5396 if (aCache->mMinContentContribution.isSome()) {
5397 return aCache->mMinContentContribution.value();
5399 if (aCache->mPercentageBasis.isNothing()) {
5400 aCache->mPercentageBasis.emplace(
5401 aState.PercentageBasisFor(aAxis, aGridItem));
5403 nscoord s = ContentContribution(
5404 aGridItem, aState, aRC, aCBWM, aAxis, aCache->mPercentageBasis,
5405 IntrinsicISizeType::MinISize, aCache->mMinSizeClamp);
5406 aCache->mMinContentContribution.emplace(s);
5407 return s;
5410 static nscoord MaxContentContribution(const GridItemInfo& aGridItem,
5411 const GridReflowInput& aState,
5412 gfxContext* aRC, WritingMode aCBWM,
5413 LogicalAxis aAxis,
5414 CachedIntrinsicSizes* aCache) {
5415 if (aCache->mMaxContentContribution.isSome()) {
5416 return aCache->mMaxContentContribution.value();
5418 if (aCache->mPercentageBasis.isNothing()) {
5419 aCache->mPercentageBasis.emplace(
5420 aState.PercentageBasisFor(aAxis, aGridItem));
5422 nscoord s = ContentContribution(
5423 aGridItem, aState, aRC, aCBWM, aAxis, aCache->mPercentageBasis,
5424 IntrinsicISizeType::PrefISize, aCache->mMinSizeClamp);
5425 aCache->mMaxContentContribution.emplace(s);
5426 return s;
5429 // Computes the min-size contribution for a grid item, as defined at
5430 // https://drafts.csswg.org/css-grid/#min-size-contribution
5431 static nscoord MinSize(const GridItemInfo& aGridItem,
5432 const GridReflowInput& aState, gfxContext* aRC,
5433 WritingMode aCBWM, LogicalAxis aAxis,
5434 CachedIntrinsicSizes* aCache) {
5435 if (aCache->mMinSize.isSome()) {
5436 return aCache->mMinSize.value();
5438 nsIFrame* child = aGridItem.mFrame;
5439 PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
5440 const nsStylePosition* stylePos = child->StylePosition();
5441 StyleSize sizeStyle =
5442 axis == eAxisHorizontal ? stylePos->mWidth : stylePos->mHeight;
5444 auto ourInlineAxis = child->GetWritingMode().PhysicalAxis(eLogicalAxisInline);
5445 // max-content and min-content should behave as initial value in block axis.
5446 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
5447 // for block size dimension on sizing properties (e.g. height), so we
5448 // treat it as `auto`.
5449 if (axis != ourInlineAxis && sizeStyle.BehavesLikeInitialValueOnBlockAxis()) {
5450 sizeStyle = StyleSize::Auto();
5453 if (!sizeStyle.IsAuto() && !sizeStyle.HasPercent()) {
5454 nscoord s =
5455 MinContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, aCache);
5456 aCache->mMinSize.emplace(s);
5457 return s;
5460 if (aCache->mPercentageBasis.isNothing()) {
5461 aCache->mPercentageBasis.emplace(
5462 aState.PercentageBasisFor(aAxis, aGridItem));
5465 // https://drafts.csswg.org/css-grid/#min-size-auto
5466 // This calculates the min-content contribution from either a definite
5467 // min-width (or min-height depending on aAxis), or the "specified /
5468 // transferred size" for min-width:auto if overflow == visible (as min-width:0
5469 // otherwise), or NS_UNCONSTRAINEDSIZE for other min-width intrinsic values
5470 // (which results in always taking the "content size" part below).
5471 MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
5472 "baseline offset should be non-negative at this point");
5473 MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
5474 aGridItem.mBaselineOffset[aAxis] == nscoord(0),
5475 "baseline offset should be zero when not baseline-aligned");
5476 nscoord sz = aGridItem.mBaselineOffset[aAxis] +
5477 nsLayoutUtils::MinSizeContributionForAxis(
5478 axis, aRC, child, IntrinsicISizeType::MinISize,
5479 *aCache->mPercentageBasis);
5480 const StyleSize& style =
5481 axis == eAxisHorizontal ? stylePos->mMinWidth : stylePos->mMinHeight;
5482 // max-content and min-content should behave as initial value in block axis.
5483 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
5484 // for block size dimension on sizing properties (e.g. height), so we
5485 // treat it as `auto`.
5486 const bool inInlineAxis = axis == ourInlineAxis;
5487 const bool isAuto =
5488 style.IsAuto() ||
5489 (!inInlineAxis && style.BehavesLikeInitialValueOnBlockAxis());
5490 if ((inInlineAxis && nsIFrame::ToExtremumLength(style)) ||
5491 (isAuto && child->StyleDisplay()->mOverflowX == StyleOverflow::Visible)) {
5492 // Now calculate the "content size" part and return whichever is smaller.
5493 MOZ_ASSERT(isAuto || sz == NS_UNCONSTRAINEDSIZE);
5494 sz = std::min(sz, ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
5495 aCache->mPercentageBasis,
5496 IntrinsicISizeType::MinISize,
5497 aCache->mMinSizeClamp,
5498 nsLayoutUtils::MIN_INTRINSIC_ISIZE));
5500 aCache->mMinSize.emplace(sz);
5501 return sz;
5504 void nsGridContainerFrame::Tracks::CalculateSizes(
5505 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
5506 const TrackSizingFunctions& aFunctions, nscoord aContentBoxSize,
5507 LineRange GridArea::*aRange, SizingConstraint aConstraint) {
5508 nscoord percentageBasis = aContentBoxSize;
5509 if (percentageBasis == NS_UNCONSTRAINEDSIZE) {
5510 percentageBasis = 0;
5512 InitializeItemBaselines(aState, aGridItems);
5513 ResolveIntrinsicSize(aState, aGridItems, aFunctions, aRange, percentageBasis,
5514 aConstraint);
5515 if (aConstraint != SizingConstraint::MinContent) {
5516 nscoord freeSpace = aContentBoxSize;
5517 if (freeSpace != NS_UNCONSTRAINEDSIZE) {
5518 freeSpace -= SumOfGridGaps();
5520 DistributeFreeSpace(freeSpace);
5521 StretchFlexibleTracks(aState, aGridItems, aFunctions, freeSpace);
5525 TrackSize::StateBits nsGridContainerFrame::Tracks::StateBitsForRange(
5526 const LineRange& aRange) const {
5527 MOZ_ASSERT(!aRange.IsAuto(), "must have a definite range");
5528 TrackSize::StateBits state = TrackSize::StateBits(0);
5529 for (auto i : aRange.Range()) {
5530 state |= mSizes[i].mState;
5532 return state;
5535 static void AddSubgridContribution(TrackSize& aSize,
5536 nscoord aMarginBorderPadding) {
5537 if (aSize.mState & TrackSize::eIntrinsicMinSizing) {
5538 aSize.mBase = std::max(aSize.mBase, aMarginBorderPadding);
5539 aSize.mLimit = std::max(aSize.mLimit, aSize.mBase);
5541 // XXX maybe eFlexMaxSizing too?
5542 // (once we implement https://github.com/w3c/csswg-drafts/issues/2177)
5543 if (aSize.mState &
5544 (TrackSize::eIntrinsicMaxSizing | TrackSize::eFitContent)) {
5545 aSize.mLimit = std::max(aSize.mLimit, aMarginBorderPadding);
5549 bool nsGridContainerFrame::Tracks::ResolveIntrinsicSizeStep1(
5550 GridReflowInput& aState, const TrackSizingFunctions& aFunctions,
5551 nscoord aPercentageBasis, SizingConstraint aConstraint,
5552 const LineRange& aRange, const GridItemInfo& aGridItem) {
5553 CachedIntrinsicSizes cache;
5554 TrackSize& sz = mSizes[aRange.mStart];
5555 WritingMode wm = aState.mWM;
5557 // min sizing
5558 gfxContext* rc = &aState.mRenderingContext;
5559 if (sz.mState & TrackSize::eAutoMinSizing) {
5560 nscoord s;
5561 // Check if we need to apply "Automatic Minimum Size" and cache it.
5562 if (aGridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) {
5563 aGridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize;
5564 // Clamp it if it's spanning a definite track max-sizing function.
5565 if (TrackSize::IsDefiniteMaxSizing(sz.mState)) {
5566 cache.mMinSizeClamp = aFunctions.MaxSizingFor(aRange.mStart)
5567 .AsBreadth()
5568 .Resolve(aPercentageBasis);
5569 aGridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
5571 if (aConstraint != SizingConstraint::MaxContent) {
5572 s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5573 } else {
5574 s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5576 } else {
5577 s = MinSize(aGridItem, aState, rc, wm, mAxis, &cache);
5579 sz.mBase = std::max(sz.mBase, s);
5580 } else if (sz.mState & TrackSize::eMinContentMinSizing) {
5581 auto s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5582 sz.mBase = std::max(sz.mBase, s);
5583 } else if (sz.mState & TrackSize::eMaxContentMinSizing) {
5584 auto s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5585 sz.mBase = std::max(sz.mBase, s);
5587 // max sizing
5588 if (sz.mState & TrackSize::eMinContentMaxSizing) {
5589 auto s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5590 if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
5591 sz.mLimit = s;
5592 } else {
5593 sz.mLimit = std::max(sz.mLimit, s);
5595 } else if (sz.mState &
5596 (TrackSize::eAutoMaxSizing | TrackSize::eMaxContentMaxSizing)) {
5597 auto s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5598 if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
5599 sz.mLimit = s;
5600 } else {
5601 sz.mLimit = std::max(sz.mLimit, s);
5603 if (MOZ_UNLIKELY(sz.mState & TrackSize::eFitContent)) {
5604 // Clamp mLimit to the fit-content() size, for §12.5.1.
5605 nscoord fitContentClamp = aFunctions.SizingFor(aRange.mStart)
5606 .AsFitContent()
5607 .AsBreadth()
5608 .Resolve(aPercentageBasis);
5609 sz.mLimit = std::min(sz.mLimit, fitContentClamp);
5612 if (sz.mLimit < sz.mBase) {
5613 sz.mLimit = sz.mBase;
5615 return sz.mState & TrackSize::eFlexMaxSizing;
5618 void nsGridContainerFrame::Tracks::CalculateItemBaselines(
5619 nsTArray<ItemBaselineData>& aBaselineItems,
5620 BaselineSharingGroup aBaselineGroup) {
5621 if (aBaselineItems.IsEmpty()) {
5622 return;
5625 // Sort the collected items on their baseline track.
5626 std::sort(aBaselineItems.begin(), aBaselineItems.end(),
5627 ItemBaselineData::IsBaselineTrackLessThan);
5629 MOZ_ASSERT(mSizes.Length() > 0, "having an item implies at least one track");
5630 const uint32_t lastTrack = mSizes.Length() - 1;
5631 nscoord maxBaseline = 0;
5632 nscoord maxDescent = 0;
5633 uint32_t currentTrack = kAutoLine; // guaranteed to not match any item
5634 uint32_t trackStartIndex = 0;
5635 for (uint32_t i = 0, len = aBaselineItems.Length(); true; ++i) {
5636 // Find the maximum baseline and descent in the current track.
5637 if (i != len) {
5638 const ItemBaselineData& item = aBaselineItems[i];
5639 if (currentTrack == item.mBaselineTrack) {
5640 maxBaseline = std::max(maxBaseline, item.mBaseline);
5641 maxDescent = std::max(maxDescent, item.mSize - item.mBaseline);
5642 continue;
5645 // Iterate the current track again and update the baseline offsets making
5646 // all items baseline-aligned within this group in this track.
5647 for (uint32_t j = trackStartIndex; j < i; ++j) {
5648 const ItemBaselineData& item = aBaselineItems[j];
5649 item.mGridItem->mBaselineOffset[mAxis] = maxBaseline - item.mBaseline;
5650 MOZ_ASSERT(item.mGridItem->mBaselineOffset[mAxis] >= 0);
5652 if (i != 0) {
5653 // Store the size of this baseline-aligned subtree.
5654 mSizes[currentTrack].mBaselineSubtreeSize[aBaselineGroup] =
5655 maxBaseline + maxDescent;
5656 // Record the first(last) baseline for the first(last) track.
5657 if (currentTrack == 0 && aBaselineGroup == BaselineSharingGroup::First) {
5658 mBaseline[aBaselineGroup] = maxBaseline;
5660 if (currentTrack == lastTrack &&
5661 aBaselineGroup == BaselineSharingGroup::Last) {
5662 mBaseline[aBaselineGroup] = maxBaseline;
5665 if (i == len) {
5666 break;
5668 // Initialize data for the next track with baseline-aligned items.
5669 const ItemBaselineData& item = aBaselineItems[i];
5670 currentTrack = item.mBaselineTrack;
5671 trackStartIndex = i;
5672 maxBaseline = item.mBaseline;
5673 maxDescent = item.mSize - item.mBaseline;
5677 void nsGridContainerFrame::Tracks::InitializeItemBaselines(
5678 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems) {
5679 MOZ_ASSERT(!mIsMasonry);
5680 if (aState.mFrame->IsSubgrid(mAxis)) {
5681 // A grid container's subgridded axis doesn't have a baseline.
5682 return;
5684 nsTArray<ItemBaselineData> firstBaselineItems;
5685 nsTArray<ItemBaselineData> lastBaselineItems;
5686 WritingMode wm = aState.mWM;
5687 ComputedStyle* containerSC = aState.mFrame->Style();
5688 for (GridItemInfo& gridItem : aGridItems) {
5689 if (gridItem.IsSubgrid(mAxis)) {
5690 // A subgrid itself is never baseline-aligned.
5691 continue;
5693 nsIFrame* child = gridItem.mFrame;
5694 uint32_t baselineTrack = kAutoLine;
5695 auto state = ItemState(0);
5696 auto childWM = child->GetWritingMode();
5697 const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
5698 const bool isInlineAxis = mAxis == eLogicalAxisInline; // i.e. columns
5699 // XXX update the line below to include orthogonal grid/table boxes
5700 // XXX since they have baselines in both dimensions. And flexbox with
5701 // XXX reversed main/cross axis?
5702 const bool itemHasBaselineParallelToTrack = isInlineAxis == isOrthogonal;
5703 if (itemHasBaselineParallelToTrack) {
5704 // [align|justify]-self:[last ]baseline.
5705 auto selfAlignment =
5706 isOrthogonal ? child->StylePosition()->UsedJustifySelf(containerSC)._0
5707 : child->StylePosition()->UsedAlignSelf(containerSC)._0;
5708 selfAlignment &= ~StyleAlignFlags::FLAG_BITS;
5709 if (selfAlignment == StyleAlignFlags::BASELINE) {
5710 state |= ItemState::eFirstBaseline | ItemState::eSelfBaseline;
5711 const GridArea& area = gridItem.mArea;
5712 baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
5713 } else if (selfAlignment == StyleAlignFlags::LAST_BASELINE) {
5714 state |= ItemState::eLastBaseline | ItemState::eSelfBaseline;
5715 const GridArea& area = gridItem.mArea;
5716 baselineTrack = (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
5719 // [align|justify]-content:[last ]baseline.
5720 // https://drafts.csswg.org/css-align-3/#baseline-align-content
5721 // "[...] and its computed 'align-self' or 'justify-self' (whichever
5722 // affects its block axis) is 'stretch' or 'self-start' ('self-end').
5723 // For this purpose, the 'start', 'end', 'flex-start', and 'flex-end'
5724 // values of 'align-self' are treated as either 'self-start' or
5725 // 'self-end', whichever they end up equivalent to.
5726 auto alignContent = child->StylePosition()->mAlignContent.primary;
5727 alignContent &= ~StyleAlignFlags::FLAG_BITS;
5728 if (alignContent == StyleAlignFlags::BASELINE ||
5729 alignContent == StyleAlignFlags::LAST_BASELINE) {
5730 const auto selfAlignEdge = alignContent == StyleAlignFlags::BASELINE
5731 ? StyleAlignFlags::SELF_START
5732 : StyleAlignFlags::SELF_END;
5733 bool validCombo = selfAlignment == StyleAlignFlags::NORMAL ||
5734 selfAlignment == StyleAlignFlags::STRETCH ||
5735 selfAlignment == selfAlignEdge;
5736 if (!validCombo) {
5737 // We're doing alignment in the axis that's orthogonal to mAxis here.
5738 LogicalAxis alignAxis = GetOrthogonalAxis(mAxis);
5739 // |sameSide| is true if the container's start side in this axis is
5740 // the same as the child's start side, in the child's parallel axis.
5741 bool sameSide = wm.ParallelAxisStartsOnSameSide(alignAxis, childWM);
5742 if (selfAlignment == StyleAlignFlags::LEFT) {
5743 selfAlignment = !isInlineAxis || wm.IsBidiLTR()
5744 ? StyleAlignFlags::START
5745 : StyleAlignFlags::END;
5746 } else if (selfAlignment == StyleAlignFlags::RIGHT) {
5747 selfAlignment = isInlineAxis && wm.IsBidiLTR()
5748 ? StyleAlignFlags::END
5749 : StyleAlignFlags::START;
5752 if (selfAlignment == StyleAlignFlags::START ||
5753 selfAlignment == StyleAlignFlags::FLEX_START) {
5754 validCombo =
5755 sameSide == (alignContent == StyleAlignFlags::BASELINE);
5756 } else if (selfAlignment == StyleAlignFlags::END ||
5757 selfAlignment == StyleAlignFlags::FLEX_END) {
5758 validCombo =
5759 sameSide == (alignContent == StyleAlignFlags::LAST_BASELINE);
5762 if (validCombo) {
5763 const GridArea& area = gridItem.mArea;
5764 if (alignContent == StyleAlignFlags::BASELINE) {
5765 state |= ItemState::eFirstBaseline | ItemState::eContentBaseline;
5766 baselineTrack =
5767 isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
5768 } else if (alignContent == StyleAlignFlags::LAST_BASELINE) {
5769 state |= ItemState::eLastBaseline | ItemState::eContentBaseline;
5770 baselineTrack =
5771 (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
5777 if (state & ItemState::eIsBaselineAligned) {
5778 // XXXmats if |child| is a descendant of a subgrid then the metrics
5779 // below needs to account for the accumulated MPB somehow...
5781 // XXX available size issue
5782 LogicalSize avail(childWM, INFINITE_ISIZE_COORD, NS_UNCONSTRAINEDSIZE);
5783 auto* rc = &aState.mRenderingContext;
5784 // XXX figure out if we can avoid/merge this reflow with the main reflow.
5785 // XXX (after bug 1174569 is sorted out)
5787 // XXX How should we handle percentage padding here? (bug 1330866)
5788 // XXX (see ::ContentContribution and how it deals with percentages)
5789 // XXX What if the true baseline after line-breaking differs from this
5790 // XXX hypothetical baseline based on an infinite inline size?
5791 // XXX Maybe we should just call ::ContentContribution here instead?
5792 // XXX For now we just pass a zero-sized CB:
5793 LogicalSize cbSize(childWM, 0, 0);
5794 ::MeasuringReflow(child, aState.mReflowInput, rc, avail, cbSize);
5795 nscoord baseline;
5796 nsGridContainerFrame* grid = do_QueryFrame(child);
5797 if (state & ItemState::eFirstBaseline) {
5798 if (grid) {
5799 if (isOrthogonal == isInlineAxis) {
5800 grid->GetBBaseline(BaselineSharingGroup::First, &baseline);
5801 } else {
5802 grid->GetIBaseline(BaselineSharingGroup::First, &baseline);
5805 if (grid || nsLayoutUtils::GetFirstLineBaseline(wm, child, &baseline)) {
5806 NS_ASSERTION(baseline != NS_INTRINSIC_ISIZE_UNKNOWN,
5807 "about to use an unknown baseline");
5808 auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
5809 auto m = child->GetLogicalUsedMargin(wm);
5810 baseline += isInlineAxis ? m.IStart(wm) : m.BStart(wm);
5811 auto alignSize =
5812 frameSize + (isInlineAxis ? m.IStartEnd(wm) : m.BStartEnd(wm));
5813 firstBaselineItems.AppendElement(ItemBaselineData(
5814 {baselineTrack, baseline, alignSize, &gridItem}));
5815 } else {
5816 state &= ~ItemState::eAllBaselineBits;
5818 } else {
5819 if (grid) {
5820 if (isOrthogonal == isInlineAxis) {
5821 grid->GetBBaseline(BaselineSharingGroup::Last, &baseline);
5822 } else {
5823 grid->GetIBaseline(BaselineSharingGroup::Last, &baseline);
5826 if (grid || nsLayoutUtils::GetLastLineBaseline(wm, child, &baseline)) {
5827 NS_ASSERTION(baseline != NS_INTRINSIC_ISIZE_UNKNOWN,
5828 "about to use an unknown baseline");
5829 auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
5830 auto m = child->GetLogicalUsedMargin(wm);
5831 if (!grid) {
5832 // Convert to distance from border-box end.
5833 baseline = frameSize - baseline;
5835 auto descent = baseline + (isInlineAxis ? m.IEnd(wm) : m.BEnd(wm));
5836 auto alignSize =
5837 frameSize + (isInlineAxis ? m.IStartEnd(wm) : m.BStartEnd(wm));
5838 lastBaselineItems.AppendElement(
5839 ItemBaselineData({baselineTrack, descent, alignSize, &gridItem}));
5840 state |= ItemState::eEndSideBaseline;
5841 } else {
5842 state &= ~ItemState::eAllBaselineBits;
5846 MOZ_ASSERT(
5847 (state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) !=
5848 (ItemState::eFirstBaseline | ItemState::eLastBaseline),
5849 "first/last baseline bits are mutually exclusive");
5850 MOZ_ASSERT(
5851 (state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)) !=
5852 (ItemState::eSelfBaseline | ItemState::eContentBaseline),
5853 "*-self and *-content baseline bits are mutually exclusive");
5854 MOZ_ASSERT(
5855 !(state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) ==
5856 !(state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)),
5857 "first/last bit requires self/content bit and vice versa");
5858 gridItem.mState[mAxis] |= state;
5859 gridItem.mBaselineOffset[mAxis] = nscoord(0);
5862 if (firstBaselineItems.IsEmpty() && lastBaselineItems.IsEmpty()) {
5863 return;
5866 // TODO: CSS Align spec issue - how to align a baseline subtree in a track?
5867 // https://lists.w3.org/Archives/Public/www-style/2016May/0141.html
5868 mBaselineSubtreeAlign[BaselineSharingGroup::First] = StyleAlignFlags::START;
5869 mBaselineSubtreeAlign[BaselineSharingGroup::Last] = StyleAlignFlags::END;
5871 CalculateItemBaselines(firstBaselineItems, BaselineSharingGroup::First);
5872 CalculateItemBaselines(lastBaselineItems, BaselineSharingGroup::Last);
5875 // TODO: we store the wrong baseline group offset in some cases (bug 1632200)
5876 void nsGridContainerFrame::Tracks::InitializeItemBaselinesInMasonryAxis(
5877 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
5878 BaselineAlignmentSet aSet, const nsSize& aContainerSize,
5879 nsTArray<nscoord>& aTrackSizes,
5880 nsTArray<ItemBaselineData>& aFirstBaselineItems,
5881 nsTArray<ItemBaselineData>& aLastBaselineItems) {
5882 MOZ_ASSERT(mIsMasonry);
5883 WritingMode wm = aState.mWM;
5884 ComputedStyle* containerSC = aState.mFrame->Style();
5885 for (GridItemInfo& gridItem : aGridItems) {
5886 if (gridItem.IsSubgrid(mAxis)) {
5887 // A subgrid itself is never baseline-aligned.
5888 continue;
5890 const auto& area = gridItem.mArea;
5891 if (aSet.mItemSet == BaselineAlignmentSet::LastItems) {
5892 // NOTE: eIsLastItemInMasonryTrack is set also if the item is the ONLY
5893 // item in its track; the eIsBaselineAligned check excludes it though
5894 // since it participates in the start baseline groups in that case.
5896 // XXX what if it's the only item in THAT baseline group?
5897 // XXX should it participate in the last-item group instead then
5898 // if there are more baseline-aligned items there?
5899 if (!(gridItem.mState[mAxis] & ItemState::eIsLastItemInMasonryTrack) ||
5900 (gridItem.mState[mAxis] & ItemState::eIsBaselineAligned)) {
5901 continue;
5903 } else {
5904 if (area.LineRangeForAxis(mAxis).mStart > 0 ||
5905 (gridItem.mState[mAxis] & ItemState::eIsBaselineAligned)) {
5906 continue;
5909 auto trackAlign =
5910 aState.mGridStyle
5911 ->UsedTracksAlignment(
5912 mAxis, area.LineRangeForAxis(GetOrthogonalAxis(mAxis)).mStart)
5913 .primary;
5914 if (!aSet.MatchTrackAlignment(trackAlign)) {
5915 continue;
5918 nsIFrame* child = gridItem.mFrame;
5919 uint32_t baselineTrack = kAutoLine;
5920 auto state = ItemState(0);
5921 auto childWM = child->GetWritingMode();
5922 const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
5923 const bool isInlineAxis = mAxis == eLogicalAxisInline; // i.e. columns
5924 // XXX update the line below to include orthogonal grid/table boxes
5925 // XXX since they have baselines in both dimensions. And flexbox with
5926 // XXX reversed main/cross axis?
5927 const bool itemHasBaselineParallelToTrack = isInlineAxis == isOrthogonal;
5928 if (itemHasBaselineParallelToTrack) {
5929 const auto* pos = child->StylePosition();
5930 // [align|justify]-self:[last ]baseline.
5931 auto selfAlignment = pos->UsedSelfAlignment(mAxis, containerSC);
5932 selfAlignment &= ~StyleAlignFlags::FLAG_BITS;
5933 if (selfAlignment == StyleAlignFlags::BASELINE) {
5934 state |= ItemState::eFirstBaseline | ItemState::eSelfBaseline;
5935 baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
5936 } else if (selfAlignment == StyleAlignFlags::LAST_BASELINE) {
5937 state |= ItemState::eLastBaseline | ItemState::eSelfBaseline;
5938 baselineTrack = (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
5939 } else {
5940 // [align|justify]-content:[last ]baseline.
5941 auto childAxis = isOrthogonal ? GetOrthogonalAxis(mAxis) : mAxis;
5942 auto alignContent = pos->UsedContentAlignment(childAxis).primary;
5943 alignContent &= ~StyleAlignFlags::FLAG_BITS;
5944 if (alignContent == StyleAlignFlags::BASELINE) {
5945 state |= ItemState::eFirstBaseline | ItemState::eContentBaseline;
5946 baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
5947 } else if (alignContent == StyleAlignFlags::LAST_BASELINE) {
5948 state |= ItemState::eLastBaseline | ItemState::eContentBaseline;
5949 baselineTrack =
5950 (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
5955 if (state & ItemState::eIsBaselineAligned) {
5956 // XXXmats if |child| is a descendant of a subgrid then the metrics
5957 // below needs to account for the accumulated MPB somehow...
5959 nscoord baseline;
5960 nsGridContainerFrame* grid = do_QueryFrame(child);
5961 if (state & ItemState::eFirstBaseline) {
5962 if (grid) {
5963 if (isOrthogonal == isInlineAxis) {
5964 grid->GetBBaseline(BaselineSharingGroup::First, &baseline);
5965 } else {
5966 grid->GetIBaseline(BaselineSharingGroup::First, &baseline);
5969 if (grid || nsLayoutUtils::GetFirstLineBaseline(wm, child, &baseline)) {
5970 NS_ASSERTION(baseline != NS_INTRINSIC_ISIZE_UNKNOWN,
5971 "about to use an unknown baseline");
5972 auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
5973 nscoord alignSize;
5974 LogicalPoint pos =
5975 child->GetLogicalNormalPosition(wm, aContainerSize);
5976 baseline += pos.Pos(mAxis, wm);
5977 if (aSet.mTrackAlignmentSet == BaselineAlignmentSet::EndStretch) {
5978 state |= ItemState::eEndSideBaseline;
5979 // Convert to distance from the track end.
5980 baseline =
5981 aTrackSizes[gridItem.mArea
5982 .LineRangeForAxis(GetOrthogonalAxis(mAxis))
5983 .mStart] -
5984 baseline;
5986 alignSize = frameSize;
5987 aFirstBaselineItems.AppendElement(ItemBaselineData(
5988 {baselineTrack, baseline, alignSize, &gridItem}));
5989 } else {
5990 state &= ~ItemState::eAllBaselineBits;
5992 } else {
5993 if (grid) {
5994 if (isOrthogonal == isInlineAxis) {
5995 grid->GetBBaseline(BaselineSharingGroup::Last, &baseline);
5996 } else {
5997 grid->GetIBaseline(BaselineSharingGroup::Last, &baseline);
6000 if (grid || nsLayoutUtils::GetLastLineBaseline(wm, child, &baseline)) {
6001 NS_ASSERTION(baseline != NS_INTRINSIC_ISIZE_UNKNOWN,
6002 "about to use an unknown baseline");
6003 auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
6004 auto m = child->GetLogicalUsedMargin(wm);
6005 if (!grid &&
6006 aSet.mTrackAlignmentSet == BaselineAlignmentSet::EndStretch) {
6007 // Convert to distance from border-box end.
6008 state |= ItemState::eEndSideBaseline;
6009 LogicalPoint pos =
6010 child->GetLogicalNormalPosition(wm, aContainerSize);
6011 baseline += pos.Pos(mAxis, wm);
6012 baseline =
6013 aTrackSizes[gridItem.mArea
6014 .LineRangeForAxis(GetOrthogonalAxis(mAxis))
6015 .mStart] -
6016 baseline;
6017 } else if (grid && aSet.mTrackAlignmentSet ==
6018 BaselineAlignmentSet::StartStretch) {
6019 // Convert to distance from border-box start.
6020 baseline = frameSize - baseline;
6022 if (aSet.mItemSet == BaselineAlignmentSet::LastItems &&
6023 aSet.mTrackAlignmentSet == BaselineAlignmentSet::StartStretch) {
6024 LogicalPoint pos =
6025 child->GetLogicalNormalPosition(wm, aContainerSize);
6026 baseline += pos.B(wm);
6028 if (aSet.mTrackAlignmentSet == BaselineAlignmentSet::EndStretch) {
6029 state |= ItemState::eEndSideBaseline;
6031 auto descent =
6032 baseline + ((state & ItemState::eEndSideBaseline)
6033 ? (isInlineAxis ? m.IEnd(wm) : m.BEnd(wm))
6034 : (isInlineAxis ? m.IStart(wm) : m.BStart(wm)));
6035 auto alignSize =
6036 frameSize + (isInlineAxis ? m.IStartEnd(wm) : m.BStartEnd(wm));
6037 aLastBaselineItems.AppendElement(
6038 ItemBaselineData({baselineTrack, descent, alignSize, &gridItem}));
6039 } else {
6040 state &= ~ItemState::eAllBaselineBits;
6044 MOZ_ASSERT(
6045 (state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) !=
6046 (ItemState::eFirstBaseline | ItemState::eLastBaseline),
6047 "first/last baseline bits are mutually exclusive");
6048 MOZ_ASSERT(
6049 (state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)) !=
6050 (ItemState::eSelfBaseline | ItemState::eContentBaseline),
6051 "*-self and *-content baseline bits are mutually exclusive");
6052 MOZ_ASSERT(
6053 !(state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) ==
6054 !(state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)),
6055 "first/last bit requires self/content bit and vice versa");
6056 gridItem.mState[mAxis] |= state;
6057 gridItem.mBaselineOffset[mAxis] = nscoord(0);
6060 CalculateItemBaselines(aFirstBaselineItems, BaselineSharingGroup::First);
6061 CalculateItemBaselines(aLastBaselineItems, BaselineSharingGroup::Last);
6063 // TODO: make sure the mBaselines (i.e. the baselines we export from
6064 // the grid container) are offset from the correct container edge.
6065 // Also, which of the baselines do we pick to export exactly?
6067 MOZ_ASSERT(aFirstBaselineItems.Length() != 1 ||
6068 aFirstBaselineItems[0].mGridItem->mBaselineOffset[mAxis] == 0,
6069 "a baseline group that contains only one item should not "
6070 "produce a non-zero item baseline offset");
6071 MOZ_ASSERT(aLastBaselineItems.Length() != 1 ||
6072 aLastBaselineItems[0].mGridItem->mBaselineOffset[mAxis] == 0,
6073 "a baseline group that contains only one item should not "
6074 "produce a non-zero item baseline offset");
6077 void nsGridContainerFrame::Tracks::AlignBaselineSubtree(
6078 const GridItemInfo& aGridItem) const {
6079 if (mIsMasonry) {
6080 return;
6082 auto state = aGridItem.mState[mAxis];
6083 if (!(state & ItemState::eIsBaselineAligned)) {
6084 return;
6086 const GridArea& area = aGridItem.mArea;
6087 int32_t baselineTrack;
6088 const bool isFirstBaseline = state & ItemState::eFirstBaseline;
6089 if (isFirstBaseline) {
6090 baselineTrack =
6091 mAxis == eLogicalAxisBlock ? area.mRows.mStart : area.mCols.mStart;
6092 } else {
6093 baselineTrack =
6094 (mAxis == eLogicalAxisBlock ? area.mRows.mEnd : area.mCols.mEnd) - 1;
6096 const TrackSize& sz = mSizes[baselineTrack];
6097 auto baselineGroup = isFirstBaseline ? BaselineSharingGroup::First
6098 : BaselineSharingGroup::Last;
6099 nscoord delta = sz.mBase - sz.mBaselineSubtreeSize[baselineGroup];
6100 const auto subtreeAlign = mBaselineSubtreeAlign[baselineGroup];
6101 if (subtreeAlign == StyleAlignFlags::START) {
6102 if (state & ItemState::eLastBaseline) {
6103 aGridItem.mBaselineOffset[mAxis] += delta;
6105 } else if (subtreeAlign == StyleAlignFlags::END) {
6106 if (isFirstBaseline) {
6107 aGridItem.mBaselineOffset[mAxis] += delta;
6109 } else if (subtreeAlign == StyleAlignFlags::CENTER) {
6110 aGridItem.mBaselineOffset[mAxis] += delta / 2;
6111 } else {
6112 MOZ_ASSERT_UNREACHABLE("unexpected baseline subtree alignment");
6116 template <nsGridContainerFrame::Tracks::TrackSizingPhase phase>
6117 bool nsGridContainerFrame::Tracks::GrowSizeForSpanningItems(
6118 nsTArray<Step2ItemData>::iterator aIter,
6119 const nsTArray<Step2ItemData>::iterator aIterEnd,
6120 nsTArray<uint32_t>& aTracks, nsTArray<TrackSize>& aPlan,
6121 nsTArray<TrackSize>& aItemPlan, TrackSize::StateBits aSelector,
6122 const FitContentClamper& aFitContentClamper,
6123 bool aNeedInfinitelyGrowableFlag) {
6124 constexpr bool isMaxSizingPhase =
6125 phase == TrackSizingPhase::IntrinsicMaximums ||
6126 phase == TrackSizingPhase::MaxContentMaximums;
6127 bool needToUpdateSizes = false;
6128 InitializePlan<phase>(aPlan);
6129 for (; aIter != aIterEnd; ++aIter) {
6130 const Step2ItemData& item = *aIter;
6131 if (!(item.mState & aSelector)) {
6132 continue;
6134 if (isMaxSizingPhase) {
6135 for (auto i : item.mLineRange.Range()) {
6136 aPlan[i].mState |= TrackSize::eModified;
6139 nscoord space = item.SizeContributionForPhase<phase>();
6140 if (space <= 0) {
6141 continue;
6143 aTracks.ClearAndRetainStorage();
6144 space = CollectGrowable<phase>(space, item.mLineRange, aSelector, aTracks);
6145 if (space > 0) {
6146 DistributeToTrackSizes<phase>(space, aPlan, aItemPlan, aTracks, aSelector,
6147 aFitContentClamper);
6148 needToUpdateSizes = true;
6151 if (isMaxSizingPhase) {
6152 needToUpdateSizes = true;
6154 if (needToUpdateSizes) {
6155 CopyPlanToSize<phase>(aPlan, aNeedInfinitelyGrowableFlag);
6157 return needToUpdateSizes;
6160 void nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
6161 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
6162 const TrackSizingFunctions& aFunctions, LineRange GridArea::*aRange,
6163 nscoord aPercentageBasis, SizingConstraint aConstraint) {
6164 // Resolve Intrinsic Track Sizes
6165 // http://dev.w3.org/csswg/css-grid/#algo-content
6166 // We're also setting eIsFlexing on the item state here to speed up
6167 // FindUsedFlexFraction later.
6168 struct PerSpanData {
6169 PerSpanData()
6170 : mItemCountWithSameSpan(0), mStateBits(TrackSize::StateBits(0)) {}
6171 uint32_t mItemCountWithSameSpan;
6172 TrackSize::StateBits mStateBits;
6174 AutoTArray<PerSpanData, 16> perSpanData;
6175 nsTArray<Step2ItemData> step2Items;
6176 gfxContext* rc = &aState.mRenderingContext;
6177 WritingMode wm = aState.mWM;
6178 uint32_t maxSpan = 0; // max span of the step2Items items
6179 // Setup track selector for step 2.2:
6180 const auto contentBasedMinSelector =
6181 aConstraint == SizingConstraint::MinContent
6182 ? TrackSize::eIntrinsicMinSizing
6183 : TrackSize::eMinOrMaxContentMinSizing;
6184 // Setup track selector for step 2.3:
6185 const auto maxContentMinSelector =
6186 aConstraint == SizingConstraint::MaxContent
6187 ? (TrackSize::eMaxContentMinSizing | TrackSize::eAutoMinSizing)
6188 : TrackSize::eMaxContentMinSizing;
6189 const auto orthogonalAxis = GetOrthogonalAxis(mAxis);
6190 const bool isMasonryInOtherAxis = aState.mFrame->IsMasonry(orthogonalAxis);
6191 for (auto& gridItem : aGridItems) {
6192 MOZ_ASSERT(!(gridItem.mState[mAxis] &
6193 (ItemState::eApplyAutoMinSize | ItemState::eIsFlexing |
6194 ItemState::eClampMarginBoxMinSize)),
6195 "Why are any of these bits set already?");
6196 const GridArea& area = gridItem.mArea;
6197 const LineRange& lineRange = area.*aRange;
6199 // If we have masonry layout in the other axis then skip this item unless
6200 // it's in the first masonry track, or has definite placement in this axis,
6201 // or spans all tracks in this axis (since that implies it will be placed
6202 // at line 1 regardless of layout results of other items).
6203 if (isMasonryInOtherAxis &&
6204 gridItem.mArea.LineRangeForAxis(orthogonalAxis).mStart != 0 &&
6205 (gridItem.mState[mAxis] & ItemState::eAutoPlacement) &&
6206 gridItem.mArea.LineRangeForAxis(mAxis).Extent() != mSizes.Length()) {
6207 continue;
6210 uint32_t span = lineRange.Extent();
6211 if (MOZ_UNLIKELY(gridItem.mState[mAxis] & ItemState::eIsSubgrid)) {
6212 auto itemWM = gridItem.mFrame->GetWritingMode();
6213 auto percentageBasis = aState.PercentageBasisFor(mAxis, gridItem);
6214 if (percentageBasis.ISize(itemWM) == NS_UNCONSTRAINEDSIZE) {
6215 percentageBasis.ISize(itemWM) = nscoord(0);
6217 if (percentageBasis.BSize(itemWM) == NS_UNCONSTRAINEDSIZE) {
6218 percentageBasis.BSize(itemWM) = nscoord(0);
6220 auto* subgrid =
6221 SubgridComputeMarginBorderPadding(gridItem, percentageBasis);
6222 LogicalMargin mbp = SubgridAccumulatedMarginBorderPadding(
6223 gridItem.SubgridFrame(), subgrid, wm, mAxis);
6224 if (span == 1) {
6225 AddSubgridContribution(mSizes[lineRange.mStart],
6226 mbp.StartEnd(mAxis, wm));
6227 } else {
6228 AddSubgridContribution(mSizes[lineRange.mStart], mbp.Start(mAxis, wm));
6229 AddSubgridContribution(mSizes[lineRange.mEnd - 1], mbp.End(mAxis, wm));
6231 continue;
6234 if (span == 1) {
6235 // Step 1. Size tracks to fit non-spanning items.
6236 if (ResolveIntrinsicSizeStep1(aState, aFunctions, aPercentageBasis,
6237 aConstraint, lineRange, gridItem)) {
6238 gridItem.mState[mAxis] |= ItemState::eIsFlexing;
6240 } else {
6241 TrackSize::StateBits state = StateBitsForRange(lineRange);
6243 // Check if we need to apply "Automatic Minimum Size" and cache it.
6244 if ((state & TrackSize::eAutoMinSizing) &&
6245 gridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) {
6246 gridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize;
6249 if (state & TrackSize::eFlexMaxSizing) {
6250 gridItem.mState[mAxis] |= ItemState::eIsFlexing;
6251 } else if (state & (TrackSize::eIntrinsicMinSizing |
6252 TrackSize::eIntrinsicMaxSizing)) {
6253 // Collect data for Step 2.
6254 maxSpan = std::max(maxSpan, span);
6255 if (span >= perSpanData.Length()) {
6256 perSpanData.SetLength(2 * span);
6258 perSpanData[span].mItemCountWithSameSpan++;
6259 perSpanData[span].mStateBits |= state;
6260 CachedIntrinsicSizes cache;
6261 // Calculate data for "Automatic Minimum Size" clamping, if needed.
6262 if (TrackSize::IsDefiniteMaxSizing(state) &&
6263 (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize)) {
6264 nscoord minSizeClamp = 0;
6265 for (auto i : lineRange.Range()) {
6266 minSizeClamp += aFunctions.MaxSizingFor(i).AsBreadth().Resolve(
6267 aPercentageBasis);
6269 minSizeClamp += mGridGap * (span - 1);
6270 cache.mMinSizeClamp = minSizeClamp;
6271 gridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
6273 // Collect the various grid item size contributions we need.
6274 nscoord minSize = 0;
6275 if (state & TrackSize::eIntrinsicMinSizing) { // for 2.1
6276 minSize = MinSize(gridItem, aState, rc, wm, mAxis, &cache);
6278 nscoord minContent = 0;
6279 if (state & (contentBasedMinSelector | // for 2.2
6280 TrackSize::eIntrinsicMaxSizing)) { // for 2.5
6281 minContent =
6282 MinContentContribution(gridItem, aState, rc, wm, mAxis, &cache);
6284 nscoord maxContent = 0;
6285 if (state & (maxContentMinSelector | // for 2.3
6286 TrackSize::eAutoOrMaxContentMaxSizing)) { // for 2.6
6287 maxContent =
6288 MaxContentContribution(gridItem, aState, rc, wm, mAxis, &cache);
6290 step2Items.AppendElement(
6291 Step2ItemData({span, state, lineRange, minSize, minContent,
6292 maxContent, gridItem.mFrame}));
6295 MOZ_ASSERT(!(gridItem.mState[mAxis] & ItemState::eClampMarginBoxMinSize) ||
6296 (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize),
6297 "clamping only applies to Automatic Minimum Size");
6300 // Step 2.
6301 if (maxSpan) {
6302 auto fitContentClamper = [&aFunctions, aPercentageBasis](uint32_t aTrack,
6303 nscoord aMinSize,
6304 nscoord* aSize) {
6305 nscoord fitContentLimit = ::ResolveToDefiniteSize(
6306 aFunctions.MaxSizingFor(aTrack), aPercentageBasis);
6307 if (*aSize > fitContentLimit) {
6308 *aSize = std::max(aMinSize, fitContentLimit);
6309 return true;
6311 return false;
6314 // Sort the collected items on span length, shortest first. There's no need
6315 // for a stable sort here since the sizing isn't order dependent within
6316 // a group of items with the same span length.
6317 std::sort(step2Items.begin(), step2Items.end(),
6318 Step2ItemData::IsSpanLessThan);
6320 nsTArray<uint32_t> tracks(maxSpan);
6321 nsTArray<TrackSize> plan(mSizes.Length());
6322 plan.SetLength(mSizes.Length());
6323 nsTArray<TrackSize> itemPlan(mSizes.Length());
6324 itemPlan.SetLength(mSizes.Length());
6325 // Start / end iterator for items of the same span length:
6326 auto spanGroupStart = step2Items.begin();
6327 auto spanGroupEnd = spanGroupStart;
6328 const auto end = step2Items.end();
6329 for (; spanGroupStart != end; spanGroupStart = spanGroupEnd) {
6330 const uint32_t span = spanGroupStart->mSpan;
6331 spanGroupEnd = spanGroupStart + perSpanData[span].mItemCountWithSameSpan;
6332 TrackSize::StateBits stateBitsForSpan = perSpanData[span].mStateBits;
6333 bool updatedBase = false; // Did we update any mBase in step 2.1 - 2.3?
6334 TrackSize::StateBits selector(TrackSize::eIntrinsicMinSizing);
6335 if (stateBitsForSpan & selector) {
6336 // Step 2.1 MinSize to intrinsic min-sizing.
6337 updatedBase =
6338 GrowSizeForSpanningItems<TrackSizingPhase::IntrinsicMinimums>(
6339 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
6342 selector = contentBasedMinSelector;
6343 if (stateBitsForSpan & selector) {
6344 // Step 2.2 MinContentContribution to min-/max-content (and 'auto' when
6345 // sizing under a min-content constraint) min-sizing.
6346 updatedBase |=
6347 GrowSizeForSpanningItems<TrackSizingPhase::ContentBasedMinimums>(
6348 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
6351 selector = maxContentMinSelector;
6352 if (stateBitsForSpan & selector) {
6353 // Step 2.3 MaxContentContribution to max-content (and 'auto' when
6354 // sizing under a max-content constraint) min-sizing.
6355 updatedBase |=
6356 GrowSizeForSpanningItems<TrackSizingPhase::MaxContentMinimums>(
6357 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
6360 if (updatedBase) {
6361 // Step 2.4
6362 for (TrackSize& sz : mSizes) {
6363 if (sz.mBase > sz.mLimit) {
6364 sz.mLimit = sz.mBase;
6369 selector = TrackSize::eIntrinsicMaxSizing;
6370 if (stateBitsForSpan & selector) {
6371 const bool willRunStep2_6 =
6372 stateBitsForSpan & TrackSize::eAutoOrMaxContentMaxSizing;
6373 // Step 2.5 MinContentContribution to intrinsic max-sizing.
6374 GrowSizeForSpanningItems<TrackSizingPhase::IntrinsicMaximums>(
6375 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector,
6376 fitContentClamper, willRunStep2_6);
6378 if (willRunStep2_6) {
6379 // Step 2.6 MaxContentContribution to max-content max-sizing.
6380 selector = TrackSize::eAutoOrMaxContentMaxSizing;
6381 GrowSizeForSpanningItems<TrackSizingPhase::MaxContentMaximums>(
6382 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector,
6383 fitContentClamper);
6389 // Step 3.
6390 for (TrackSize& sz : mSizes) {
6391 if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
6392 sz.mLimit = sz.mBase;
6397 float nsGridContainerFrame::Tracks::FindFrUnitSize(
6398 const LineRange& aRange, const nsTArray<uint32_t>& aFlexTracks,
6399 const TrackSizingFunctions& aFunctions, nscoord aSpaceToFill) const {
6400 MOZ_ASSERT(aSpaceToFill > 0 && !aFlexTracks.IsEmpty());
6401 float flexFactorSum = 0.0f;
6402 nscoord leftOverSpace = aSpaceToFill;
6403 for (auto i : aRange.Range()) {
6404 const TrackSize& sz = mSizes[i];
6405 if (sz.mState & TrackSize::eFlexMaxSizing) {
6406 flexFactorSum += aFunctions.MaxSizingFor(i).AsFr();
6407 } else {
6408 leftOverSpace -= sz.mBase;
6409 if (leftOverSpace <= 0) {
6410 return 0.0f;
6414 bool restart;
6415 float hypotheticalFrSize;
6416 nsTArray<uint32_t> flexTracks(aFlexTracks.Clone());
6417 uint32_t numFlexTracks = flexTracks.Length();
6418 do {
6419 restart = false;
6420 hypotheticalFrSize = leftOverSpace / std::max(flexFactorSum, 1.0f);
6421 for (uint32_t i = 0, len = flexTracks.Length(); i < len; ++i) {
6422 uint32_t track = flexTracks[i];
6423 if (track == kAutoLine) {
6424 continue; // Track marked as inflexible in a prev. iter of this loop.
6426 float flexFactor = aFunctions.MaxSizingFor(track).AsFr();
6427 const nscoord base = mSizes[track].mBase;
6428 if (flexFactor * hypotheticalFrSize < base) {
6429 // 12.7.1.4: Treat this track as inflexible.
6430 flexTracks[i] = kAutoLine;
6431 flexFactorSum -= flexFactor;
6432 leftOverSpace -= base;
6433 --numFlexTracks;
6434 if (numFlexTracks == 0 || leftOverSpace <= 0) {
6435 return 0.0f;
6437 restart = true;
6438 // break; XXX (bug 1176621 comment 16) measure which is more common
6441 } while (restart);
6442 return hypotheticalFrSize;
6445 float nsGridContainerFrame::Tracks::FindUsedFlexFraction(
6446 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
6447 const nsTArray<uint32_t>& aFlexTracks,
6448 const TrackSizingFunctions& aFunctions, nscoord aAvailableSize) const {
6449 if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
6450 // Use all of the grid tracks and a 'space to fill' of the available space.
6451 const TranslatedLineRange range(0, mSizes.Length());
6452 return FindFrUnitSize(range, aFlexTracks, aFunctions, aAvailableSize);
6455 // The used flex fraction is the maximum of:
6456 // ... each flexible track's base size divided by its flex factor (which is
6457 // floored at 1).
6458 float fr = 0.0f;
6459 for (uint32_t track : aFlexTracks) {
6460 float flexFactor = aFunctions.MaxSizingFor(track).AsFr();
6461 float possiblyDividedBaseSize = (flexFactor > 1.0f)
6462 ? mSizes[track].mBase / flexFactor
6463 : mSizes[track].mBase;
6464 fr = std::max(fr, possiblyDividedBaseSize);
6466 WritingMode wm = aState.mWM;
6467 gfxContext* rc = &aState.mRenderingContext;
6468 // ... the result of 'finding the size of an fr' for each item that spans
6469 // a flex track with its max-content contribution as 'space to fill'
6470 for (const GridItemInfo& item : aGridItems) {
6471 if (item.mState[mAxis] & ItemState::eIsFlexing) {
6472 // XXX optimize: bug 1194446
6473 auto pb = Some(aState.PercentageBasisFor(mAxis, item));
6474 nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis, pb,
6475 IntrinsicISizeType::PrefISize);
6476 const LineRange& range =
6477 mAxis == eLogicalAxisInline ? item.mArea.mCols : item.mArea.mRows;
6478 MOZ_ASSERT(range.Extent() >= 1);
6479 const auto spannedGaps = range.Extent() - 1;
6480 if (spannedGaps > 0) {
6481 spaceToFill -= mGridGap * spannedGaps;
6483 if (spaceToFill <= 0) {
6484 continue;
6486 // ... and all its spanned tracks as input.
6487 nsTArray<uint32_t> itemFlexTracks;
6488 for (auto i : range.Range()) {
6489 if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
6490 itemFlexTracks.AppendElement(i);
6493 float itemFr =
6494 FindFrUnitSize(range, itemFlexTracks, aFunctions, spaceToFill);
6495 fr = std::max(fr, itemFr);
6498 return fr;
6501 void nsGridContainerFrame::Tracks::StretchFlexibleTracks(
6502 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
6503 const TrackSizingFunctions& aFunctions, nscoord aAvailableSize) {
6504 if (aAvailableSize <= 0) {
6505 return;
6507 nsTArray<uint32_t> flexTracks(mSizes.Length());
6508 for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
6509 if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
6510 flexTracks.AppendElement(i);
6513 if (flexTracks.IsEmpty()) {
6514 return;
6516 nscoord minSize = 0;
6517 nscoord maxSize = NS_UNCONSTRAINEDSIZE;
6518 if (aState.mReflowInput) {
6519 auto* ri = aState.mReflowInput;
6520 minSize = mAxis == eLogicalAxisBlock ? ri->ComputedMinBSize()
6521 : ri->ComputedMinISize();
6522 maxSize = mAxis == eLogicalAxisBlock ? ri->ComputedMaxBSize()
6523 : ri->ComputedMaxISize();
6525 Maybe<CopyableAutoTArray<TrackSize, 32>> origSizes;
6526 bool applyMinMax = (minSize != 0 || maxSize != NS_UNCONSTRAINEDSIZE) &&
6527 aAvailableSize == NS_UNCONSTRAINEDSIZE;
6528 // We iterate twice at most. The 2nd time if the grid size changed after
6529 // applying a min/max-size (can only occur if aAvailableSize is indefinite).
6530 while (true) {
6531 float fr = FindUsedFlexFraction(aState, aGridItems, flexTracks, aFunctions,
6532 aAvailableSize);
6533 if (fr != 0.0f) {
6534 for (uint32_t i : flexTracks) {
6535 float flexFactor = aFunctions.MaxSizingFor(i).AsFr();
6536 nscoord flexLength = NSToCoordRound(flexFactor * fr);
6537 nscoord& base = mSizes[i].mBase;
6538 if (flexLength > base) {
6539 if (applyMinMax && origSizes.isNothing()) {
6540 origSizes.emplace(mSizes);
6542 base = flexLength;
6546 if (applyMinMax) {
6547 applyMinMax = false;
6548 // https://drafts.csswg.org/css-grid/#algo-flex-tracks
6549 // "If using this flex fraction would cause the grid to be smaller than
6550 // the grid container’s min-width/height (or larger than the grid
6551 // container’s max-width/height), then redo this step, treating the free
6552 // space as definite [...]"
6553 const auto sumOfGridGaps = SumOfGridGaps();
6554 nscoord newSize = SumOfGridTracks() + sumOfGridGaps;
6555 if (newSize > maxSize) {
6556 aAvailableSize = maxSize;
6557 } else if (newSize < minSize) {
6558 aAvailableSize = minSize;
6560 if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
6561 aAvailableSize = std::max(0, aAvailableSize - sumOfGridGaps);
6562 // Restart with the original track sizes and definite aAvailableSize.
6563 if (origSizes.isSome()) {
6564 mSizes = std::move(*origSizes);
6565 origSizes.reset();
6566 } // else, no mSizes[].mBase were changed above so it's still correct
6567 if (aAvailableSize == 0) {
6568 break; // zero available size wouldn't change any sizes though...
6570 continue;
6573 break;
6577 void nsGridContainerFrame::Tracks::AlignJustifyContent(
6578 const nsStylePosition* aStyle, StyleContentDistribution aAligmentStyleValue,
6579 WritingMode aWM, nscoord aContentBoxSize, bool aIsSubgriddedAxis) {
6580 const bool isAlign = mAxis == eLogicalAxisBlock;
6581 // Align-/justify-content doesn't apply in a subgridded axis.
6582 // Gap properties do apply though so we need to stretch/position the tracks
6583 // to center-align the gaps with the parent's gaps.
6584 if (MOZ_UNLIKELY(aIsSubgriddedAxis)) {
6585 auto& gap = isAlign ? aStyle->mRowGap : aStyle->mColumnGap;
6586 if (gap.IsNormal()) {
6587 return;
6589 auto len = mSizes.Length();
6590 if (len <= 1) {
6591 return;
6593 // This stores the gap deltas between the subgrid gap and the gaps in
6594 // the used track sizes (as encoded in its tracks' mPosition):
6595 nsTArray<nscoord> gapDeltas;
6596 const size_t numGaps = len - 1;
6597 gapDeltas.SetLength(numGaps);
6598 for (size_t i = 0; i < numGaps; ++i) {
6599 TrackSize& sz1 = mSizes[i];
6600 TrackSize& sz2 = mSizes[i + 1];
6601 nscoord currentGap = sz2.mPosition - (sz1.mPosition + sz1.mBase);
6602 gapDeltas[i] = mGridGap - currentGap;
6604 // Recompute the tracks' size/position so that they end up with
6605 // a subgrid-gap centered on the original track gap.
6606 nscoord currentPos = mSizes[0].mPosition;
6607 nscoord lastHalfDelta(0);
6608 for (size_t i = 0; i < numGaps; ++i) {
6609 TrackSize& sz = mSizes[i];
6610 nscoord delta = gapDeltas[i];
6611 nscoord halfDelta;
6612 nscoord roundingError = NSCoordDivRem(delta, 2, &halfDelta);
6613 auto newSize = sz.mBase - (halfDelta + roundingError) - lastHalfDelta;
6614 lastHalfDelta = halfDelta;
6615 if (newSize >= 0) {
6616 sz.mBase = newSize;
6617 sz.mPosition = currentPos;
6618 currentPos += newSize + mGridGap;
6619 } else {
6620 sz.mBase = nscoord(0);
6621 sz.mPosition = currentPos + newSize;
6622 currentPos = sz.mPosition + mGridGap;
6625 auto& lastTrack = mSizes.LastElement();
6626 auto newSize = lastTrack.mBase - lastHalfDelta;
6627 if (newSize >= 0) {
6628 lastTrack.mBase = newSize;
6629 lastTrack.mPosition = currentPos;
6630 } else {
6631 lastTrack.mBase = nscoord(0);
6632 lastTrack.mPosition = currentPos + newSize;
6634 return;
6637 if (mSizes.IsEmpty()) {
6638 return;
6641 bool overflowSafe;
6642 auto alignment = ::GetAlignJustifyValue(aAligmentStyleValue.primary, aWM,
6643 isAlign, &overflowSafe);
6644 if (alignment == StyleAlignFlags::NORMAL) {
6645 alignment = StyleAlignFlags::STRETCH;
6646 // we may need a fallback for 'stretch' below
6647 aAligmentStyleValue = {alignment};
6650 // Compute the free space and count auto-sized tracks.
6651 size_t numAutoTracks = 0;
6652 nscoord space;
6653 if (alignment != StyleAlignFlags::START) {
6654 nscoord trackSizeSum = 0;
6655 if (aIsSubgriddedAxis) {
6656 numAutoTracks = mSizes.Length();
6657 } else {
6658 for (const TrackSize& sz : mSizes) {
6659 trackSizeSum += sz.mBase;
6660 if (sz.mState & TrackSize::eAutoMaxSizing) {
6661 ++numAutoTracks;
6665 space = aContentBoxSize - trackSizeSum - SumOfGridGaps();
6666 // Use the fallback value instead when applicable.
6667 if (space < 0 ||
6668 (alignment == StyleAlignFlags::SPACE_BETWEEN && mSizes.Length() == 1)) {
6669 auto fallback = ::GetAlignJustifyFallbackIfAny(aAligmentStyleValue, aWM,
6670 isAlign, &overflowSafe);
6671 if (fallback) {
6672 alignment = *fallback;
6675 if (space == 0 || (space < 0 && overflowSafe)) {
6676 // XXX check that this makes sense also for [last ]baseline (bug 1151204).
6677 alignment = StyleAlignFlags::START;
6681 // Optimize the cases where we just need to set each track's position.
6682 nscoord pos = 0;
6683 bool distribute = true;
6684 if (alignment == StyleAlignFlags::BASELINE ||
6685 alignment == StyleAlignFlags::LAST_BASELINE) {
6686 NS_WARNING("NYI: 'first/last baseline' (bug 1151204)"); // XXX
6687 alignment = StyleAlignFlags::START;
6689 if (alignment == StyleAlignFlags::START) {
6690 distribute = false;
6691 } else if (alignment == StyleAlignFlags::END) {
6692 pos = space;
6693 distribute = false;
6694 } else if (alignment == StyleAlignFlags::CENTER) {
6695 pos = space / 2;
6696 distribute = false;
6697 } else if (alignment == StyleAlignFlags::STRETCH) {
6698 distribute = numAutoTracks != 0;
6700 if (!distribute) {
6701 for (TrackSize& sz : mSizes) {
6702 sz.mPosition = pos;
6703 pos += sz.mBase + mGridGap;
6705 return;
6708 // Distribute free space to/between tracks and set their position.
6709 MOZ_ASSERT(space > 0, "should've handled that on the fallback path above");
6710 nscoord between, roundingError;
6711 if (alignment == StyleAlignFlags::STRETCH) {
6712 MOZ_ASSERT(numAutoTracks > 0, "we handled numAutoTracks == 0 above");
6713 // The outer loop typically only runs once - it repeats only in a masonry
6714 // axis when some stretchable items reach their `max-size`.
6715 // It's O(n^2) worst case; if all items are stretchable with a `max-size`
6716 // and exactly one item reaches its `max-size` each round.
6717 while (space) {
6718 pos = 0;
6719 nscoord spacePerTrack;
6720 roundingError = NSCoordDivRem(space, numAutoTracks, &spacePerTrack);
6721 space = 0;
6722 for (TrackSize& sz : mSizes) {
6723 sz.mPosition = pos;
6724 if (!(sz.mState & TrackSize::eAutoMaxSizing)) {
6725 pos += sz.mBase + mGridGap;
6726 continue;
6728 nscoord stretch = spacePerTrack;
6729 if (roundingError) {
6730 roundingError -= 1;
6731 stretch += 1;
6733 nscoord newBase = sz.mBase + stretch;
6734 if (mIsMasonry && (sz.mState & TrackSize::eClampToLimit)) {
6735 auto clampedSize = std::min(newBase, sz.mLimit);
6736 auto sizeOverLimit = newBase - clampedSize;
6737 if (sizeOverLimit > 0) {
6738 newBase = clampedSize;
6739 sz.mState &= ~(sz.mState & TrackSize::eAutoMaxSizing);
6740 // This repeats the outer loop to distribute the superfluous space:
6741 space += sizeOverLimit;
6742 if (--numAutoTracks == 0) {
6743 // ... except if we don't have any stretchable items left.
6744 space = 0;
6748 sz.mBase = newBase;
6749 pos += newBase + mGridGap;
6752 MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
6753 return;
6755 if (alignment == StyleAlignFlags::SPACE_BETWEEN) {
6756 MOZ_ASSERT(mSizes.Length() > 1, "should've used a fallback above");
6757 roundingError = NSCoordDivRem(space, mSizes.Length() - 1, &between);
6758 } else if (alignment == StyleAlignFlags::SPACE_AROUND) {
6759 roundingError = NSCoordDivRem(space, mSizes.Length(), &between);
6760 pos = between / 2;
6761 } else if (alignment == StyleAlignFlags::SPACE_EVENLY) {
6762 roundingError = NSCoordDivRem(space, mSizes.Length() + 1, &between);
6763 pos = between;
6764 } else {
6765 MOZ_ASSERT_UNREACHABLE("unknown align-/justify-content value");
6766 between = 0; // just to avoid a compiler warning
6767 roundingError = 0; // just to avoid a compiler warning
6769 between += mGridGap;
6770 for (TrackSize& sz : mSizes) {
6771 sz.mPosition = pos;
6772 nscoord spacing = between;
6773 if (roundingError) {
6774 roundingError -= 1;
6775 spacing += 1;
6777 pos += sz.mBase + spacing;
6779 MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
6782 void nsGridContainerFrame::LineRange::ToPositionAndLength(
6783 const nsTArray<TrackSize>& aTrackSizes, nscoord* aPos,
6784 nscoord* aLength) const {
6785 MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
6786 "expected a definite LineRange");
6787 MOZ_ASSERT(mStart < mEnd);
6788 nscoord startPos = aTrackSizes[mStart].mPosition;
6789 const TrackSize& sz = aTrackSizes[mEnd - 1];
6790 *aPos = startPos;
6791 *aLength = (sz.mPosition + sz.mBase) - startPos;
6794 nscoord nsGridContainerFrame::LineRange::ToLength(
6795 const nsTArray<TrackSize>& aTrackSizes) const {
6796 MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
6797 "expected a definite LineRange");
6798 MOZ_ASSERT(mStart < mEnd);
6799 nscoord startPos = aTrackSizes[mStart].mPosition;
6800 const TrackSize& sz = aTrackSizes[mEnd - 1];
6801 return (sz.mPosition + sz.mBase) - startPos;
6804 void nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos(
6805 const Tracks& aTracks, nscoord aGridOrigin, nscoord* aPos,
6806 nscoord* aLength) const {
6807 // kAutoLine for abspos children contributes the corresponding edge
6808 // of the grid container's padding-box.
6809 if (mEnd == kAutoLine) {
6810 if (mStart == kAutoLine) {
6811 // done
6812 } else {
6813 const nscoord endPos = *aPos + *aLength;
6814 auto side = mStart == aTracks.mSizes.Length()
6815 ? GridLineSide::BeforeGridGap
6816 : GridLineSide::AfterGridGap;
6817 nscoord startPos = aTracks.GridLineEdge(mStart, side);
6818 *aPos = aGridOrigin + startPos;
6819 *aLength = std::max(endPos - *aPos, 0);
6821 } else {
6822 if (mStart == kAutoLine) {
6823 auto side =
6824 mEnd == 0 ? GridLineSide::AfterGridGap : GridLineSide::BeforeGridGap;
6825 nscoord endPos = aTracks.GridLineEdge(mEnd, side);
6826 *aLength = std::max(aGridOrigin + endPos, 0);
6827 } else if (MOZ_LIKELY(mStart != mEnd)) {
6828 nscoord pos;
6829 ToPositionAndLength(aTracks.mSizes, &pos, aLength);
6830 *aPos = aGridOrigin + pos;
6831 } else {
6832 // The grid area only covers removed 'auto-fit' tracks.
6833 nscoord pos = aTracks.GridLineEdge(mStart, GridLineSide::BeforeGridGap);
6834 *aPos = aGridOrigin + pos;
6835 *aLength = nscoord(0);
6840 LogicalSize nsGridContainerFrame::GridReflowInput::PercentageBasisFor(
6841 LogicalAxis aAxis, const GridItemInfo& aGridItem) const {
6842 auto wm = aGridItem.mFrame->GetWritingMode();
6843 const auto* itemParent = aGridItem.mFrame->GetParent();
6844 if (MOZ_UNLIKELY(itemParent != mFrame)) {
6845 // The item comes from a descendant subgrid. Use the subgrid's
6846 // used track sizes to resolve the grid area size, if present.
6847 MOZ_ASSERT(itemParent->IsGridContainerFrame());
6848 auto* subgridFrame = static_cast<const nsGridContainerFrame*>(itemParent);
6849 MOZ_ASSERT(subgridFrame->IsSubgrid());
6850 if (auto* uts = subgridFrame->GetUsedTrackSizes()) {
6851 auto subgridWM = subgridFrame->GetWritingMode();
6852 LogicalSize cbSize(subgridWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
6853 if (!subgridFrame->IsSubgrid(eLogicalAxisInline) &&
6854 uts->mCanResolveLineRangeSize[eLogicalAxisInline]) {
6855 // NOTE: At this point aGridItem.mArea is in this->mFrame coordinates
6856 // and thus may have been transposed. The range values in a non-
6857 // subgridded axis still has its original values in subgridFrame's
6858 // coordinates though.
6859 auto rangeAxis = subgridWM.IsOrthogonalTo(mWM) ? eLogicalAxisBlock
6860 : eLogicalAxisInline;
6861 const auto& range = aGridItem.mArea.LineRangeForAxis(rangeAxis);
6862 cbSize.ISize(subgridWM) =
6863 range.ToLength(uts->mSizes[eLogicalAxisInline]);
6865 if (!subgridFrame->IsSubgrid(eLogicalAxisBlock) &&
6866 uts->mCanResolveLineRangeSize[eLogicalAxisBlock]) {
6867 auto rangeAxis = subgridWM.IsOrthogonalTo(mWM) ? eLogicalAxisInline
6868 : eLogicalAxisBlock;
6869 const auto& range = aGridItem.mArea.LineRangeForAxis(rangeAxis);
6870 cbSize.BSize(subgridWM) =
6871 range.ToLength(uts->mSizes[eLogicalAxisBlock]);
6873 return cbSize.ConvertTo(wm, subgridWM);
6876 return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
6879 if (aAxis == eLogicalAxisInline || !mCols.mCanResolveLineRangeSize) {
6880 return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
6882 // Note: for now, we only resolve transferred percentages to row sizing.
6883 // We may need to adjust these assertions once we implement bug 1300366.
6884 MOZ_ASSERT(!mRows.mCanResolveLineRangeSize);
6885 nscoord colSize = aGridItem.mArea.mCols.ToLength(mCols.mSizes);
6886 nscoord rowSize = NS_UNCONSTRAINEDSIZE;
6887 return !wm.IsOrthogonalTo(mWM) ? LogicalSize(wm, colSize, rowSize)
6888 : LogicalSize(wm, rowSize, colSize);
6891 LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockFor(
6892 const GridArea& aArea) const {
6893 nscoord i, b, iSize, bSize;
6894 MOZ_ASSERT(aArea.mCols.Extent() > 0, "grid items cover at least one track");
6895 MOZ_ASSERT(aArea.mRows.Extent() > 0, "grid items cover at least one track");
6896 aArea.mCols.ToPositionAndLength(mCols.mSizes, &i, &iSize);
6897 aArea.mRows.ToPositionAndLength(mRows.mSizes, &b, &bSize);
6898 return LogicalRect(mWM, i, b, iSize, bSize);
6901 LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockForAbsPos(
6902 const GridArea& aArea, const LogicalPoint& aGridOrigin,
6903 const LogicalRect& aGridCB) const {
6904 nscoord i = aGridCB.IStart(mWM);
6905 nscoord b = aGridCB.BStart(mWM);
6906 nscoord iSize = aGridCB.ISize(mWM);
6907 nscoord bSize = aGridCB.BSize(mWM);
6908 aArea.mCols.ToPositionAndLengthForAbsPos(mCols, aGridOrigin.I(mWM), &i,
6909 &iSize);
6910 aArea.mRows.ToPositionAndLengthForAbsPos(mRows, aGridOrigin.B(mWM), &b,
6911 &bSize);
6912 return LogicalRect(mWM, i, b, iSize, bSize);
6915 void nsGridContainerFrame::GridReflowInput::AlignJustifyContentInMasonryAxis(
6916 nscoord aMasonryBoxSize, nscoord aContentBoxSize) {
6917 if (aContentBoxSize == NS_UNCONSTRAINEDSIZE) {
6918 aContentBoxSize = aMasonryBoxSize;
6920 auto& masonryAxisTracks = mRows.mIsMasonry ? mRows : mCols;
6921 MOZ_ASSERT(masonryAxisTracks.mSizes.Length() == 2,
6922 "unexpected masonry axis tracks");
6923 const auto masonryAxis = masonryAxisTracks.mAxis;
6924 const auto contentAlignment = mGridStyle->UsedContentAlignment(masonryAxis);
6925 if (contentAlignment.primary == StyleAlignFlags::NORMAL ||
6926 contentAlignment.primary == StyleAlignFlags::STRETCH) {
6927 // Stretch the "masonry box" to the full content box if it's smaller.
6928 nscoord cbSize = std::max(aMasonryBoxSize, aContentBoxSize);
6929 for (auto& sz : masonryAxisTracks.mSizes) {
6930 sz.mBase = cbSize;
6932 return;
6935 // Save our current track sizes; replace them with one track sized to
6936 // the masonry box and align that within our content box.
6937 auto savedTrackSizes(std::move(masonryAxisTracks.mSizes));
6938 masonryAxisTracks.mSizes.AppendElement(savedTrackSizes[0]);
6939 masonryAxisTracks.mSizes[0].mBase = aMasonryBoxSize;
6940 masonryAxisTracks.AlignJustifyContent(mGridStyle, contentAlignment, mWM,
6941 aContentBoxSize, false);
6942 nscoord masonryBoxOffset = masonryAxisTracks.mSizes[0].mPosition;
6943 // Restore the original track sizes...
6944 masonryAxisTracks.mSizes = std::move(savedTrackSizes);
6945 // ...then reposition and resize all of them to the aligned result.
6946 for (auto& sz : masonryAxisTracks.mSizes) {
6947 sz.mPosition = masonryBoxOffset;
6948 sz.mBase = aMasonryBoxSize;
6952 // Note: this is called after all items have been positioned/reflowed.
6953 // The masonry-axis tracks have the size of the "masonry box" at this point
6954 // and are positioned according to 'align/justify-content'.
6955 void nsGridContainerFrame::GridReflowInput::AlignJustifyTracksInMasonryAxis(
6956 const LogicalSize& aContentSize, const nsSize& aContainerSize) {
6957 auto& masonryAxisTracks = mRows.mIsMasonry ? mRows : mCols;
6958 MOZ_ASSERT(masonryAxisTracks.mSizes.Length() == 2,
6959 "unexpected masonry axis tracks");
6960 const auto masonryAxis = masonryAxisTracks.mAxis;
6961 auto gridAxis = GetOrthogonalAxis(masonryAxis);
6962 auto& gridAxisTracks = TracksFor(gridAxis);
6963 AutoTArray<TrackSize, 32> savedSizes;
6964 savedSizes.AppendElements(masonryAxisTracks.mSizes);
6965 auto wm = mWM;
6966 nscoord contentAreaStart = mBorderPadding.Start(masonryAxis, wm);
6967 // The offset to the "masonry box" from our content-box start edge.
6968 nscoord masonryBoxOffset = masonryAxisTracks.mSizes[0].mPosition;
6969 nscoord alignmentContainerSize = masonryAxisTracks.mSizes[0].mBase;
6971 for (auto i : IntegerRange(gridAxisTracks.mSizes.Length())) {
6972 auto tracksAlignment = mGridStyle->UsedTracksAlignment(masonryAxis, i);
6973 if (tracksAlignment.primary != StyleAlignFlags::START) {
6974 masonryAxisTracks.mSizes.ClearAndRetainStorage();
6975 for (const auto& item : mGridItems) {
6976 if (item.mArea.LineRangeForAxis(gridAxis).mStart == i) {
6977 const auto* child = item.mFrame;
6978 LogicalRect rect = child->GetLogicalRect(wm, aContainerSize);
6979 TrackSize sz = {0, 0, 0, {0, 0}, TrackSize::StateBits(0)};
6980 const auto& margin = child->GetLogicalUsedMargin(wm);
6981 sz.mPosition = rect.Start(masonryAxis, wm) -
6982 margin.Start(masonryAxis, wm) - contentAreaStart;
6983 sz.mBase =
6984 rect.Size(masonryAxis, wm) + margin.StartEnd(masonryAxis, wm);
6985 // Account for a align-self baseline offset on the end side.
6986 // XXXmats hmm, it seems it would be a lot simpler to just store
6987 // these baseline adjustments into the UsedMarginProperty instead
6988 auto state = item.mState[masonryAxis];
6989 if ((state & ItemState::eSelfBaseline) &&
6990 (state & ItemState::eEndSideBaseline)) {
6991 sz.mBase += item.mBaselineOffset[masonryAxis];
6993 if (tracksAlignment.primary == StyleAlignFlags::STRETCH) {
6994 const auto* pos = child->StylePosition();
6995 auto itemAlignment =
6996 pos->UsedSelfAlignment(masonryAxis, mFrame->Style());
6997 if (child->StyleMargin()->HasAuto(masonryAxis, wm)) {
6998 sz.mState |= TrackSize::eAutoMaxSizing;
6999 sz.mState |= TrackSize::eItemHasAutoMargin;
7000 } else if (pos->Size(masonryAxis, wm).IsAuto() &&
7001 (itemAlignment == StyleAlignFlags::NORMAL ||
7002 itemAlignment == StyleAlignFlags::STRETCH)) {
7003 sz.mState |= TrackSize::eAutoMaxSizing;
7004 sz.mState |= TrackSize::eItemStretchSize;
7005 const auto& max = pos->MaxSize(masonryAxis, wm);
7006 if (max.ConvertsToLength()) { // XXX deal with percentages
7007 // XXX add in baselineOffset ? use actual frame size - content
7008 // size?
7009 nscoord boxSizingAdjust =
7010 child->GetLogicalUsedBorderAndPadding(wm).StartEnd(
7011 masonryAxis, wm);
7012 if (pos->mBoxSizing == StyleBoxSizing::Border) {
7013 boxSizingAdjust = 0;
7015 sz.mLimit = nsLayoutUtils::ComputeBSizeValue(
7016 aContentSize.Size(masonryAxis, wm), boxSizingAdjust,
7017 max.AsLengthPercentage());
7018 sz.mLimit += margin.StartEnd(masonryAxis, wm);
7019 sz.mState |= TrackSize::eClampToLimit;
7023 masonryAxisTracks.mSizes.AppendElement(std::move(sz));
7026 masonryAxisTracks.AlignJustifyContent(mGridStyle, tracksAlignment, wm,
7027 alignmentContainerSize, false);
7028 auto iter = mGridItems.begin();
7029 auto end = mGridItems.end();
7030 // We limit the loop to the number of items we found in the current
7031 // grid-axis axis track (in the outer loop) as an optimization.
7032 for (auto r : IntegerRange(masonryAxisTracks.mSizes.Length())) {
7033 GridItemInfo* item = nullptr;
7034 auto& sz = masonryAxisTracks.mSizes[r];
7035 // Find the next item in the current grid-axis axis track.
7036 for (; iter != end; ++iter) {
7037 if (iter->mArea.LineRangeForAxis(gridAxis).mStart == i) {
7038 item = &*iter;
7039 ++iter;
7040 break;
7043 nsIFrame* child = item->mFrame;
7044 const auto childWM = child->GetWritingMode();
7045 auto masonryChildAxis =
7046 childWM.IsOrthogonalTo(wm) ? gridAxis : masonryAxis;
7047 LogicalMargin margin = child->GetLogicalUsedMargin(childWM);
7048 bool forceReposition = false;
7049 if (sz.mState & TrackSize::eItemStretchSize) {
7050 auto size = child->GetLogicalSize().Size(masonryChildAxis, childWM);
7051 auto newSize = sz.mBase - margin.StartEnd(masonryChildAxis, childWM);
7052 if (size != newSize) {
7053 // XXX need to pass aIMinSizeClamp aBMinSizeClamp ?
7054 LogicalSize cb =
7055 ContainingBlockFor(item->mArea).Size(wm).ConvertTo(childWM, wm);
7056 LogicalSize availableSize = cb;
7057 cb.Size(masonryChildAxis, childWM) = alignmentContainerSize;
7058 availableSize.Size(eLogicalAxisBlock, childWM) =
7059 NS_UNCONSTRAINEDSIZE;
7060 const auto& bp = child->GetLogicalUsedBorderAndPadding(childWM);
7061 newSize -= bp.StartEnd(masonryChildAxis, childWM);
7062 ::PostReflowStretchChild(child, *mReflowInput, availableSize, cb,
7063 masonryChildAxis, newSize);
7064 if (childWM.IsPhysicalRTL()) {
7065 // The NormalPosition of this child is frame-size dependent so we
7066 // need to reset its stored position below.
7067 forceReposition = true;
7070 } else if (sz.mState & TrackSize::eItemHasAutoMargin) {
7071 // Re-compute the auto-margin(s) in the masonry axis.
7072 auto size = child->GetLogicalSize().Size(masonryChildAxis, childWM);
7073 auto spaceToFill = sz.mBase - size;
7074 if (spaceToFill > nscoord(0)) {
7075 const auto& marginStyle = child->StyleMargin();
7076 if (marginStyle->mMargin.Start(masonryChildAxis, childWM)
7077 .IsAuto()) {
7078 if (marginStyle->mMargin.End(masonryChildAxis, childWM)
7079 .IsAuto()) {
7080 nscoord half;
7081 nscoord roundingError = NSCoordDivRem(spaceToFill, 2, &half);
7082 margin.Start(masonryChildAxis, childWM) = half;
7083 margin.End(masonryChildAxis, childWM) = half + roundingError;
7084 } else {
7085 margin.Start(masonryChildAxis, childWM) = spaceToFill;
7087 } else {
7088 MOZ_ASSERT(
7089 marginStyle->mMargin.End(masonryChildAxis, childWM).IsAuto());
7090 margin.End(masonryChildAxis, childWM) = spaceToFill;
7092 nsMargin* propValue =
7093 child->GetProperty(nsIFrame::UsedMarginProperty());
7094 if (propValue) {
7095 *propValue = margin.GetPhysicalMargin(childWM);
7096 } else {
7097 child->AddProperty(
7098 nsIFrame::UsedMarginProperty(),
7099 new nsMargin(margin.GetPhysicalMargin(childWM)));
7103 nscoord newPos = contentAreaStart + masonryBoxOffset + sz.mPosition +
7104 margin.Start(masonryChildAxis, childWM);
7105 LogicalPoint pos = child->GetLogicalNormalPosition(wm, aContainerSize);
7106 auto delta = newPos - pos.Pos(masonryAxis, wm);
7107 if (delta != 0 || forceReposition) {
7108 LogicalPoint logicalDelta(wm);
7109 logicalDelta.Pos(masonryAxis, wm) = delta;
7110 child->MovePositionBy(wm, logicalDelta);
7113 } else if (masonryBoxOffset != nscoord(0)) {
7114 // TODO move placeholders too
7115 auto delta = masonryBoxOffset;
7116 LogicalPoint logicalDelta(wm);
7117 logicalDelta.Pos(masonryAxis, wm) = delta;
7118 for (const auto& item : mGridItems) {
7119 if (item.mArea.LineRangeForAxis(gridAxis).mStart != i) {
7120 continue;
7122 item.mFrame->MovePositionBy(wm, logicalDelta);
7126 masonryAxisTracks.mSizes = std::move(savedSizes);
7130 * Return a Fragmentainer object if we have a fragmentainer frame in our
7131 * ancestor chain of containing block (CB) reflow inputs. We'll only
7132 * continue traversing the ancestor chain as long as the CBs have
7133 * the same writing-mode and have overflow:visible.
7135 Maybe<nsGridContainerFrame::Fragmentainer>
7136 nsGridContainerFrame::GetNearestFragmentainer(
7137 const GridReflowInput& aState) const {
7138 Maybe<nsGridContainerFrame::Fragmentainer> data;
7139 const ReflowInput* gridRI = aState.mReflowInput;
7140 if (gridRI->AvailableBSize() == NS_UNCONSTRAINEDSIZE && !GetPrevInFlow()) {
7141 return data;
7143 WritingMode wm = aState.mWM;
7144 const ReflowInput* cbRI = gridRI->mCBReflowInput;
7145 for (; cbRI; cbRI = cbRI->mCBReflowInput) {
7146 nsIScrollableFrame* sf = do_QueryFrame(cbRI->mFrame);
7147 if (sf) {
7148 break;
7150 if (wm.IsOrthogonalTo(cbRI->GetWritingMode())) {
7151 break;
7153 LayoutFrameType frameType = cbRI->mFrame->Type();
7154 if ((frameType == LayoutFrameType::Canvas &&
7155 PresContext()->IsPaginated()) ||
7156 frameType == LayoutFrameType::ColumnSet) {
7157 data.emplace();
7158 data->mIsTopOfPage = gridRI->mFlags.mIsTopOfPage;
7159 if (gridRI->AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
7160 data->mToFragmentainerEnd = aState.mFragBStart +
7161 gridRI->AvailableBSize() -
7162 aState.mBorderPadding.BStart(wm);
7163 } else {
7164 // This occurs when nsColumnSetFrame reflows its last column in
7165 // unconstrained available block-size.
7166 data->mToFragmentainerEnd = NS_UNCONSTRAINEDSIZE;
7168 const auto numRows = aState.mRows.mSizes.Length();
7169 data->mCanBreakAtStart =
7170 numRows > 0 && aState.mRows.mSizes[0].mPosition > 0;
7171 nscoord bSize = gridRI->ComputedBSize();
7172 data->mIsAutoBSize = bSize == NS_UNCONSTRAINEDSIZE;
7173 if (data->mIsAutoBSize) {
7174 bSize = gridRI->ComputedMinBSize();
7175 } else {
7176 bSize = NS_CSS_MINMAX(bSize, gridRI->ComputedMinBSize(),
7177 gridRI->ComputedMaxBSize());
7179 nscoord gridEnd =
7180 aState.mRows.GridLineEdge(numRows, GridLineSide::BeforeGridGap);
7181 data->mCanBreakAtEnd = bSize > gridEnd && bSize > aState.mFragBStart;
7182 break;
7185 return data;
7188 void nsGridContainerFrame::ReflowInFlowChild(
7189 nsIFrame* aChild, const GridItemInfo* aGridItemInfo, nsSize aContainerSize,
7190 const Maybe<nscoord>& aStretchBSize, const Fragmentainer* aFragmentainer,
7191 const GridReflowInput& aState, const LogicalRect& aContentArea,
7192 ReflowOutput& aDesiredSize, nsReflowStatus& aStatus) {
7193 nsPresContext* pc = PresContext();
7194 ComputedStyle* containerSC = Style();
7195 WritingMode wm = aState.mReflowInput->GetWritingMode();
7196 const bool isGridItem = !!aGridItemInfo;
7197 MOZ_ASSERT(isGridItem == !aChild->IsPlaceholderFrame());
7198 LogicalRect cb(wm);
7199 WritingMode childWM = aChild->GetWritingMode();
7200 bool isConstrainedBSize = false;
7201 nscoord toFragmentainerEnd;
7202 // The part of the child's grid area that's in previous container fragments.
7203 nscoord consumedGridAreaBSize = 0;
7204 const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
7205 if (MOZ_LIKELY(isGridItem)) {
7206 MOZ_ASSERT(aGridItemInfo->mFrame == aChild);
7207 const GridArea& area = aGridItemInfo->mArea;
7208 MOZ_ASSERT(area.IsDefinite());
7209 cb = aState.ContainingBlockFor(area);
7210 if (aFragmentainer && !wm.IsOrthogonalTo(childWM)) {
7211 // |gridAreaBOffset| is the offset of the child's grid area in this
7212 // container fragment (if negative, that distance is the child CB size
7213 // consumed in previous container fragments). Note that cb.BStart
7214 // (initially) and aState.mFragBStart are in "global" grid coordinates
7215 // (like all track positions).
7216 nscoord gridAreaBOffset = cb.BStart(wm) - aState.mFragBStart;
7217 consumedGridAreaBSize = std::max(0, -gridAreaBOffset);
7218 cb.BStart(wm) = std::max(0, gridAreaBOffset);
7219 if (aFragmentainer->mToFragmentainerEnd != NS_UNCONSTRAINEDSIZE) {
7220 toFragmentainerEnd = aFragmentainer->mToFragmentainerEnd -
7221 aState.mFragBStart - cb.BStart(wm);
7222 toFragmentainerEnd = std::max(toFragmentainerEnd, 0);
7223 isConstrainedBSize = true;
7226 cb += aContentArea.Origin(wm);
7227 aState.mRows.AlignBaselineSubtree(*aGridItemInfo);
7228 aState.mCols.AlignBaselineSubtree(*aGridItemInfo);
7229 // Setup [align|justify]-content:[last ]baseline related frame properties.
7230 // These are added to the padding in SizeComputationInput::InitOffsets.
7231 // (a negative value signals the value is for 'last baseline' and should be
7232 // added to the (logical) end padding)
7233 typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop;
7234 auto SetProp = [aGridItemInfo, aChild](LogicalAxis aGridAxis, Prop aProp) {
7235 auto state = aGridItemInfo->mState[aGridAxis];
7236 auto baselineAdjust = (state & ItemState::eContentBaseline)
7237 ? aGridItemInfo->mBaselineOffset[aGridAxis]
7238 : nscoord(0);
7239 if (baselineAdjust < nscoord(0)) {
7240 // This happens when the subtree overflows its track.
7241 // XXX spec issue? it's unclear how to handle this.
7242 baselineAdjust = nscoord(0);
7243 } else if (GridItemInfo::BaselineAlignmentAffectsEndSide(state)) {
7244 baselineAdjust = -baselineAdjust;
7246 if (baselineAdjust != nscoord(0)) {
7247 aChild->SetProperty(aProp, baselineAdjust);
7248 } else {
7249 aChild->RemoveProperty(aProp);
7252 SetProp(eLogicalAxisBlock,
7253 isOrthogonal ? IBaselinePadProperty() : BBaselinePadProperty());
7254 SetProp(eLogicalAxisInline,
7255 isOrthogonal ? BBaselinePadProperty() : IBaselinePadProperty());
7256 } else {
7257 // By convention, for frames that perform CSS Box Alignment, we position
7258 // placeholder children at the start corner of their alignment container,
7259 // and in this case that's usually the grid's content-box.
7260 // ("Usually" - the exception is when the grid *also* forms the
7261 // abs.pos. containing block. In that case, the alignment container isn't
7262 // the content-box -- it's some grid area instead. But that case doesn't
7263 // require any special handling here, because we handle it later using a
7264 // special flag (ReflowInput::InitFlag::StaticPosIsCBOrigin) which will make
7265 // us ignore the placeholder's position entirely.)
7266 cb = aContentArea;
7267 aChild->AddStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN);
7270 LogicalSize reflowSize(cb.Size(wm));
7271 if (isConstrainedBSize) {
7272 reflowSize.BSize(wm) = toFragmentainerEnd;
7274 LogicalSize childCBSize = reflowSize.ConvertTo(childWM, wm);
7276 // Setup the ClampMarginBoxMinSize reflow flags and property, if needed.
7277 ComputeSizeFlags csFlags;
7278 if (aGridItemInfo) {
7279 // AlignJustifyTracksInMasonryAxis stretches items in a masonry-axis so we
7280 // don't do that here.
7281 auto* pos = aChild->StylePosition();
7282 auto j = IsMasonry(eLogicalAxisInline) ? StyleAlignFlags::START
7283 : pos->UsedJustifySelf(Style())._0;
7284 auto a = IsMasonry(eLogicalAxisBlock) ? StyleAlignFlags::START
7285 : pos->UsedAlignSelf(Style())._0;
7286 bool stretch[2];
7287 stretch[eLogicalAxisInline] =
7288 j == StyleAlignFlags::NORMAL || j == StyleAlignFlags::STRETCH;
7289 stretch[eLogicalAxisBlock] =
7290 a == StyleAlignFlags::NORMAL || a == StyleAlignFlags::STRETCH;
7291 auto childIAxis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
7292 // Clamp during reflow if we're stretching in that axis.
7293 if (stretch[childIAxis]) {
7294 if (aGridItemInfo->mState[childIAxis] &
7295 ItemState::eClampMarginBoxMinSize) {
7296 csFlags += ComputeSizeFlag::IClampMarginBoxMinSize;
7298 } else {
7299 csFlags += ComputeSizeFlag::ShrinkWrap;
7302 auto childBAxis = GetOrthogonalAxis(childIAxis);
7303 if (stretch[childBAxis] &&
7304 aGridItemInfo->mState[childBAxis] & ItemState::eClampMarginBoxMinSize) {
7305 csFlags += ComputeSizeFlag::BClampMarginBoxMinSize;
7306 aChild->SetProperty(BClampMarginBoxMinSizeProperty(),
7307 childCBSize.BSize(childWM));
7308 } else {
7309 aChild->RemoveProperty(BClampMarginBoxMinSizeProperty());
7312 if ((aGridItemInfo->mState[childIAxis] & ItemState::eApplyAutoMinSize)) {
7313 csFlags += ComputeSizeFlag::IApplyAutoMinSize;
7317 if (!isConstrainedBSize) {
7318 childCBSize.BSize(childWM) = NS_UNCONSTRAINEDSIZE;
7320 LogicalSize percentBasis(cb.Size(wm).ConvertTo(childWM, wm));
7321 ReflowInput childRI(pc, *aState.mReflowInput, aChild, childCBSize,
7322 Some(percentBasis), {}, {}, csFlags);
7323 childRI.mFlags.mIsTopOfPage =
7324 aFragmentainer ? aFragmentainer->mIsTopOfPage : false;
7326 // Because we pass ComputeSizeFlag::UseAutoBSize, and the
7327 // previous reflow of the child might not have, set the child's
7328 // block-resize flag to true.
7329 // FIXME (perf): It would be faster to do this only if the previous
7330 // reflow of the child was a measuring reflow, and only if the child
7331 // does some of the things that are affected by
7332 // ComputeSizeFlag::UseAutoBSize.
7333 childRI.SetBResize(true);
7334 childRI.mFlags.mIsBResizeForPercentages = true;
7336 // If the child is stretching in its block axis, and we might be fragmenting
7337 // it in that axis, then setup a frame property to tell
7338 // nsBlockFrame::ComputeFinalSize the size.
7339 if (isConstrainedBSize && !wm.IsOrthogonalTo(childWM)) {
7340 bool stretch = false;
7341 if (!childRI.mStyleMargin->HasBlockAxisAuto(childWM) &&
7342 childRI.mStylePosition->BSize(childWM).IsAuto()) {
7343 auto blockAxisAlignment = childRI.mStylePosition->UsedAlignSelf(Style());
7344 if (!IsMasonry(eLogicalAxisBlock) &&
7345 (blockAxisAlignment._0 == StyleAlignFlags::NORMAL ||
7346 blockAxisAlignment._0 == StyleAlignFlags::STRETCH)) {
7347 stretch = true;
7350 if (stretch) {
7351 aChild->SetProperty(FragStretchBSizeProperty(), *aStretchBSize);
7352 } else {
7353 aChild->RemoveProperty(FragStretchBSizeProperty());
7357 // We need the width of the child before we can correctly convert
7358 // the writing-mode of its origin, so we reflow at (0, 0) using a dummy
7359 // aContainerSize, and then pass the correct position to FinishReflowChild.
7360 ReflowOutput childSize(childRI);
7361 const nsSize dummyContainerSize;
7363 // XXXdholbert The childPos that we use for ReflowChild shouldn't matter,
7364 // since we finalize it in FinishReflowChild. However, it does matter if the
7365 // child happens to be XUL (which sizes menu popup frames based on the
7366 // position within the viewport, during this ReflowChild call). So we make an
7367 // educated guess that the child will be at the origin of its containing
7368 // block, and then use align/justify to correct that as-needed further
7369 // down. (If the child has a different writing mode than its parent, though,
7370 // then we can't express the CB origin until we've reflowed the child and
7371 // determined its size. In that case, we throw up our hands and don't bother
7372 // trying to guess the position up-front after all.)
7373 // XXXdholbert We'll remove this special case in bug 1600542, and then we can
7374 // go back to just setting childPos in a single call after ReflowChild.
7375 LogicalPoint childPos(childWM);
7376 if (MOZ_LIKELY(childWM == wm)) {
7377 // Initially, assume the child will be at the containing block origin.
7378 // (This may get corrected during alignment/justification below.)
7379 childPos = cb.Origin(wm);
7381 ReflowChild(aChild, pc, childSize, childRI, childWM, childPos,
7382 dummyContainerSize, ReflowChildFlags::Default, aStatus);
7383 if (MOZ_UNLIKELY(childWM != wm)) {
7384 // As above: assume the child will be at the containing block origin.
7385 // (which we can now compute in terms of the childWM, now that we know the
7386 // child's size).
7387 childPos = cb.Origin(wm).ConvertTo(
7388 childWM, wm, aContainerSize - childSize.PhysicalSize());
7390 // Apply align/justify-self and reflow again if that affects the size.
7391 if (MOZ_LIKELY(isGridItem)) {
7392 LogicalSize size = childSize.Size(childWM); // from the ReflowChild()
7393 auto applyItemSelfAlignment = [&](LogicalAxis aAxis, nscoord aCBSize) {
7394 auto align =
7395 childRI.mStylePosition->UsedSelfAlignment(aAxis, containerSC);
7396 auto state = aGridItemInfo->mState[aAxis];
7397 auto flags = AlignJustifyFlags::NoFlags;
7398 if (IsMasonry(aAxis)) {
7399 // In a masonry axis, we inhibit applying 'stretch' and auto-margins
7400 // here since AlignJustifyTracksInMasonryAxis deals with that.
7401 // The only other {align,justify}-{self,content} values that have an
7402 // effect are '[last] baseline', the rest behave as 'start'.
7403 if (MOZ_LIKELY(!(state & ItemState::eSelfBaseline))) {
7404 align = {StyleAlignFlags::START};
7405 } else {
7406 auto group = (state & ItemState::eFirstBaseline)
7407 ? BaselineSharingGroup::First
7408 : BaselineSharingGroup::Last;
7409 auto itemStart = aGridItemInfo->mArea.LineRangeForAxis(aAxis).mStart;
7410 aCBSize = aState.TracksFor(aAxis)
7411 .mSizes[itemStart]
7412 .mBaselineSubtreeSize[group];
7414 flags = AlignJustifyFlags::IgnoreAutoMargins;
7415 } else if (state & ItemState::eContentBaseline) {
7416 align = {(state & ItemState::eFirstBaseline)
7417 ? StyleAlignFlags::SELF_START
7418 : StyleAlignFlags::SELF_END};
7420 if (aAxis == eLogicalAxisBlock) {
7421 AlignSelf(*aGridItemInfo, align, aCBSize, wm, childRI, size, flags,
7422 &childPos);
7423 } else {
7424 JustifySelf(*aGridItemInfo, align, aCBSize, wm, childRI, size, flags,
7425 &childPos);
7428 if (aStatus.IsComplete()) {
7429 applyItemSelfAlignment(eLogicalAxisBlock,
7430 cb.BSize(wm) - consumedGridAreaBSize);
7432 applyItemSelfAlignment(eLogicalAxisInline, cb.ISize(wm));
7433 } // else, nsAbsoluteContainingBlock.cpp will handle align/justify-self.
7435 FinishReflowChild(aChild, pc, childSize, &childRI, childWM, childPos,
7436 aContainerSize, ReflowChildFlags::ApplyRelativePositioning);
7437 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, aChild);
7440 nscoord nsGridContainerFrame::ReflowInFragmentainer(
7441 GridReflowInput& aState, const LogicalRect& aContentArea,
7442 ReflowOutput& aDesiredSize, nsReflowStatus& aStatus,
7443 Fragmentainer& aFragmentainer, const nsSize& aContainerSize) {
7444 MOZ_ASSERT(aStatus.IsEmpty());
7445 MOZ_ASSERT(aState.mReflowInput);
7447 // Collect our grid items and sort them in row order. Collect placeholders
7448 // and put them in a separate array.
7449 nsTArray<const GridItemInfo*> sortedItems(aState.mGridItems.Length());
7450 nsTArray<nsIFrame*> placeholders(aState.mAbsPosItems.Length());
7451 aState.mIter.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll);
7452 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
7453 nsIFrame* child = *aState.mIter;
7454 if (!child->IsPlaceholderFrame()) {
7455 const GridItemInfo* info = &aState.mGridItems[aState.mIter.ItemIndex()];
7456 sortedItems.AppendElement(info);
7457 } else {
7458 placeholders.AppendElement(child);
7461 // NOTE: We don't need stable_sort here, except in Masonry layout. There are
7462 // no dependencies on having content order between items on the same row in
7463 // the code below in the non-Masonry case.
7464 if (IsMasonry()) {
7465 std::stable_sort(sortedItems.begin(), sortedItems.end(),
7466 GridItemInfo::IsStartRowLessThan);
7467 } else {
7468 std::sort(sortedItems.begin(), sortedItems.end(),
7469 GridItemInfo::IsStartRowLessThan);
7472 // Reflow our placeholder children; they must all be complete.
7473 for (auto child : placeholders) {
7474 nsReflowStatus childStatus;
7475 ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(),
7476 &aFragmentainer, aState, aContentArea, aDesiredSize,
7477 childStatus);
7478 MOZ_ASSERT(childStatus.IsComplete(),
7479 "nsPlaceholderFrame should never need to be fragmented");
7482 // The available size for children - we'll set this to the edge of the last
7483 // row in most cases below, but for now use the full size.
7484 nscoord childAvailableSize = aFragmentainer.mToFragmentainerEnd;
7485 const uint32_t startRow = aState.mStartRow;
7486 const uint32_t numRows = aState.mRows.mSizes.Length();
7487 bool isBDBClone = aState.mReflowInput->mStyleBorder->mBoxDecorationBreak ==
7488 StyleBoxDecorationBreak::Clone;
7489 nscoord bpBEnd = aState.mBorderPadding.BEnd(aState.mWM);
7491 // Set |endRow| to the first row that doesn't fit.
7492 uint32_t endRow = numRows;
7493 for (uint32_t row = startRow; row < numRows; ++row) {
7494 auto& sz = aState.mRows.mSizes[row];
7495 const nscoord bEnd = sz.mPosition + sz.mBase;
7496 nscoord remainingAvailableSize = childAvailableSize - bEnd;
7497 if (remainingAvailableSize < 0 ||
7498 (isBDBClone && remainingAvailableSize < bpBEnd)) {
7499 endRow = row;
7500 break;
7504 // Check for forced breaks on the items if available block-size for children
7505 // is constrained. That is, ignore forced breaks if available block-size for
7506 // children is unconstrained since our parent expected us to be fully
7507 // complete.
7508 bool isForcedBreak = false;
7509 const bool avoidBreakInside = ShouldAvoidBreakInside(*aState.mReflowInput);
7510 if (childAvailableSize != NS_UNCONSTRAINEDSIZE) {
7511 const bool isTopOfPage = aFragmentainer.mIsTopOfPage;
7512 for (const GridItemInfo* info : sortedItems) {
7513 uint32_t itemStartRow = info->mArea.mRows.mStart;
7514 if (itemStartRow == endRow) {
7515 break;
7517 const auto* disp = info->mFrame->StyleDisplay();
7518 if (disp->BreakBefore()) {
7519 // Propagate break-before on the first row to the container unless we're
7520 // already at top-of-page.
7521 if ((itemStartRow == 0 && !isTopOfPage) || avoidBreakInside) {
7522 aStatus.SetInlineLineBreakBeforeAndReset();
7523 return aState.mFragBStart;
7525 if ((itemStartRow > startRow ||
7526 (itemStartRow == startRow && !isTopOfPage)) &&
7527 itemStartRow < endRow) {
7528 endRow = itemStartRow;
7529 isForcedBreak = true;
7530 // reset any BREAK_AFTER we found on an earlier item
7531 aStatus.Reset();
7532 break; // we're done since the items are sorted in row order
7535 uint32_t itemEndRow = info->mArea.mRows.mEnd;
7536 if (disp->BreakAfter()) {
7537 if (itemEndRow != numRows) {
7538 if (itemEndRow > startRow && itemEndRow < endRow) {
7539 endRow = itemEndRow;
7540 isForcedBreak = true;
7541 // No "break;" here since later items with break-after may have
7542 // a shorter span.
7544 } else {
7545 // Propagate break-after on the last row to the container, we may
7546 // still find a break-before on this row though (and reset aStatus).
7547 aStatus.SetInlineLineBreakAfter(); // tentative
7552 // Consume at least one row in each fragment until we have consumed them
7553 // all. Except for the first row if there's a break opportunity before it.
7554 if (startRow == endRow && startRow != numRows &&
7555 (startRow != 0 || !aFragmentainer.mCanBreakAtStart)) {
7556 ++endRow;
7559 // Honor break-inside:avoid if we can't fit all rows.
7560 if (avoidBreakInside && endRow < numRows) {
7561 aStatus.SetInlineLineBreakBeforeAndReset();
7562 return aState.mFragBStart;
7566 // Calculate the block-size including this fragment.
7567 nscoord bEndRow =
7568 aState.mRows.GridLineEdge(endRow, GridLineSide::BeforeGridGap);
7569 nscoord bSize;
7570 if (aFragmentainer.mIsAutoBSize) {
7571 // We only apply min-bsize once all rows are complete (when bsize is auto).
7572 if (endRow < numRows) {
7573 bSize = bEndRow;
7574 auto clampedBSize = ClampToCSSMaxBSize(bSize, aState.mReflowInput);
7575 if (MOZ_UNLIKELY(clampedBSize != bSize)) {
7576 // We apply max-bsize in all fragments though.
7577 bSize = clampedBSize;
7578 } else if (!isBDBClone) {
7579 // The max-bsize won't make this fragment COMPLETE, so the block-end
7580 // border will be in a later fragment.
7581 bpBEnd = 0;
7583 } else {
7584 bSize = NS_CSS_MINMAX(bEndRow, aState.mReflowInput->ComputedMinBSize(),
7585 aState.mReflowInput->ComputedMaxBSize());
7587 } else {
7588 bSize = NS_CSS_MINMAX(aState.mReflowInput->ComputedBSize(),
7589 aState.mReflowInput->ComputedMinBSize(),
7590 aState.mReflowInput->ComputedMaxBSize());
7593 // Check for overflow and set aStatus INCOMPLETE if so.
7594 bool overflow = bSize + bpBEnd > childAvailableSize;
7595 if (overflow) {
7596 if (avoidBreakInside) {
7597 aStatus.SetInlineLineBreakBeforeAndReset();
7598 return aState.mFragBStart;
7600 bool breakAfterLastRow = endRow == numRows && aFragmentainer.mCanBreakAtEnd;
7601 if (breakAfterLastRow) {
7602 MOZ_ASSERT(bEndRow < bSize, "bogus aFragmentainer.mCanBreakAtEnd");
7603 nscoord availableSize = childAvailableSize;
7604 if (isBDBClone) {
7605 availableSize -= bpBEnd;
7607 // Pretend we have at least 1px available size, otherwise we'll never make
7608 // progress in consuming our bSize.
7609 availableSize =
7610 std::max(availableSize, aState.mFragBStart + AppUnitsPerCSSPixel());
7611 // Fill the fragmentainer, but not more than our desired block-size and
7612 // at least to the size of the last row (even if that overflows).
7613 nscoord newBSize = std::min(bSize, availableSize);
7614 newBSize = std::max(newBSize, bEndRow);
7615 // If it's just the border+padding that is overflowing and we have
7616 // box-decoration-break:clone then we are technically COMPLETE. There's
7617 // no point in creating another zero-bsize fragment in this case.
7618 if (newBSize < bSize || !isBDBClone) {
7619 aStatus.SetIncomplete();
7621 bSize = newBSize;
7622 } else if (bSize <= bEndRow && startRow + 1 < endRow) {
7623 if (endRow == numRows) {
7624 // We have more than one row in this fragment, so we can break before
7625 // the last row instead.
7626 --endRow;
7627 bEndRow =
7628 aState.mRows.GridLineEdge(endRow, GridLineSide::BeforeGridGap);
7629 bSize = bEndRow;
7630 if (aFragmentainer.mIsAutoBSize) {
7631 bSize = ClampToCSSMaxBSize(bSize, aState.mReflowInput);
7634 aStatus.SetIncomplete();
7635 } else if (endRow < numRows) {
7636 bSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
7637 } // else - no break opportunities.
7638 } else {
7639 // Even though our block-size fits we need to honor forced breaks, or if
7640 // a row doesn't fit in an auto-sized container (unless it's constrained
7641 // by a max-bsize which make us overflow-incomplete).
7642 if (endRow < numRows &&
7643 (isForcedBreak || (aFragmentainer.mIsAutoBSize && bEndRow == bSize))) {
7644 bSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
7648 // If we can't fit all rows then we're at least overflow-incomplete.
7649 if (endRow < numRows) {
7650 childAvailableSize = bEndRow;
7651 if (aStatus.IsComplete()) {
7652 aStatus.SetOverflowIncomplete();
7653 aStatus.SetNextInFlowNeedsReflow();
7655 } else {
7656 // Children always have the full size of the rows in this fragment.
7657 childAvailableSize = std::max(childAvailableSize, bEndRow);
7660 return ReflowRowsInFragmentainer(aState, aContentArea, aDesiredSize, aStatus,
7661 aFragmentainer, aContainerSize, sortedItems,
7662 startRow, endRow, bSize, childAvailableSize);
7665 nscoord nsGridContainerFrame::ReflowRowsInFragmentainer(
7666 GridReflowInput& aState, const LogicalRect& aContentArea,
7667 ReflowOutput& aDesiredSize, nsReflowStatus& aStatus,
7668 Fragmentainer& aFragmentainer, const nsSize& aContainerSize,
7669 const nsTArray<const GridItemInfo*>& aSortedItems, uint32_t aStartRow,
7670 uint32_t aEndRow, nscoord aBSize, nscoord aAvailableSize) {
7671 FrameHashtable pushedItems;
7672 FrameHashtable incompleteItems;
7673 FrameHashtable overflowIncompleteItems;
7674 Maybe<nsTArray<nscoord>> masonryAxisPos;
7675 const auto rowCount = aState.mRows.mSizes.Length();
7676 nscoord masonryAxisGap;
7677 const auto wm = aState.mWM;
7678 const bool isColMasonry = IsMasonry(eLogicalAxisInline);
7679 if (isColMasonry) {
7680 for (auto& sz : aState.mCols.mSizes) {
7681 sz.mPosition = 0;
7683 masonryAxisGap = nsLayoutUtils::ResolveGapToLength(
7684 aState.mGridStyle->mColumnGap, aContentArea.ISize(wm));
7685 aState.mCols.mGridGap = masonryAxisGap;
7686 masonryAxisPos.emplace(rowCount);
7687 masonryAxisPos->SetLength(rowCount);
7688 PodZero(masonryAxisPos->Elements(), rowCount);
7690 bool isBDBClone = aState.mReflowInput->mStyleBorder->mBoxDecorationBreak ==
7691 StyleBoxDecorationBreak::Clone;
7692 bool didGrowRow = false;
7693 // As we walk across rows, we track whether the current row is at the top
7694 // of its grid-fragment, to help decide whether we can break before it. When
7695 // this function starts, our row is at the top of the current fragment if:
7696 // - we're starting with a nonzero row (i.e. we're a continuation)
7697 // OR:
7698 // - we're starting with the first row, & we're not allowed to break before
7699 // it (which makes it effectively at the top of its grid-fragment).
7700 bool isRowTopOfPage = aStartRow != 0 || !aFragmentainer.mCanBreakAtStart;
7701 const bool isStartRowTopOfPage = isRowTopOfPage;
7702 // Save our full available size for later.
7703 const nscoord gridAvailableSize = aFragmentainer.mToFragmentainerEnd;
7704 // Propagate the constrained size to our children.
7705 aFragmentainer.mToFragmentainerEnd = aAvailableSize;
7706 // Reflow the items in row order up to |aEndRow| and push items after that.
7707 uint32_t row = 0;
7708 // |i| is intentionally signed, so we can set it to -1 to restart the loop.
7709 for (int32_t i = 0, len = aSortedItems.Length(); i < len; ++i) {
7710 const GridItemInfo* const info = aSortedItems[i];
7711 nsIFrame* child = info->mFrame;
7712 row = info->mArea.mRows.mStart;
7713 MOZ_ASSERT(child->GetPrevInFlow() ? row < aStartRow : row >= aStartRow,
7714 "unexpected child start row");
7715 if (row >= aEndRow) {
7716 pushedItems.Insert(child);
7717 continue;
7720 bool rowCanGrow = false;
7721 nscoord maxRowSize = 0;
7722 if (row >= aStartRow) {
7723 if (row > aStartRow) {
7724 isRowTopOfPage = false;
7726 // Can we grow this row? Only consider span=1 items per spec...
7727 rowCanGrow = !didGrowRow && info->mArea.mRows.Extent() == 1;
7728 if (rowCanGrow) {
7729 auto& sz = aState.mRows.mSizes[row];
7730 // and only min-/max-content rows or flex rows in an auto-sized
7731 // container
7732 rowCanGrow = (sz.mState & TrackSize::eMinOrMaxContentMinSizing) ||
7733 ((sz.mState & TrackSize::eFlexMaxSizing) &&
7734 aFragmentainer.mIsAutoBSize);
7735 if (rowCanGrow) {
7736 if (isBDBClone) {
7737 maxRowSize = gridAvailableSize - aState.mBorderPadding.BEnd(wm);
7738 } else {
7739 maxRowSize = gridAvailableSize;
7741 maxRowSize -= sz.mPosition;
7742 // ...and only if there is space for it to grow.
7743 rowCanGrow = maxRowSize > sz.mBase;
7748 if (isColMasonry) {
7749 const auto& cols = info->mArea.mCols;
7750 MOZ_ASSERT((cols.mStart == 0 || cols.mStart == 1) && cols.Extent() == 1);
7751 aState.mCols.mSizes[cols.mStart].mPosition = masonryAxisPos.ref()[row];
7754 // aFragmentainer.mIsTopOfPage is propagated to the child reflow input.
7755 // When it's false the child may request InlineBreak::Before. We set it
7756 // to false when the row is growable (as determined in the CSS Grid
7757 // Fragmentation spec) and there is a non-zero space between it and the
7758 // fragmentainer end (that can be used to grow it). If the child reports
7759 // a forced break in this case, we grow this row to fill the fragment and
7760 // restart the loop. We also restart the loop with |aEndRow = row|
7761 // (but without growing any row) for a InlineBreak::Before child if it spans
7762 // beyond the last row in this fragment. This is to avoid fragmenting it.
7763 // We only restart the loop once.
7764 aFragmentainer.mIsTopOfPage = isRowTopOfPage && !rowCanGrow;
7765 nsReflowStatus childStatus;
7766 // Pass along how much to stretch this fragment, in case it's needed.
7767 nscoord bSize =
7768 aState.mRows.GridLineEdge(std::min(aEndRow, info->mArea.mRows.mEnd),
7769 GridLineSide::BeforeGridGap) -
7770 aState.mRows.GridLineEdge(std::max(aStartRow, row),
7771 GridLineSide::AfterGridGap);
7772 ReflowInFlowChild(child, info, aContainerSize, Some(bSize), &aFragmentainer,
7773 aState, aContentArea, aDesiredSize, childStatus);
7774 MOZ_ASSERT(childStatus.IsInlineBreakBefore() ||
7775 !childStatus.IsFullyComplete() || !child->GetNextInFlow(),
7776 "fully-complete reflow should destroy any NIFs");
7778 if (childStatus.IsInlineBreakBefore()) {
7779 MOZ_ASSERT(
7780 !child->GetPrevInFlow(),
7781 "continuations should never report InlineBreak::Before status");
7782 MOZ_ASSERT(!aFragmentainer.mIsTopOfPage,
7783 "got IsInlineBreakBefore() at top of page");
7784 if (!didGrowRow) {
7785 if (rowCanGrow) {
7786 // Grow this row and restart with the next row as |aEndRow|.
7787 aState.mRows.ResizeRow(row, maxRowSize);
7788 if (aState.mSharedGridData) {
7789 aState.mSharedGridData->mRows.ResizeRow(row, maxRowSize);
7791 didGrowRow = true;
7792 aEndRow = row + 1; // growing this row makes the next one not fit
7793 i = -1; // i == 0 after the next loop increment
7794 isRowTopOfPage = isStartRowTopOfPage;
7795 overflowIncompleteItems.Clear();
7796 incompleteItems.Clear();
7797 nscoord bEndRow =
7798 aState.mRows.GridLineEdge(aEndRow, GridLineSide::BeforeGridGap);
7799 aFragmentainer.mToFragmentainerEnd = bEndRow;
7800 if (aFragmentainer.mIsAutoBSize) {
7801 aBSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
7802 } else if (aStatus.IsIncomplete()) {
7803 aBSize = NS_CSS_MINMAX(aState.mReflowInput->ComputedBSize(),
7804 aState.mReflowInput->ComputedMinBSize(),
7805 aState.mReflowInput->ComputedMaxBSize());
7806 aBSize = std::min(bEndRow, aBSize);
7808 continue;
7811 if (!isRowTopOfPage) {
7812 // We can break before this row - restart with it as the new end row.
7813 aEndRow = row;
7814 aBSize =
7815 aState.mRows.GridLineEdge(aEndRow, GridLineSide::BeforeGridGap);
7816 i = -1; // i == 0 after the next loop increment
7817 isRowTopOfPage = isStartRowTopOfPage;
7818 overflowIncompleteItems.Clear();
7819 incompleteItems.Clear();
7820 aStatus.SetIncomplete();
7821 continue;
7823 NS_ERROR("got InlineBreak::Before at top-of-page");
7824 childStatus.Reset();
7825 } else {
7826 // We got InlineBreak::Before again after growing the row - this can
7827 // happen if the child isn't splittable, e.g. some form controls.
7828 childStatus.Reset();
7829 if (child->GetNextInFlow()) {
7830 // The child already has a fragment, so we know it's splittable.
7831 childStatus.SetIncomplete();
7832 } // else, report that it's complete
7834 } else if (childStatus.IsInlineBreakAfter()) {
7835 MOZ_ASSERT_UNREACHABLE("unexpected child reflow status");
7838 MOZ_ASSERT(!childStatus.IsInlineBreakBefore(),
7839 "should've handled InlineBreak::Before above");
7840 if (childStatus.IsIncomplete()) {
7841 incompleteItems.Insert(child);
7842 } else if (!childStatus.IsFullyComplete()) {
7843 overflowIncompleteItems.Insert(child);
7845 if (isColMasonry) {
7846 auto childWM = child->GetWritingMode();
7847 auto childAxis =
7848 !childWM.IsOrthogonalTo(wm) ? eLogicalAxisInline : eLogicalAxisBlock;
7849 auto normalPos = child->GetLogicalNormalPosition(wm, aContainerSize);
7850 auto sz =
7851 childAxis == eLogicalAxisBlock ? child->BSize() : child->ISize();
7852 auto pos = normalPos.Pos(eLogicalAxisInline, wm) + sz +
7853 child->GetLogicalUsedMargin(childWM).End(childAxis, childWM);
7854 masonryAxisPos.ref()[row] =
7855 pos + masonryAxisGap - aContentArea.Start(eLogicalAxisInline, wm);
7859 // Record a break before |aEndRow|.
7860 aState.mNextFragmentStartRow = aEndRow;
7861 if (aEndRow < rowCount) {
7862 aState.mRows.BreakBeforeRow(aEndRow);
7863 if (aState.mSharedGridData) {
7864 aState.mSharedGridData->mRows.BreakBeforeRow(aEndRow);
7868 const bool childrenMoved = PushIncompleteChildren(
7869 pushedItems, incompleteItems, overflowIncompleteItems);
7870 if (childrenMoved && aStatus.IsComplete()) {
7871 aStatus.SetOverflowIncomplete();
7872 aStatus.SetNextInFlowNeedsReflow();
7874 if (!pushedItems.IsEmpty()) {
7875 AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
7876 // NOTE since we messed with our child list here, we intentionally
7877 // make aState.mIter invalid to avoid any use of it after this point.
7878 aState.mIter.Invalidate();
7880 if (!incompleteItems.IsEmpty()) {
7881 // NOTE since we messed with our child list here, we intentionally
7882 // make aState.mIter invalid to avoid any use of it after this point.
7883 aState.mIter.Invalidate();
7886 if (isColMasonry) {
7887 nscoord maxSize = 0;
7888 for (auto pos : masonryAxisPos.ref()) {
7889 maxSize = std::max(maxSize, pos);
7891 maxSize = std::max(nscoord(0), maxSize - masonryAxisGap);
7892 aState.AlignJustifyContentInMasonryAxis(maxSize, aContentArea.ISize(wm));
7895 return aBSize;
7898 // Here's a brief overview of how Masonry layout is implemented:
7899 // We setup two synthetic tracks in the Masonry axis so that the Reflow code
7900 // can treat it the same as for normal grid layout. The first track is
7901 // fixed (during item placement/layout) at the content box start and contains
7902 // the start items for each grid-axis track. The second track contains
7903 // all other items and is moved to the position where we want to position
7904 // the currently laid out item (like a sliding window as we place items).
7905 // Once item layout is done, the tracks are resized to be the size of
7906 // the "masonry box", which is the offset from the content box start to
7907 // the margin-box end of the item that is furthest away (this happens in
7908 // AlignJustifyContentInMasonryAxis() called at the end of this method).
7909 // This is to prepare for AlignJustifyTracksInMasonryAxis, which is called
7910 // later by our caller.
7911 // Both tracks store their first-/last-baseline group offsets as usual.
7912 // The first-baseline of the start track, and the last-baseline of the last
7913 // track (if they exist) are exported as the grid container's baselines, or
7914 // we fall back to picking an item's baseline (all this is per normal grid
7915 // layout). There's a slight difference in which items belongs to which
7916 // group though - see InitializeItemBaselinesInMasonryAxis for details.
7917 // This method returns the "masonry box" size (in the masonry axis).
7918 nscoord nsGridContainerFrame::MasonryLayout(GridReflowInput& aState,
7919 const LogicalRect& aContentArea,
7920 SizingConstraint aConstraint,
7921 ReflowOutput& aDesiredSize,
7922 nsReflowStatus& aStatus,
7923 Fragmentainer* aFragmentainer,
7924 const nsSize& aContainerSize) {
7925 using BaselineAlignmentSet = Tracks::BaselineAlignmentSet;
7927 auto recordAutoPlacement = [this, &aState](GridItemInfo* aItem,
7928 LogicalAxis aGridAxis) {
7929 // When we're auto-placing an item in a continuation we need to record
7930 // the placement in mSharedGridData.
7931 if (MOZ_UNLIKELY(aState.mSharedGridData && GetPrevInFlow()) &&
7932 (aItem->mState[aGridAxis] & ItemState::eAutoPlacement)) {
7933 auto* child = aItem->mFrame;
7934 MOZ_RELEASE_ASSERT(!child->GetPrevInFlow(),
7935 "continuations should never be auto-placed");
7936 for (auto& sharedItem : aState.mSharedGridData->mGridItems) {
7937 if (sharedItem.mFrame == child) {
7938 sharedItem.mArea.LineRangeForAxis(aGridAxis) =
7939 aItem->mArea.LineRangeForAxis(aGridAxis);
7940 MOZ_ASSERT(sharedItem.mState[aGridAxis] & ItemState::eAutoPlacement);
7941 sharedItem.mState[aGridAxis] &= ~ItemState::eAutoPlacement;
7942 break;
7946 aItem->mState[aGridAxis] &= ~ItemState::eAutoPlacement;
7949 // Collect our grid items and sort them in grid order.
7950 nsTArray<GridItemInfo*> sortedItems(aState.mGridItems.Length());
7951 aState.mIter.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll);
7952 size_t absposIndex = 0;
7953 const LogicalAxis masonryAxis =
7954 IsMasonry(eLogicalAxisBlock) ? eLogicalAxisBlock : eLogicalAxisInline;
7955 const auto wm = aState.mWM;
7956 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
7957 nsIFrame* child = *aState.mIter;
7958 if (MOZ_LIKELY(!child->IsPlaceholderFrame())) {
7959 GridItemInfo* item = &aState.mGridItems[aState.mIter.ItemIndex()];
7960 sortedItems.AppendElement(item);
7961 } else if (aConstraint == SizingConstraint::NoConstraint) {
7962 // (we only collect placeholders in the NoConstraint case since they
7963 // don't affect intrinsic sizing in any way)
7964 GridItemInfo* item = nullptr;
7965 auto* ph = static_cast<nsPlaceholderFrame*>(child);
7966 if (ph->GetOutOfFlowFrame()->GetParent() == this) {
7967 item = &aState.mAbsPosItems[absposIndex++];
7968 MOZ_RELEASE_ASSERT(item->mFrame == ph->GetOutOfFlowFrame());
7969 auto masonryStart = item->mArea.LineRangeForAxis(masonryAxis).mStart;
7970 // If the item was placed by the author at line 1 (masonryStart == 0)
7971 // then include it to be placed at the masonry-box start. If it's
7972 // auto-placed and has an `auto` inset value in the masonry axis then
7973 // we include it to be placed after the last grid item with the same
7974 // grid-axis start track.
7975 // XXXmats this is all a bit experimental at this point, pending a spec
7976 if (masonryStart == 0 ||
7977 (masonryStart == kAutoLine && item->mFrame->StylePosition()
7978 ->mOffset.Start(masonryAxis, wm)
7979 .IsAuto())) {
7980 sortedItems.AppendElement(item);
7981 } else {
7982 item = nullptr;
7985 if (!item) {
7986 // It wasn't included above - just reflow it and be done with it.
7987 nsReflowStatus childStatus;
7988 ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(), nullptr,
7989 aState, aContentArea, aDesiredSize, childStatus);
7993 const auto masonryAutoFlow = aState.mGridStyle->mMasonryAutoFlow;
7994 bool definiteFirst = masonryAutoFlow & NS_STYLE_MASONRY_ORDER_DEFINITE_FIRST;
7995 if (masonryAxis == eLogicalAxisBlock) {
7996 std::stable_sort(sortedItems.begin(), sortedItems.end(),
7997 definiteFirst ? GridItemInfo::RowMasonryDefiniteFirst
7998 : GridItemInfo::RowMasonryOrdered);
7999 } else {
8000 std::stable_sort(sortedItems.begin(), sortedItems.end(),
8001 definiteFirst ? GridItemInfo::ColMasonryDefiniteFirst
8002 : GridItemInfo::ColMasonryOrdered);
8005 FrameHashtable pushedItems;
8006 FrameHashtable incompleteItems;
8007 FrameHashtable overflowIncompleteItems;
8008 nscoord toFragmentainerEnd = nscoord_MAX;
8009 nscoord fragStartPos = aState.mFragBStart;
8010 const bool avoidBreakInside =
8011 aFragmentainer && ShouldAvoidBreakInside(*aState.mReflowInput);
8012 const bool isTopOfPageAtStart =
8013 aFragmentainer && aFragmentainer->mIsTopOfPage;
8014 if (aFragmentainer) {
8015 toFragmentainerEnd = std::max(0, aFragmentainer->mToFragmentainerEnd);
8017 const LogicalAxis gridAxis = GetOrthogonalAxis(masonryAxis);
8018 const auto gridAxisTrackCount = aState.TracksFor(gridAxis).mSizes.Length();
8019 auto& masonryTracks = aState.TracksFor(masonryAxis);
8020 auto& masonrySizes = masonryTracks.mSizes;
8021 MOZ_ASSERT(masonrySizes.Length() == 2);
8022 for (auto& sz : masonrySizes) {
8023 sz.mPosition = fragStartPos;
8025 // The current running position for each grid-axis track where the next item
8026 // should be positioned. When an item is placed we'll update the tracks it
8027 // spans to the end of its margin box + 'gap'.
8028 nsTArray<nscoord> currentPos(gridAxisTrackCount);
8029 currentPos.SetLength(gridAxisTrackCount);
8030 for (auto& sz : currentPos) {
8031 sz = fragStartPos;
8033 nsTArray<nscoord> lastPos(currentPos.Clone());
8034 nsTArray<GridItemInfo*> lastItems(gridAxisTrackCount);
8035 lastItems.SetLength(gridAxisTrackCount);
8036 PodZero(lastItems.Elements(), gridAxisTrackCount);
8037 const nscoord gap = nsLayoutUtils::ResolveGapToLength(
8038 masonryAxis == eLogicalAxisBlock ? aState.mGridStyle->mRowGap
8039 : aState.mGridStyle->mColumnGap,
8040 masonryTracks.mContentBoxSize);
8041 masonryTracks.mGridGap = gap;
8042 uint32_t cursor = 0;
8043 const auto containerToMasonryBoxOffset =
8044 fragStartPos - aContentArea.Start(masonryAxis, wm);
8045 const bool isPack = masonryAutoFlow & NS_STYLE_MASONRY_PLACEMENT_PACK;
8046 bool didAlignStartAlignedFirstItems = false;
8048 // Return true if any of the lastItems in aRange are baseline-aligned in
8049 // the masonry axis.
8050 auto lastItemHasBaselineAlignment = [&](const LineRange& aRange) {
8051 for (auto i : aRange.Range()) {
8052 if (auto* child = lastItems[i] ? lastItems[i]->mFrame : nullptr) {
8053 const auto& pos = child->StylePosition();
8054 auto selfAlignment = pos->UsedSelfAlignment(masonryAxis, this->Style());
8055 if (selfAlignment == StyleAlignFlags::BASELINE ||
8056 selfAlignment == StyleAlignFlags::LAST_BASELINE) {
8057 return true;
8059 auto childAxis = masonryAxis;
8060 if (child->GetWritingMode().IsOrthogonalTo(wm)) {
8061 childAxis = gridAxis;
8063 auto contentAlignment = pos->UsedContentAlignment(childAxis).primary;
8064 if (contentAlignment == StyleAlignFlags::BASELINE ||
8065 contentAlignment == StyleAlignFlags::LAST_BASELINE) {
8066 return true;
8070 return false;
8073 // Resolve aItem's placement, unless it's definite already. Return its
8074 // masonry axis position with that placement.
8075 auto placeItem = [&](GridItemInfo* aItem) -> nscoord {
8076 auto& masonryAxisRange = aItem->mArea.LineRangeForAxis(masonryAxis);
8077 MOZ_ASSERT(masonryAxisRange.mStart != 0, "item placement is already final");
8078 auto& gridAxisRange = aItem->mArea.LineRangeForAxis(gridAxis);
8079 bool isAutoPlaced = aItem->mState[gridAxis] & ItemState::eAutoPlacement;
8080 uint32_t start = isAutoPlaced ? 0 : gridAxisRange.mStart;
8081 if (isAutoPlaced && !isPack) {
8082 start = cursor;
8083 isAutoPlaced = false;
8085 const uint32_t extent = gridAxisRange.Extent();
8086 if (start + extent > gridAxisTrackCount) {
8087 // Note that this will only happen to auto-placed items since the grid is
8088 // always wide enough to fit other items.
8089 start = 0;
8091 // This keeps track of the smallest `maxPosForRange` value that
8092 // we discover in the loop below:
8093 nscoord minPos = nscoord_MAX;
8094 MOZ_ASSERT(extent <= gridAxisTrackCount);
8095 const uint32_t iEnd = gridAxisTrackCount + 1 - extent;
8096 for (uint32_t i = start; i < iEnd; ++i) {
8097 // Find the max `currentPos` value for the tracks that we would span
8098 // if we were to use `i` as our start track:
8099 nscoord maxPosForRange = 0;
8100 for (auto j = i, jEnd = j + extent; j < jEnd; ++j) {
8101 maxPosForRange = std::max(currentPos[j], maxPosForRange);
8103 if (maxPosForRange < minPos) {
8104 minPos = maxPosForRange;
8105 start = i;
8107 if (!isAutoPlaced) {
8108 break;
8111 gridAxisRange.mStart = start;
8112 gridAxisRange.mEnd = start + extent;
8113 bool isFirstItem = true;
8114 for (uint32_t i : gridAxisRange.Range()) {
8115 if (lastItems[i]) {
8116 isFirstItem = false;
8117 break;
8120 // If this is the first item in its spanned grid tracks, then place it in
8121 // the first masonry track. Otherwise, place it in the second masonry track.
8122 masonryAxisRange.mStart = isFirstItem ? 0 : 1;
8123 masonryAxisRange.mEnd = masonryAxisRange.mStart + 1;
8124 return minPos;
8127 // Handle the resulting reflow status after reflowing aItem.
8128 // This may set aStatus to BreakBefore which the caller is expected
8129 // to handle by returning from MasonryLayout.
8130 // @return true if this item should consume all remaining space
8131 auto handleChildStatus = [&](GridItemInfo* aItem,
8132 const nsReflowStatus& aChildStatus) {
8133 bool result = false;
8134 if (MOZ_UNLIKELY(aFragmentainer)) {
8135 auto* child = aItem->mFrame;
8136 if (!aChildStatus.IsComplete() || aChildStatus.IsInlineBreakBefore() ||
8137 aChildStatus.IsInlineBreakAfter() ||
8138 child->StyleDisplay()->BreakAfter()) {
8139 if (!isTopOfPageAtStart && avoidBreakInside) {
8140 aStatus.SetInlineLineBreakBeforeAndReset();
8141 return result;
8143 result = true;
8145 if (aChildStatus.IsInlineBreakBefore()) {
8146 aStatus.SetIncomplete();
8147 pushedItems.Insert(child);
8148 } else if (aChildStatus.IsIncomplete()) {
8149 recordAutoPlacement(aItem, gridAxis);
8150 aStatus.SetIncomplete();
8151 incompleteItems.Insert(child);
8152 } else if (!aChildStatus.IsFullyComplete()) {
8153 recordAutoPlacement(aItem, gridAxis);
8154 overflowIncompleteItems.Insert(child);
8157 return result;
8160 // @return the distance from the masonry-box start to the end of the margin-
8161 // box of aChild
8162 auto offsetToMarginBoxEnd = [&](nsIFrame* aChild) {
8163 auto childWM = aChild->GetWritingMode();
8164 auto childAxis = !childWM.IsOrthogonalTo(wm) ? masonryAxis : gridAxis;
8165 auto normalPos = aChild->GetLogicalNormalPosition(wm, aContainerSize);
8166 auto sz =
8167 childAxis == eLogicalAxisBlock ? aChild->BSize() : aChild->ISize();
8168 return containerToMasonryBoxOffset + normalPos.Pos(masonryAxis, wm) + sz +
8169 aChild->GetLogicalUsedMargin(childWM).End(childAxis, childWM);
8172 // Apply baseline alignment to items belonging to the given set.
8173 nsTArray<Tracks::ItemBaselineData> firstBaselineItems;
8174 nsTArray<Tracks::ItemBaselineData> lastBaselineItems;
8175 auto applyBaselineAlignment = [&](BaselineAlignmentSet aSet) {
8176 firstBaselineItems.ClearAndRetainStorage();
8177 lastBaselineItems.ClearAndRetainStorage();
8178 masonryTracks.InitializeItemBaselinesInMasonryAxis(
8179 aState, aState.mGridItems, aSet, aContainerSize, currentPos,
8180 firstBaselineItems, lastBaselineItems);
8182 bool didBaselineAdjustment = false;
8183 nsTArray<Tracks::ItemBaselineData>* baselineItems[] = {&firstBaselineItems,
8184 &lastBaselineItems};
8185 for (const auto* items : baselineItems) {
8186 for (const auto& data : *items) {
8187 GridItemInfo* item = data.mGridItem;
8188 MOZ_ASSERT((item->mState[masonryAxis] & ItemState::eIsBaselineAligned));
8189 nscoord baselineOffset = item->mBaselineOffset[masonryAxis];
8190 if (baselineOffset == nscoord(0)) {
8191 continue; // no adjustment needed for this item
8193 didBaselineAdjustment = true;
8194 auto* child = item->mFrame;
8195 auto masonryAxisStart =
8196 item->mArea.LineRangeForAxis(masonryAxis).mStart;
8197 auto gridAxisRange = item->mArea.LineRangeForAxis(gridAxis);
8198 masonrySizes[masonryAxisStart].mPosition =
8199 aSet.mItemSet == BaselineAlignmentSet::LastItems
8200 ? lastPos[gridAxisRange.mStart]
8201 : fragStartPos;
8202 bool consumeAllSpace = false;
8203 const auto state = item->mState[masonryAxis];
8204 if ((state & ItemState::eContentBaseline) ||
8205 MOZ_UNLIKELY(aFragmentainer)) {
8206 if (MOZ_UNLIKELY(aFragmentainer)) {
8207 aFragmentainer->mIsTopOfPage =
8208 isTopOfPageAtStart &&
8209 masonrySizes[masonryAxisStart].mPosition == fragStartPos;
8211 nsReflowStatus childStatus;
8212 ReflowInFlowChild(child, item, aContainerSize, Nothing(),
8213 aFragmentainer, aState, aContentArea, aDesiredSize,
8214 childStatus);
8215 consumeAllSpace = handleChildStatus(item, childStatus);
8216 if (aStatus.IsInlineBreakBefore()) {
8217 return false;
8219 } else if (!(state & ItemState::eEndSideBaseline)) {
8220 // `align/justify-self` baselines on the start side can be handled by
8221 // just moving the frame (except in a fragmentainer in which case we
8222 // reflow it above instead since it might make it INCOMPLETE).
8223 LogicalPoint logicalDelta(wm);
8224 logicalDelta.Pos(masonryAxis, wm) = baselineOffset;
8225 child->MovePositionBy(wm, logicalDelta);
8227 if ((state & ItemState::eEndSideBaseline) && !consumeAllSpace) {
8228 // Account for an end-side baseline adjustment.
8229 for (uint32_t i : gridAxisRange.Range()) {
8230 currentPos[i] += baselineOffset;
8232 } else {
8233 nscoord pos = consumeAllSpace ? toFragmentainerEnd
8234 : offsetToMarginBoxEnd(child);
8235 pos += gap;
8236 for (uint32_t i : gridAxisRange.Range()) {
8237 currentPos[i] = pos;
8242 return didBaselineAdjustment;
8245 // Place and reflow items. We'll use two fake tracks in the masonry axis.
8246 // The first contains items that were placed there by the regular grid
8247 // placement algo (PlaceGridItems) and we may add some items here if there
8248 // are still empty slots. The second track contains all other items.
8249 // Both tracks always have the size of the content box in the masonry axis.
8250 // The position of the first track is always at the start. The position
8251 // of the second track is updated as we go to a position where we want
8252 // the current item to be positioned.
8253 for (GridItemInfo* item : sortedItems) {
8254 auto* child = item->mFrame;
8255 auto& masonryRange = item->mArea.LineRangeForAxis(masonryAxis);
8256 auto& gridRange = item->mArea.LineRangeForAxis(gridAxis);
8257 nsReflowStatus childStatus;
8258 if (MOZ_UNLIKELY(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) {
8259 auto contentArea = aContentArea;
8260 nscoord pos = nscoord_MAX;
8261 // XXXmats take mEnd into consideration...
8262 if (gridRange.mStart == kAutoLine) {
8263 for (auto p : currentPos) {
8264 pos = std::min(p, pos);
8266 } else if (gridRange.mStart < currentPos.Length()) {
8267 pos = currentPos[gridRange.mStart];
8268 } else if (currentPos.Length() > 0) {
8269 pos = currentPos.LastElement();
8271 if (pos == nscoord_MAX) {
8272 pos = nscoord(0);
8274 contentArea.Start(masonryAxis, wm) = pos;
8275 child = child->GetPlaceholderFrame();
8276 ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(), nullptr,
8277 aState, contentArea, aDesiredSize, childStatus);
8278 } else {
8279 MOZ_ASSERT(gridRange.Extent() > 0 &&
8280 gridRange.Extent() <= gridAxisTrackCount);
8281 MOZ_ASSERT((masonryRange.mStart == 0 || masonryRange.mStart == 1) &&
8282 masonryRange.Extent() == 1);
8283 if (masonryRange.mStart != 0) {
8284 masonrySizes[1].mPosition = placeItem(item);
8287 // If this is the first item NOT in the first track and if any of
8288 // the grid-axis tracks we span has a baseline-aligned item then we
8289 // need to do that baseline alignment now since it may affect
8290 // the placement of this and later items.
8291 if (!didAlignStartAlignedFirstItems &&
8292 aConstraint == SizingConstraint::NoConstraint &&
8293 masonryRange.mStart != 0 && lastItemHasBaselineAlignment(gridRange)) {
8294 didAlignStartAlignedFirstItems = true;
8295 if (applyBaselineAlignment({BaselineAlignmentSet::FirstItems,
8296 BaselineAlignmentSet::StartStretch})) {
8297 // Baseline alignment resized some items - redo our placement.
8298 masonrySizes[1].mPosition = placeItem(item);
8300 if (aStatus.IsInlineBreakBefore()) {
8301 return fragStartPos;
8305 for (uint32_t i : gridRange.Range()) {
8306 lastItems[i] = item;
8308 cursor = gridRange.mEnd;
8309 if (cursor >= gridAxisTrackCount) {
8310 cursor = 0;
8313 nscoord pos;
8314 if (aConstraint == SizingConstraint::NoConstraint) {
8315 const auto* disp = child->StyleDisplay();
8316 if (MOZ_UNLIKELY(aFragmentainer)) {
8317 aFragmentainer->mIsTopOfPage =
8318 isTopOfPageAtStart &&
8319 masonrySizes[masonryRange.mStart].mPosition == fragStartPos;
8320 if (!aFragmentainer->mIsTopOfPage &&
8321 (disp->BreakBefore() ||
8322 masonrySizes[masonryRange.mStart].mPosition >=
8323 toFragmentainerEnd)) {
8324 childStatus.SetInlineLineBreakBeforeAndReset();
8327 if (!childStatus.IsInlineBreakBefore()) {
8328 ReflowInFlowChild(child, item, aContainerSize, Nothing(),
8329 aFragmentainer, aState, aContentArea, aDesiredSize,
8330 childStatus);
8332 bool consumeAllSpace = handleChildStatus(item, childStatus);
8333 if (aStatus.IsInlineBreakBefore()) {
8334 return fragStartPos;
8336 pos =
8337 consumeAllSpace ? toFragmentainerEnd : offsetToMarginBoxEnd(child);
8338 } else {
8339 LogicalSize percentBasis(
8340 aState.PercentageBasisFor(eLogicalAxisInline, *item));
8341 IntrinsicISizeType type = aConstraint == SizingConstraint::MaxContent
8342 ? IntrinsicISizeType::PrefISize
8343 : IntrinsicISizeType::MinISize;
8344 auto sz =
8345 ::ContentContribution(*item, aState, &aState.mRenderingContext, wm,
8346 masonryAxis, Some(percentBasis), type);
8347 pos = sz + masonrySizes[masonryRange.mStart].mPosition;
8349 pos += gap;
8350 for (uint32_t i : gridRange.Range()) {
8351 lastPos[i] = currentPos[i];
8352 currentPos[i] = pos;
8357 // Do the remaining baseline alignment sets.
8358 if (aConstraint == SizingConstraint::NoConstraint) {
8359 for (auto*& item : lastItems) {
8360 if (item) {
8361 item->mState[masonryAxis] |= ItemState::eIsLastItemInMasonryTrack;
8364 BaselineAlignmentSet baselineSets[] = {
8365 {BaselineAlignmentSet::FirstItems, BaselineAlignmentSet::StartStretch},
8366 {BaselineAlignmentSet::FirstItems, BaselineAlignmentSet::EndStretch},
8367 {BaselineAlignmentSet::LastItems, BaselineAlignmentSet::StartStretch},
8368 {BaselineAlignmentSet::LastItems, BaselineAlignmentSet::EndStretch},
8370 for (uint32_t i = 0; i < ArrayLength(baselineSets); ++i) {
8371 if (i == 0 && didAlignStartAlignedFirstItems) {
8372 continue;
8374 applyBaselineAlignment(baselineSets[i]);
8378 const bool childrenMoved = PushIncompleteChildren(
8379 pushedItems, incompleteItems, overflowIncompleteItems);
8380 if (childrenMoved && aStatus.IsComplete()) {
8381 aStatus.SetOverflowIncomplete();
8382 aStatus.SetNextInFlowNeedsReflow();
8384 if (!pushedItems.IsEmpty()) {
8385 AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
8386 // NOTE since we messed with our child list here, we intentionally
8387 // make aState.mIter invalid to avoid any use of it after this point.
8388 aState.mIter.Invalidate();
8390 if (!incompleteItems.IsEmpty()) {
8391 // NOTE since we messed with our child list here, we intentionally
8392 // make aState.mIter invalid to avoid any use of it after this point.
8393 aState.mIter.Invalidate();
8396 nscoord masonryBoxSize = 0;
8397 for (auto pos : currentPos) {
8398 masonryBoxSize = std::max(masonryBoxSize, pos);
8400 masonryBoxSize = std::max(nscoord(0), masonryBoxSize - gap);
8401 if (aConstraint == SizingConstraint::NoConstraint) {
8402 aState.AlignJustifyContentInMasonryAxis(masonryBoxSize,
8403 masonryTracks.mContentBoxSize);
8405 return masonryBoxSize;
8408 nsGridContainerFrame* nsGridContainerFrame::ParentGridContainerForSubgrid()
8409 const {
8410 MOZ_ASSERT(IsSubgrid());
8411 nsIFrame* p = GetParent();
8412 while (p->GetContent() == GetContent()) {
8413 p = p->GetParent();
8415 MOZ_ASSERT(p->IsGridContainerFrame());
8416 auto* parent = static_cast<nsGridContainerFrame*>(p);
8417 MOZ_ASSERT(parent->HasSubgridItems());
8418 return parent;
8421 nscoord nsGridContainerFrame::ReflowChildren(GridReflowInput& aState,
8422 const LogicalRect& aContentArea,
8423 const nsSize& aContainerSize,
8424 ReflowOutput& aDesiredSize,
8425 nsReflowStatus& aStatus) {
8426 MOZ_ASSERT(aState.mReflowInput);
8427 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
8429 OverflowAreas ocBounds;
8430 nsReflowStatus ocStatus;
8431 if (GetPrevInFlow()) {
8432 ReflowOverflowContainerChildren(PresContext(), *aState.mReflowInput,
8433 ocBounds, ReflowChildFlags::Default,
8434 ocStatus, MergeSortedFrameListsFor);
8437 WritingMode wm = aState.mReflowInput->GetWritingMode();
8438 nscoord bSize = aContentArea.BSize(wm);
8439 Maybe<Fragmentainer> fragmentainer = GetNearestFragmentainer(aState);
8440 // MasonryLayout() can only handle fragmentation in the masonry-axis,
8441 // so we let ReflowInFragmentainer() deal with grid-axis fragmentation
8442 // in the else-clause below.
8443 if (IsMasonry() &&
8444 !(IsMasonry(eLogicalAxisInline) && fragmentainer.isSome())) {
8445 aState.mInFragmentainer = fragmentainer.isSome();
8446 nscoord sz = MasonryLayout(
8447 aState, aContentArea, SizingConstraint::NoConstraint, aDesiredSize,
8448 aStatus, fragmentainer.ptrOr(nullptr), aContainerSize);
8449 if (IsMasonry(eLogicalAxisBlock)) {
8450 bSize = aState.mReflowInput->ComputedBSize();
8451 if (bSize == NS_UNCONSTRAINEDSIZE) {
8452 bSize = NS_CSS_MINMAX(sz, aState.mReflowInput->ComputedMinBSize(),
8453 aState.mReflowInput->ComputedMaxBSize());
8456 } else if (MOZ_UNLIKELY(fragmentainer.isSome())) {
8457 if (IsMasonry(eLogicalAxisInline) && !GetPrevInFlow()) {
8458 // First we do an unconstrained reflow to resolve the item placement
8459 // which is then kept as-is in the constrained reflow below.
8460 MasonryLayout(aState, aContentArea, SizingConstraint::NoConstraint,
8461 aDesiredSize, aStatus, nullptr, aContainerSize);
8463 aState.mInFragmentainer = true;
8464 bSize = ReflowInFragmentainer(aState, aContentArea, aDesiredSize, aStatus,
8465 *fragmentainer, aContainerSize);
8466 } else {
8467 aState.mIter.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll);
8468 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
8469 nsIFrame* child = *aState.mIter;
8470 const GridItemInfo* info = nullptr;
8471 if (!child->IsPlaceholderFrame()) {
8472 info = &aState.mGridItems[aState.mIter.ItemIndex()];
8474 ReflowInFlowChild(child, info, aContainerSize, Nothing(), nullptr, aState,
8475 aContentArea, aDesiredSize, aStatus);
8476 MOZ_ASSERT(aStatus.IsComplete(),
8477 "child should be complete in unconstrained reflow");
8481 // Merge overflow container bounds and status.
8482 aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
8483 aStatus.MergeCompletionStatusFrom(ocStatus);
8485 if (IsAbsoluteContainer()) {
8486 nsFrameList children(GetChildList(GetAbsoluteListID()));
8487 if (!children.IsEmpty()) {
8488 // 'gridOrigin' is the origin of the grid (the start of the first track),
8489 // with respect to the grid container's padding-box (CB).
8490 LogicalMargin pad(aState.mReflowInput->ComputedLogicalPadding(wm));
8491 const LogicalPoint gridOrigin(wm, pad.IStart(wm), pad.BStart(wm));
8492 const LogicalRect gridCB(wm, 0, 0,
8493 aContentArea.ISize(wm) + pad.IStartEnd(wm),
8494 bSize + pad.BStartEnd(wm));
8495 const nsSize gridCBPhysicalSize = gridCB.Size(wm).GetPhysicalSize(wm);
8496 size_t i = 0;
8497 for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next(), ++i) {
8498 nsIFrame* child = e.get();
8499 MOZ_ASSERT(i < aState.mAbsPosItems.Length());
8500 MOZ_ASSERT(aState.mAbsPosItems[i].mFrame == child);
8501 GridArea& area = aState.mAbsPosItems[i].mArea;
8502 LogicalRect itemCB =
8503 aState.ContainingBlockForAbsPos(area, gridOrigin, gridCB);
8504 // nsAbsoluteContainingBlock::Reflow uses physical coordinates.
8505 nsRect* cb = child->GetProperty(GridItemContainingBlockRect());
8506 if (!cb) {
8507 cb = new nsRect;
8508 child->SetProperty(GridItemContainingBlockRect(), cb);
8510 *cb = itemCB.GetPhysicalRect(wm, gridCBPhysicalSize);
8512 // We pass a dummy rect as CB because each child has its own CB rect.
8513 // The eIsGridContainerCB flag tells nsAbsoluteContainingBlock::Reflow to
8514 // use those instead.
8515 nsRect dummyRect;
8516 AbsPosReflowFlags flags =
8517 AbsPosReflowFlags::CBWidthAndHeightChanged; // XXX could be optimized
8518 flags |= AbsPosReflowFlags::ConstrainHeight;
8519 flags |= AbsPosReflowFlags::IsGridContainerCB;
8520 GetAbsoluteContainingBlock()->Reflow(
8521 this, PresContext(), *aState.mReflowInput, aStatus, dummyRect, flags,
8522 &aDesiredSize.mOverflowAreas);
8525 return bSize;
8528 void nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
8529 ReflowOutput& aDesiredSize,
8530 const ReflowInput& aReflowInput,
8531 nsReflowStatus& aStatus) {
8532 MarkInReflow();
8533 DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
8534 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
8535 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
8537 if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) {
8538 return;
8541 NormalizeChildLists();
8543 #ifdef DEBUG
8544 mDidPushItemsBitMayLie = false;
8545 SanityCheckChildListsBeforeReflow();
8546 #endif // DEBUG
8548 for (auto& perAxisBaseline : mBaseline) {
8549 for (auto& baseline : perAxisBaseline) {
8550 baseline = NS_INTRINSIC_ISIZE_UNKNOWN;
8554 const nsStylePosition* stylePos = aReflowInput.mStylePosition;
8555 auto prevInFlow = static_cast<nsGridContainerFrame*>(GetPrevInFlow());
8556 if (MOZ_LIKELY(!prevInFlow)) {
8557 InitImplicitNamedAreas(stylePos);
8558 } else {
8559 MOZ_ASSERT(prevInFlow->HasAnyStateBits(kIsSubgridBits) ==
8560 HasAnyStateBits(kIsSubgridBits),
8561 "continuations should have same kIsSubgridBits");
8563 GridReflowInput gridReflowInput(this, aReflowInput);
8564 if (gridReflowInput.mIter.ItemsAreAlreadyInOrder()) {
8565 AddStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
8566 } else {
8567 RemoveStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
8569 if (gridReflowInput.mIter.AtEnd() ||
8570 aReflowInput.mStyleDisplay->IsContainLayout()) {
8571 // We have no grid items, or we're layout-contained. So, we have no
8572 // baseline, and our parent should synthesize a baseline if needed.
8573 AddStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
8574 } else {
8575 RemoveStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
8577 const nscoord computedBSize = aReflowInput.ComputedBSize();
8578 const nscoord computedISize = aReflowInput.ComputedISize();
8579 const WritingMode& wm = gridReflowInput.mWM;
8580 const LogicalSize computedSize(wm, computedISize, computedBSize);
8582 nscoord consumedBSize = 0;
8583 nscoord bSize = 0;
8584 if (MOZ_LIKELY(!prevInFlow)) {
8585 Grid grid;
8586 if (MOZ_LIKELY(!IsSubgrid())) {
8587 RepeatTrackSizingInput repeatSizing(aReflowInput.ComputedMinSize(),
8588 computedSize,
8589 aReflowInput.ComputedMaxSize());
8590 grid.PlaceGridItems(gridReflowInput, repeatSizing);
8591 } else {
8592 auto* subgrid = GetProperty(Subgrid::Prop());
8593 MOZ_ASSERT(subgrid, "an ancestor forgot to call PlaceGridItems?");
8594 gridReflowInput.mGridItems = subgrid->mGridItems.Clone();
8595 gridReflowInput.mAbsPosItems = subgrid->mAbsPosItems.Clone();
8596 grid.mGridColEnd = subgrid->mGridColEnd;
8597 grid.mGridRowEnd = subgrid->mGridRowEnd;
8599 gridReflowInput.CalculateTrackSizes(grid, computedSize,
8600 SizingConstraint::NoConstraint);
8601 // XXX Technically incorrect: We're ignoring our row sizes, when really
8602 // we should use them but *they* should be computed as if we had no
8603 // children. To be fixed in bug 1488878.
8604 if (!aReflowInput.mStyleDisplay->IsContainSize()) {
8605 if (IsMasonry(eLogicalAxisBlock)) {
8606 bSize = computedBSize;
8607 } else {
8608 const auto& rowSizes = gridReflowInput.mRows.mSizes;
8609 if (MOZ_LIKELY(!IsSubgrid(eLogicalAxisBlock))) {
8610 // Note: we can't use GridLineEdge here since we haven't calculated
8611 // the rows' mPosition yet (happens in AlignJustifyContent below).
8612 for (const auto& sz : rowSizes) {
8613 bSize += sz.mBase;
8615 bSize += gridReflowInput.mRows.SumOfGridGaps();
8616 } else if (computedBSize == NS_UNCONSTRAINEDSIZE) {
8617 bSize = gridReflowInput.mRows.GridLineEdge(
8618 rowSizes.Length(), GridLineSide::BeforeGridGap);
8622 } else {
8623 consumedBSize = CalcAndCacheConsumedBSize();
8624 gridReflowInput.InitializeForContinuation(this, consumedBSize);
8625 // XXX Technically incorrect: We're ignoring our row sizes, when really
8626 // we should use them but *they* should be computed as if we had no
8627 // children. To be fixed in bug 1488878.
8628 if (!aReflowInput.mStyleDisplay->IsContainSize()) {
8629 const uint32_t numRows = gridReflowInput.mRows.mSizes.Length();
8630 bSize = gridReflowInput.mRows.GridLineEdge(numRows,
8631 GridLineSide::AfterGridGap);
8634 if (computedBSize == NS_UNCONSTRAINEDSIZE) {
8635 bSize = NS_CSS_MINMAX(bSize, aReflowInput.ComputedMinBSize(),
8636 aReflowInput.ComputedMaxBSize());
8637 } else {
8638 bSize = computedBSize;
8640 if (bSize != NS_UNCONSTRAINEDSIZE) {
8641 bSize = std::max(bSize - consumedBSize, 0);
8643 auto& bp = gridReflowInput.mBorderPadding;
8644 LogicalRect contentArea(wm, bp.IStart(wm), bp.BStart(wm), computedISize,
8645 bSize);
8647 if (!prevInFlow) {
8648 const auto& rowSizes = gridReflowInput.mRows.mSizes;
8649 if (!IsRowSubgrid()) {
8650 // Apply 'align-content' to the grid.
8651 if (computedBSize == NS_UNCONSTRAINEDSIZE &&
8652 stylePos->mRowGap.IsLengthPercentage() &&
8653 stylePos->mRowGap.AsLengthPercentage().HasPercent()) {
8654 // Re-resolve the row-gap now that we know our intrinsic block-size.
8655 gridReflowInput.mRows.mGridGap =
8656 nsLayoutUtils::ResolveGapToLength(stylePos->mRowGap, bSize);
8658 if (!gridReflowInput.mRows.mIsMasonry) {
8659 auto alignment = stylePos->mAlignContent;
8660 gridReflowInput.mRows.AlignJustifyContent(stylePos, alignment, wm,
8661 bSize, false);
8663 } else {
8664 if (computedBSize == NS_UNCONSTRAINEDSIZE) {
8665 bSize = gridReflowInput.mRows.GridLineEdge(rowSizes.Length(),
8666 GridLineSide::BeforeGridGap);
8667 contentArea.BSize(wm) = std::max(bSize, nscoord(0));
8670 // Save the final row sizes for use by subgrids, if needed.
8671 if (HasSubgridItems() || IsSubgrid()) {
8672 StoreUsedTrackSizes(eLogicalAxisBlock, rowSizes);
8676 nsSize containerSize = contentArea.Size(wm).GetPhysicalSize(wm);
8677 bool repositionChildren = false;
8678 if (containerSize.width == NS_UNCONSTRAINEDSIZE && wm.IsVerticalRL()) {
8679 // Note that writing-mode:vertical-rl is the only case where the block
8680 // logical direction progresses in a negative physical direction, and
8681 // therefore block-dir coordinate conversion depends on knowing the width
8682 // of the coordinate space in order to translate between the logical and
8683 // physical origins.
8685 // A masonry axis size may be unconstrained, otherwise in a regular grid
8686 // our intrinsic size is always known by now. We'll re-position
8687 // the children below once our size is known.
8688 repositionChildren = true;
8689 containerSize.width = 0;
8691 containerSize.width += bp.LeftRight(wm);
8692 containerSize.height += bp.TopBottom(wm);
8694 bSize = ReflowChildren(gridReflowInput, contentArea, containerSize,
8695 aDesiredSize, aStatus);
8696 bSize = std::max(bSize - consumedBSize, 0);
8698 // Skip our block-end border if we're INCOMPLETE.
8699 if (!aStatus.IsComplete() && !gridReflowInput.mSkipSides.BEnd() &&
8700 StyleBorder()->mBoxDecorationBreak != StyleBoxDecorationBreak::Clone) {
8701 bp.BEnd(wm) = nscoord(0);
8704 LogicalSize desiredSize(wm, computedISize + bp.IStartEnd(wm),
8705 bSize + bp.BStartEnd(wm));
8706 aDesiredSize.SetSize(wm, desiredSize);
8707 nsRect frameRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height());
8708 aDesiredSize.mOverflowAreas.UnionAllWith(frameRect);
8710 if (repositionChildren) {
8711 nsPoint physicalDelta(aDesiredSize.Width() - bp.LeftRight(wm), 0);
8712 for (const auto& item : gridReflowInput.mGridItems) {
8713 auto* child = item.mFrame;
8714 child->MovePositionBy(physicalDelta);
8715 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child);
8719 if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
8720 // Per spec, the grid area is included in a grid container's scrollable
8721 // overflow region [1], as well as the padding on the end-edge sides that
8722 // would satisfy the requirements of 'place-content: end' alignment [2].
8724 // Note that we include the padding from all sides of the grid area, not
8725 // just the end sides; this is fine because the grid area is relative to our
8726 // content-box origin. The inflated bounds won't go beyond our padding-box
8727 // edges on the start sides.
8729 // The margin areas of grid item boxes are also included in the scrollable
8730 // overflow region [2].
8732 // [1] https://drafts.csswg.org/css-grid-1/#overflow
8733 // [2] https://drafts.csswg.org/css-overflow-3/#scrollable
8735 // Synthesize a grid area covering all columns and rows, and compute its
8736 // rect relative to our border-box.
8738 // Note: the grid columns and rows exist only if there is an explicit grid;
8739 // or when an implicit grid is needed to place any grid items. See
8740 // nsGridContainerFrame::Grid::PlaceGridItems().
8741 const auto numCols =
8742 static_cast<int32_t>(gridReflowInput.mCols.mSizes.Length());
8743 const auto numRows =
8744 static_cast<int32_t>(gridReflowInput.mRows.mSizes.Length());
8745 if (numCols > 0 && numRows > 0) {
8746 const GridArea gridArea(LineRange(0, numCols), LineRange(0, numRows));
8747 const LogicalRect gridAreaRect =
8748 gridReflowInput.ContainingBlockFor(gridArea) +
8749 LogicalPoint(wm, bp.IStart(wm), bp.BStart(wm));
8751 MOZ_ASSERT(bp == aReflowInput.ComputedLogicalPadding(wm),
8752 "A scrolled inner frame shouldn't have any border!");
8753 const LogicalMargin& padding = bp;
8754 nsRect physicalGridAreaRectWithPadding =
8755 gridAreaRect.GetPhysicalRect(wm, containerSize);
8756 physicalGridAreaRectWithPadding.Inflate(padding.GetPhysicalMargin(wm));
8757 aDesiredSize.mOverflowAreas.UnionAllWith(physicalGridAreaRectWithPadding);
8760 nsRect gridItemMarginBoxBounds;
8761 for (const auto& item : gridReflowInput.mGridItems) {
8762 gridItemMarginBoxBounds =
8763 gridItemMarginBoxBounds.Union(item.mFrame->GetMarginRect());
8765 aDesiredSize.mOverflowAreas.UnionAllWith(gridItemMarginBoxBounds);
8768 // TODO: fix align-tracks alignment in fragments
8769 if ((IsMasonry(eLogicalAxisBlock) && !prevInFlow) ||
8770 IsMasonry(eLogicalAxisInline)) {
8771 gridReflowInput.AlignJustifyTracksInMasonryAxis(
8772 contentArea.Size(wm), aDesiredSize.PhysicalSize());
8775 // Convert INCOMPLETE -> OVERFLOW_INCOMPLETE and zero bsize if we're an OC.
8776 if (HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
8777 if (!aStatus.IsComplete()) {
8778 aStatus.SetOverflowIncomplete();
8779 aStatus.SetNextInFlowNeedsReflow();
8781 bSize = 0;
8782 desiredSize.BSize(wm) = bSize + bp.BStartEnd(wm);
8783 aDesiredSize.SetSize(wm, desiredSize);
8786 if (!gridReflowInput.mInFragmentainer) {
8787 MOZ_ASSERT(gridReflowInput.mIter.IsValid());
8788 auto sz = frameRect.Size();
8789 CalculateBaselines(BaselineSet::eBoth, &gridReflowInput.mIter,
8790 &gridReflowInput.mGridItems, gridReflowInput.mCols, 0,
8791 gridReflowInput.mCols.mSizes.Length(), wm, sz,
8792 bp.IStart(wm), bp.IEnd(wm), desiredSize.ISize(wm));
8793 CalculateBaselines(BaselineSet::eBoth, &gridReflowInput.mIter,
8794 &gridReflowInput.mGridItems, gridReflowInput.mRows, 0,
8795 gridReflowInput.mRows.mSizes.Length(), wm, sz,
8796 bp.BStart(wm), bp.BEnd(wm), desiredSize.BSize(wm));
8797 } else {
8798 // Only compute 'first baseline' if this fragment contains the first track.
8799 // XXXmats maybe remove this condition? bug 1306499
8800 BaselineSet baselines = BaselineSet::eNone;
8801 if (gridReflowInput.mStartRow == 0 &&
8802 gridReflowInput.mStartRow != gridReflowInput.mNextFragmentStartRow) {
8803 baselines = BaselineSet::eFirst;
8805 // Only compute 'last baseline' if this fragment contains the last track.
8806 // XXXmats maybe remove this condition? bug 1306499
8807 uint32_t len = gridReflowInput.mRows.mSizes.Length();
8808 if (gridReflowInput.mStartRow != len &&
8809 gridReflowInput.mNextFragmentStartRow == len) {
8810 baselines = BaselineSet(baselines | BaselineSet::eLast);
8812 Maybe<CSSOrderAwareFrameIterator> iter;
8813 Maybe<nsTArray<GridItemInfo>> gridItems;
8814 if (baselines != BaselineSet::eNone) {
8815 // We need to create a new iterator and GridItemInfo array because we
8816 // might have pushed some children at this point.
8817 // Even if the gridReflowInput iterator is invalid we can reuse its
8818 // state about order to optimize initialization of the new iterator.
8819 // An ordered child list can't become unordered by pushing frames.
8820 // An unordered list can become ordered in a number of cases, but we
8821 // ignore that here and guess that the child list is still unordered.
8822 // XXX this is O(n^2) in the number of items in this fragment: bug 1306705
8823 using Filter = CSSOrderAwareFrameIterator::ChildFilter;
8824 using Order = CSSOrderAwareFrameIterator::OrderState;
8825 bool ordered = gridReflowInput.mIter.ItemsAreAlreadyInOrder();
8826 auto orderState = ordered ? Order::Ordered : Order::Unordered;
8827 iter.emplace(this, kPrincipalList, Filter::SkipPlaceholders, orderState);
8828 gridItems.emplace();
8829 for (; !iter->AtEnd(); iter->Next()) {
8830 auto child = **iter;
8831 for (const auto& info : gridReflowInput.mGridItems) {
8832 if (info.mFrame == child) {
8833 gridItems->AppendElement(info);
8838 auto sz = frameRect.Size();
8839 CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
8840 gridReflowInput.mCols, 0,
8841 gridReflowInput.mCols.mSizes.Length(), wm, sz,
8842 bp.IStart(wm), bp.IEnd(wm), desiredSize.ISize(wm));
8843 CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
8844 gridReflowInput.mRows, gridReflowInput.mStartRow,
8845 gridReflowInput.mNextFragmentStartRow, wm, sz,
8846 bp.BStart(wm), bp.BEnd(wm), desiredSize.BSize(wm));
8849 if (ShouldGenerateComputedInfo()) {
8850 // This state bit will never be cleared, since reflow can be called
8851 // multiple times in fragmented grids, and it's challenging to scope
8852 // the bit to only that sequence of calls. This is relatively harmless
8853 // since this bit is only set by accessing a ChromeOnly property, and
8854 // therefore can't unduly slow down normal web browsing.
8856 // Now that we know column and row sizes and positions, set
8857 // the ComputedGridTrackInfo and related properties
8859 const auto* subgrid = GetProperty(Subgrid::Prop());
8860 const auto* subgridColRange = subgrid && IsSubgrid(eLogicalAxisInline)
8861 ? &subgrid->SubgridCols()
8862 : nullptr;
8864 LineNameMap colLineNameMap(
8865 gridReflowInput.mGridStyle, GetImplicitNamedAreas(),
8866 gridReflowInput.mColFunctions, nullptr, subgridColRange, true);
8867 uint32_t colTrackCount = gridReflowInput.mCols.mSizes.Length();
8868 nsTArray<nscoord> colTrackPositions(colTrackCount);
8869 nsTArray<nscoord> colTrackSizes(colTrackCount);
8870 nsTArray<uint32_t> colTrackStates(colTrackCount);
8871 nsTArray<bool> colRemovedRepeatTracks(
8872 gridReflowInput.mColFunctions.mRemovedRepeatTracks.Clone());
8873 uint32_t col = 0;
8874 for (const TrackSize& sz : gridReflowInput.mCols.mSizes) {
8875 colTrackPositions.AppendElement(sz.mPosition);
8876 colTrackSizes.AppendElement(sz.mBase);
8877 bool isRepeat =
8878 ((col >= gridReflowInput.mColFunctions.mRepeatAutoStart) &&
8879 (col < gridReflowInput.mColFunctions.mRepeatAutoEnd));
8880 colTrackStates.AppendElement(
8881 isRepeat ? (uint32_t)mozilla::dom::GridTrackState::Repeat
8882 : (uint32_t)mozilla::dom::GridTrackState::Static);
8884 col++;
8886 // Get the number of explicit tracks first. The order of argument evaluation
8887 // is implementation-defined. We should be OK here because colTrackSizes is
8888 // taken by rvalue, but computing the size first prevents any changes in the
8889 // argument types of the constructor from breaking this.
8890 const uint32_t numColExplicitTracks =
8891 IsSubgrid(eLogicalAxisInline)
8892 ? colTrackSizes.Length()
8893 : gridReflowInput.mColFunctions.NumExplicitTracks();
8894 ComputedGridTrackInfo* colInfo = new ComputedGridTrackInfo(
8895 gridReflowInput.mColFunctions.mExplicitGridOffset, numColExplicitTracks,
8896 0, col, std::move(colTrackPositions), std::move(colTrackSizes),
8897 std::move(colTrackStates), std::move(colRemovedRepeatTracks),
8898 gridReflowInput.mColFunctions.mRepeatAutoStart,
8899 colLineNameMap.GetResolvedLineNamesForComputedGridTrackInfo(),
8900 IsSubgrid(eLogicalAxisInline), IsMasonry(eLogicalAxisInline));
8901 SetProperty(GridColTrackInfo(), colInfo);
8903 const auto* subgridRowRange = subgrid && IsSubgrid(eLogicalAxisBlock)
8904 ? &subgrid->SubgridRows()
8905 : nullptr;
8906 LineNameMap rowLineNameMap(
8907 gridReflowInput.mGridStyle, GetImplicitNamedAreas(),
8908 gridReflowInput.mRowFunctions, nullptr, subgridRowRange, true);
8909 uint32_t rowTrackCount = gridReflowInput.mRows.mSizes.Length();
8910 nsTArray<nscoord> rowTrackPositions(rowTrackCount);
8911 nsTArray<nscoord> rowTrackSizes(rowTrackCount);
8912 nsTArray<uint32_t> rowTrackStates(rowTrackCount);
8913 nsTArray<bool> rowRemovedRepeatTracks(
8914 gridReflowInput.mRowFunctions.mRemovedRepeatTracks.Clone());
8915 uint32_t row = 0;
8916 for (const TrackSize& sz : gridReflowInput.mRows.mSizes) {
8917 rowTrackPositions.AppendElement(sz.mPosition);
8918 rowTrackSizes.AppendElement(sz.mBase);
8919 bool isRepeat =
8920 ((row >= gridReflowInput.mRowFunctions.mRepeatAutoStart) &&
8921 (row < gridReflowInput.mRowFunctions.mRepeatAutoEnd));
8922 rowTrackStates.AppendElement(
8923 isRepeat ? (uint32_t)mozilla::dom::GridTrackState::Repeat
8924 : (uint32_t)mozilla::dom::GridTrackState::Static);
8926 row++;
8928 // Get the number of explicit tracks first. The order of argument evaluation
8929 // is implementation-defined. We should be OK here because colTrackSizes is
8930 // taken by rvalue, but computing the size first prevents any changes in the
8931 // argument types of the constructor from breaking this.
8932 const uint32_t numRowExplicitTracks =
8933 IsSubgrid(eLogicalAxisBlock)
8934 ? rowTrackSizes.Length()
8935 : gridReflowInput.mRowFunctions.NumExplicitTracks();
8936 // Row info has to accommodate fragmentation of the grid, which may happen
8937 // in later calls to Reflow. For now, presume that no more fragmentation
8938 // will occur.
8939 ComputedGridTrackInfo* rowInfo = new ComputedGridTrackInfo(
8940 gridReflowInput.mRowFunctions.mExplicitGridOffset, numRowExplicitTracks,
8941 gridReflowInput.mStartRow, row, std::move(rowTrackPositions),
8942 std::move(rowTrackSizes), std::move(rowTrackStates),
8943 std::move(rowRemovedRepeatTracks),
8944 gridReflowInput.mRowFunctions.mRepeatAutoStart,
8945 rowLineNameMap.GetResolvedLineNamesForComputedGridTrackInfo(),
8946 IsSubgrid(eLogicalAxisBlock), IsMasonry(eLogicalAxisBlock));
8947 SetProperty(GridRowTrackInfo(), rowInfo);
8949 if (prevInFlow) {
8950 // This frame is fragmenting rows from a previous frame, so patch up
8951 // the prior GridRowTrackInfo with a new end row.
8953 // FIXME: This can be streamlined and/or removed when bug 1151204 lands.
8955 ComputedGridTrackInfo* priorRowInfo =
8956 prevInFlow->GetProperty(GridRowTrackInfo());
8958 // Adjust track positions based on the first track in this fragment.
8959 if (priorRowInfo->mPositions.Length() >
8960 priorRowInfo->mStartFragmentTrack) {
8961 nscoord delta =
8962 priorRowInfo->mPositions[priorRowInfo->mStartFragmentTrack];
8963 for (nscoord& pos : priorRowInfo->mPositions) {
8964 pos -= delta;
8968 ComputedGridTrackInfo* revisedPriorRowInfo = new ComputedGridTrackInfo(
8969 priorRowInfo->mNumLeadingImplicitTracks,
8970 priorRowInfo->mNumExplicitTracks, priorRowInfo->mStartFragmentTrack,
8971 gridReflowInput.mStartRow, std::move(priorRowInfo->mPositions),
8972 std::move(priorRowInfo->mSizes), std::move(priorRowInfo->mStates),
8973 std::move(priorRowInfo->mRemovedRepeatTracks),
8974 priorRowInfo->mRepeatFirstTrack,
8975 std::move(priorRowInfo->mResolvedLineNames), priorRowInfo->mIsSubgrid,
8976 priorRowInfo->mIsMasonry);
8977 prevInFlow->SetProperty(GridRowTrackInfo(), revisedPriorRowInfo);
8980 // Generate the line info properties. We need to provide the number of
8981 // repeat tracks produced in the reflow. Only explicit names are assigned
8982 // to lines here; the mozilla::dom::GridLines class will later extract
8983 // implicit names from grid areas and assign them to the appropriate lines.
8985 auto& colFunctions = gridReflowInput.mColFunctions;
8987 // Generate column lines first.
8988 uint32_t capacity = gridReflowInput.mCols.mSizes.Length();
8989 nsTArray<nsTArray<RefPtr<nsAtom>>> columnLineNames(capacity);
8990 for (col = 0; col <= gridReflowInput.mCols.mSizes.Length(); col++) {
8991 // Offset col by the explicit grid offset, to get the original names.
8992 nsTArray<RefPtr<nsAtom>> explicitNames =
8993 colLineNameMap.GetExplicitLineNamesAtIndex(
8994 col - colFunctions.mExplicitGridOffset);
8996 columnLineNames.EmplaceBack(std::move(explicitNames));
8998 // Get the explicit names that follow a repeat auto declaration.
8999 nsTArray<RefPtr<nsAtom>> colNamesFollowingRepeat;
9000 nsTArray<RefPtr<nsAtom>> colBeforeRepeatAuto;
9001 nsTArray<RefPtr<nsAtom>> colAfterRepeatAuto;
9002 // Note: the following is only used for a non-subgridded axis.
9003 if (colLineNameMap.HasRepeatAuto()) {
9004 MOZ_ASSERT(!colFunctions.mTemplate.IsSubgrid());
9005 // The line name list after the repeatAutoIndex holds the line names
9006 // for the first explicit line after the repeat auto declaration.
9007 uint32_t repeatAutoEnd = colLineNameMap.RepeatAutoStart() + 1;
9008 for (auto* list : colLineNameMap.ExpandedLineNames()[repeatAutoEnd]) {
9009 for (auto& name : list->AsSpan()) {
9010 colNamesFollowingRepeat.AppendElement(name.AsAtom());
9013 auto names = colLineNameMap.TrackAutoRepeatLineNames();
9014 for (auto& name : names[0].AsSpan()) {
9015 colBeforeRepeatAuto.AppendElement(name.AsAtom());
9017 for (auto& name : names[1].AsSpan()) {
9018 colAfterRepeatAuto.AppendElement(name.AsAtom());
9022 ComputedGridLineInfo* columnLineInfo = new ComputedGridLineInfo(
9023 std::move(columnLineNames), std::move(colBeforeRepeatAuto),
9024 std::move(colAfterRepeatAuto), std::move(colNamesFollowingRepeat));
9025 SetProperty(GridColumnLineInfo(), columnLineInfo);
9027 // Generate row lines next.
9028 auto& rowFunctions = gridReflowInput.mRowFunctions;
9029 capacity = gridReflowInput.mRows.mSizes.Length();
9030 nsTArray<nsTArray<RefPtr<nsAtom>>> rowLineNames(capacity);
9031 for (row = 0; row <= gridReflowInput.mRows.mSizes.Length(); row++) {
9032 // Offset row by the explicit grid offset, to get the original names.
9033 nsTArray<RefPtr<nsAtom>> explicitNames =
9034 rowLineNameMap.GetExplicitLineNamesAtIndex(
9035 row - rowFunctions.mExplicitGridOffset);
9036 rowLineNames.EmplaceBack(std::move(explicitNames));
9038 // Get the explicit names that follow a repeat auto declaration.
9039 nsTArray<RefPtr<nsAtom>> rowNamesFollowingRepeat;
9040 nsTArray<RefPtr<nsAtom>> rowBeforeRepeatAuto;
9041 nsTArray<RefPtr<nsAtom>> rowAfterRepeatAuto;
9042 // Note: the following is only used for a non-subgridded axis.
9043 if (rowLineNameMap.HasRepeatAuto()) {
9044 MOZ_ASSERT(!rowFunctions.mTemplate.IsSubgrid());
9045 // The line name list after the repeatAutoIndex holds the line names
9046 // for the first explicit line after the repeat auto declaration.
9047 uint32_t repeatAutoEnd = rowLineNameMap.RepeatAutoStart() + 1;
9048 for (auto* list : rowLineNameMap.ExpandedLineNames()[repeatAutoEnd]) {
9049 for (auto& name : list->AsSpan()) {
9050 rowNamesFollowingRepeat.AppendElement(name.AsAtom());
9053 auto names = rowLineNameMap.TrackAutoRepeatLineNames();
9054 for (auto& name : names[0].AsSpan()) {
9055 rowBeforeRepeatAuto.AppendElement(name.AsAtom());
9057 for (auto& name : names[1].AsSpan()) {
9058 rowAfterRepeatAuto.AppendElement(name.AsAtom());
9062 ComputedGridLineInfo* rowLineInfo = new ComputedGridLineInfo(
9063 std::move(rowLineNames), std::move(rowBeforeRepeatAuto),
9064 std::move(rowAfterRepeatAuto), std::move(rowNamesFollowingRepeat));
9065 SetProperty(GridRowLineInfo(), rowLineInfo);
9067 // Generate area info for explicit areas. Implicit areas are handled
9068 // elsewhere.
9069 if (!gridReflowInput.mGridStyle->mGridTemplateAreas.IsNone()) {
9070 auto* areas = new StyleOwnedSlice<NamedArea>(
9071 gridReflowInput.mGridStyle->mGridTemplateAreas.AsAreas()->areas);
9072 SetProperty(ExplicitNamedAreasProperty(), areas);
9073 } else {
9074 RemoveProperty(ExplicitNamedAreasProperty());
9078 if (!prevInFlow) {
9079 SharedGridData* sharedGridData = GetProperty(SharedGridData::Prop());
9080 if (!aStatus.IsFullyComplete()) {
9081 if (!sharedGridData) {
9082 sharedGridData = new SharedGridData;
9083 SetProperty(SharedGridData::Prop(), sharedGridData);
9085 sharedGridData->mCols.mSizes = std::move(gridReflowInput.mCols.mSizes);
9086 sharedGridData->mCols.mContentBoxSize =
9087 gridReflowInput.mCols.mContentBoxSize;
9088 sharedGridData->mCols.mBaselineSubtreeAlign =
9089 gridReflowInput.mCols.mBaselineSubtreeAlign;
9090 sharedGridData->mCols.mIsMasonry = gridReflowInput.mCols.mIsMasonry;
9091 sharedGridData->mRows.mSizes = std::move(gridReflowInput.mRows.mSizes);
9092 // Save the original row grid sizes and gaps so we can restore them later
9093 // in GridReflowInput::Initialize for the continuations.
9094 auto& origRowData = sharedGridData->mOriginalRowData;
9095 origRowData.ClearAndRetainStorage();
9096 origRowData.SetCapacity(sharedGridData->mRows.mSizes.Length());
9097 nscoord prevTrackEnd = 0;
9098 for (auto& sz : sharedGridData->mRows.mSizes) {
9099 SharedGridData::RowData data = {sz.mBase, sz.mPosition - prevTrackEnd};
9100 origRowData.AppendElement(data);
9101 prevTrackEnd = sz.mPosition + sz.mBase;
9103 sharedGridData->mRows.mContentBoxSize =
9104 gridReflowInput.mRows.mContentBoxSize;
9105 sharedGridData->mRows.mBaselineSubtreeAlign =
9106 gridReflowInput.mRows.mBaselineSubtreeAlign;
9107 sharedGridData->mRows.mIsMasonry = gridReflowInput.mRows.mIsMasonry;
9108 sharedGridData->mGridItems = std::move(gridReflowInput.mGridItems);
9109 sharedGridData->mAbsPosItems = std::move(gridReflowInput.mAbsPosItems);
9111 sharedGridData->mGenerateComputedGridInfo = ShouldGenerateComputedInfo();
9112 } else if (sharedGridData && !GetNextInFlow()) {
9113 RemoveProperty(SharedGridData::Prop());
9117 FinishAndStoreOverflow(&aDesiredSize);
9118 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
9121 void nsGridContainerFrame::UpdateSubgridFrameState() {
9122 nsFrameState oldBits = GetStateBits() & kIsSubgridBits;
9123 nsFrameState newBits = ComputeSelfSubgridMasonryBits() & kIsSubgridBits;
9124 if (newBits != oldBits) {
9125 RemoveStateBits(kIsSubgridBits);
9126 if (!newBits) {
9127 RemoveProperty(Subgrid::Prop());
9128 } else {
9129 AddStateBits(newBits);
9134 nsFrameState nsGridContainerFrame::ComputeSelfSubgridMasonryBits() const {
9135 // 'contain:layout/paint' makes us an "independent formatting context",
9136 // which prevents us from being a subgrid in this case (but not always).
9137 // We will also need to check our containing scroll frame for this property.
9138 // https://drafts.csswg.org/css-display-3/#establish-an-independent-formatting-context
9139 const auto* display = StyleDisplay();
9140 const bool inhibitSubgrid =
9141 display->IsContainLayout() || display->IsContainPaint();
9143 nsFrameState bits = nsFrameState(0);
9144 const auto* pos = StylePosition();
9146 // We can only have masonry layout in one axis.
9147 if (pos->mGridTemplateRows.IsMasonry()) {
9148 bits |= NS_STATE_GRID_IS_ROW_MASONRY;
9149 } else if (pos->mGridTemplateColumns.IsMasonry()) {
9150 bits |= NS_STATE_GRID_IS_COL_MASONRY;
9153 // Skip our scroll frame and such if we have it.
9154 // This will store the outermost frame that shares our content node:
9155 const nsIFrame* outerFrame = this;
9156 // ...and this will store that frame's parent:
9157 auto* parent = GetParent();
9158 while (parent && parent->GetContent() == GetContent()) {
9159 // If we find our containing frame has 'contain:layout/paint' we can't be
9160 // subgrid, for the same reasons as above. This can happen when this frame
9161 // is itself a grid item.
9162 const auto* parentDisplay = parent->StyleDisplay();
9163 if (parentDisplay->IsContainLayout() || parentDisplay->IsContainPaint()) {
9164 return nsFrameState(0);
9166 outerFrame = parent;
9167 parent = parent->GetParent();
9169 const nsGridContainerFrame* gridParent = do_QueryFrame(parent);
9170 if (gridParent) {
9171 bool isOrthogonal =
9172 GetWritingMode().IsOrthogonalTo(parent->GetWritingMode());
9173 // NOTE: our NS_FRAME_OUT_OF_FLOW isn't set yet so we check our style.
9174 bool isOutOfFlow =
9175 outerFrame->StyleDisplay()->IsAbsolutelyPositionedStyle();
9176 bool isColSubgrid =
9177 pos->mGridTemplateColumns.IsSubgrid() && !inhibitSubgrid;
9178 // Subgridding a parent masonry axis makes us use masonry layout too,
9179 // unless our other axis is a masonry axis.
9180 if (isColSubgrid &&
9181 parent->HasAnyStateBits(isOrthogonal ? NS_STATE_GRID_IS_ROW_MASONRY
9182 : NS_STATE_GRID_IS_COL_MASONRY)) {
9183 isColSubgrid = false;
9184 if (!HasAnyStateBits(NS_STATE_GRID_IS_ROW_MASONRY)) {
9185 bits |= NS_STATE_GRID_IS_COL_MASONRY;
9188 // OOF subgrids don't create tracks in the parent, so we need to check that
9189 // it has one anyway. Otherwise we refuse to subgrid that axis since we
9190 // can't place grid items inside a subgrid without at least one track.
9191 if (isColSubgrid && isOutOfFlow) {
9192 auto parentAxis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
9193 if (!gridParent->WillHaveAtLeastOneTrackInAxis(parentAxis)) {
9194 isColSubgrid = false;
9197 if (isColSubgrid) {
9198 bits |= NS_STATE_GRID_IS_COL_SUBGRID;
9201 bool isRowSubgrid = pos->mGridTemplateRows.IsSubgrid() && !inhibitSubgrid;
9202 if (isRowSubgrid &&
9203 parent->HasAnyStateBits(isOrthogonal ? NS_STATE_GRID_IS_COL_MASONRY
9204 : NS_STATE_GRID_IS_ROW_MASONRY)) {
9205 isRowSubgrid = false;
9206 if (!HasAnyStateBits(NS_STATE_GRID_IS_COL_MASONRY)) {
9207 bits |= NS_STATE_GRID_IS_ROW_MASONRY;
9210 if (isRowSubgrid && isOutOfFlow) {
9211 auto parentAxis = isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock;
9212 if (!gridParent->WillHaveAtLeastOneTrackInAxis(parentAxis)) {
9213 isRowSubgrid = false;
9216 if (isRowSubgrid) {
9217 bits |= NS_STATE_GRID_IS_ROW_SUBGRID;
9220 return bits;
9223 bool nsGridContainerFrame::WillHaveAtLeastOneTrackInAxis(
9224 LogicalAxis aAxis) const {
9225 if (IsSubgrid(aAxis)) {
9226 // This is enforced by refusing to be a subgrid unless our parent has
9227 // at least one track in aAxis by ComputeSelfSubgridMasonryBits above.
9228 return true;
9230 if (IsMasonry(aAxis)) {
9231 return false;
9233 const auto* pos = StylePosition();
9234 const auto& gridTemplate = aAxis == eLogicalAxisBlock
9235 ? pos->mGridTemplateRows
9236 : pos->mGridTemplateColumns;
9237 if (gridTemplate.IsTrackList()) {
9238 return true;
9240 for (nsIFrame* child : PrincipalChildList()) {
9241 if (!child->IsPlaceholderFrame()) {
9242 // A grid item triggers at least one implicit track in each axis.
9243 return true;
9246 if (!pos->mGridTemplateAreas.IsNone()) {
9247 return true;
9249 return false;
9252 void nsGridContainerFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
9253 nsIFrame* aPrevInFlow) {
9254 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
9256 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
9257 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
9260 nsFrameState bits = nsFrameState(0);
9261 if (MOZ_LIKELY(!aPrevInFlow)) {
9262 bits = ComputeSelfSubgridMasonryBits();
9263 } else {
9264 bits = aPrevInFlow->GetStateBits() &
9265 (NS_STATE_GRID_IS_ROW_MASONRY | NS_STATE_GRID_IS_COL_MASONRY |
9266 kIsSubgridBits | NS_STATE_GRID_HAS_COL_SUBGRID_ITEM |
9267 NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
9269 AddStateBits(bits);
9272 void nsGridContainerFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
9273 nsContainerFrame::DidSetComputedStyle(aOldStyle);
9275 if (!aOldStyle) {
9276 return; // Init() already initialized the bits.
9278 UpdateSubgridFrameState();
9281 nscoord nsGridContainerFrame::IntrinsicISize(gfxContext* aRenderingContext,
9282 IntrinsicISizeType aType) {
9283 // Calculate the sum of column sizes under intrinsic sizing.
9284 // http://dev.w3.org/csswg/css-grid/#intrinsic-sizes
9285 NormalizeChildLists();
9286 GridReflowInput state(this, *aRenderingContext);
9287 InitImplicitNamedAreas(state.mGridStyle); // XXX optimize
9289 // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
9290 // https://drafts.csswg.org/css-grid/#auto-repeat
9291 // They're only used for auto-repeat so we skip computing them otherwise.
9292 RepeatTrackSizingInput repeatSizing(state.mWM);
9293 if (!IsColSubgrid() && state.mColFunctions.mHasRepeatAuto) {
9294 repeatSizing.InitFromStyle(eLogicalAxisInline, state.mWM,
9295 state.mFrame->Style());
9297 if ((!IsRowSubgrid() && state.mRowFunctions.mHasRepeatAuto &&
9298 !(state.mGridStyle->mGridAutoFlow & StyleGridAutoFlow::ROW)) ||
9299 IsMasonry(eLogicalAxisInline)) {
9300 // Only 'grid-auto-flow:column' can create new implicit columns, so that's
9301 // the only case where our block-size can affect the number of columns.
9302 // Masonry layout always depends on how many rows we have though.
9303 repeatSizing.InitFromStyle(eLogicalAxisBlock, state.mWM,
9304 state.mFrame->Style());
9307 Grid grid;
9308 if (MOZ_LIKELY(!IsSubgrid())) {
9309 grid.PlaceGridItems(state, repeatSizing); // XXX optimize
9310 } else {
9311 auto* subgrid = GetProperty(Subgrid::Prop());
9312 state.mGridItems = subgrid->mGridItems.Clone();
9313 state.mAbsPosItems = subgrid->mAbsPosItems.Clone();
9314 grid.mGridColEnd = subgrid->mGridColEnd;
9315 grid.mGridRowEnd = subgrid->mGridRowEnd;
9318 auto constraint = aType == IntrinsicISizeType::MinISize
9319 ? SizingConstraint::MinContent
9320 : SizingConstraint::MaxContent;
9321 if (IsMasonry(eLogicalAxisInline)) {
9322 ReflowOutput desiredSize(state.mWM);
9323 nsSize containerSize;
9324 LogicalRect contentArea(state.mWM);
9325 nsReflowStatus status;
9326 state.mRows.mSizes.SetLength(grid.mGridRowEnd);
9327 state.CalculateTrackSizesForAxis(eLogicalAxisInline, grid,
9328 NS_UNCONSTRAINEDSIZE, constraint);
9329 return MasonryLayout(state, contentArea, constraint, desiredSize, status,
9330 nullptr, containerSize);
9333 if (grid.mGridColEnd == 0) {
9334 return nscoord(0);
9337 state.CalculateTrackSizesForAxis(eLogicalAxisInline, grid,
9338 NS_UNCONSTRAINEDSIZE, constraint);
9340 if (MOZ_LIKELY(!IsSubgrid())) {
9341 return state.mCols.SumOfGridTracksAndGaps();
9343 const auto& last = state.mCols.mSizes.LastElement();
9344 return last.mPosition + last.mBase;
9347 nscoord nsGridContainerFrame::GetMinISize(gfxContext* aRC) {
9348 auto* f = static_cast<nsGridContainerFrame*>(FirstContinuation());
9349 if (f != this) {
9350 return f->GetMinISize(aRC);
9353 DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize);
9354 if (mCachedMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
9355 mCachedMinISize = StyleDisplay()->IsContainSize()
9357 : IntrinsicISize(aRC, IntrinsicISizeType::MinISize);
9359 return mCachedMinISize;
9362 nscoord nsGridContainerFrame::GetPrefISize(gfxContext* aRC) {
9363 auto* f = static_cast<nsGridContainerFrame*>(FirstContinuation());
9364 if (f != this) {
9365 return f->GetPrefISize(aRC);
9368 DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize);
9369 if (mCachedPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
9370 mCachedPrefISize = StyleDisplay()->IsContainSize()
9372 : IntrinsicISize(aRC, IntrinsicISizeType::PrefISize);
9374 return mCachedPrefISize;
9377 void nsGridContainerFrame::MarkIntrinsicISizesDirty() {
9378 mCachedMinISize = NS_INTRINSIC_ISIZE_UNKNOWN;
9379 mCachedPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN;
9380 for (auto& perAxisBaseline : mBaseline) {
9381 for (auto& baseline : perAxisBaseline) {
9382 baseline = NS_INTRINSIC_ISIZE_UNKNOWN;
9385 nsContainerFrame::MarkIntrinsicISizesDirty();
9388 void nsGridContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
9389 const nsDisplayListSet& aLists) {
9390 DisplayBorderBackgroundOutline(aBuilder, aLists);
9391 if (GetPrevInFlow()) {
9392 DisplayOverflowContainers(aBuilder, aLists);
9395 // Our children are all grid-level boxes, which behave the same as
9396 // inline-blocks in painting, so their borders/backgrounds all go on
9397 // the BlockBorderBackgrounds list.
9398 typedef CSSOrderAwareFrameIterator::OrderState OrderState;
9399 OrderState order =
9400 HasAnyStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
9401 ? OrderState::Ordered
9402 : OrderState::Unordered;
9403 CSSOrderAwareFrameIterator iter(
9404 this, kPrincipalList, CSSOrderAwareFrameIterator::ChildFilter::IncludeAll,
9405 order);
9406 for (; !iter.AtEnd(); iter.Next()) {
9407 nsIFrame* child = *iter;
9408 BuildDisplayListForChild(aBuilder, child, aLists,
9409 child->DisplayFlagForFlexOrGridItem());
9413 bool nsGridContainerFrame::DrainSelfOverflowList() {
9414 return DrainAndMergeSelfOverflowList();
9417 void nsGridContainerFrame::AppendFrames(ChildListID aListID,
9418 nsFrameList& aFrameList) {
9419 NoteNewChildren(aListID, aFrameList);
9420 nsContainerFrame::AppendFrames(aListID, aFrameList);
9423 void nsGridContainerFrame::InsertFrames(
9424 ChildListID aListID, nsIFrame* aPrevFrame,
9425 const nsLineList::iterator* aPrevFrameLine, nsFrameList& aFrameList) {
9426 NoteNewChildren(aListID, aFrameList);
9427 nsContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
9428 aFrameList);
9431 void nsGridContainerFrame::RemoveFrame(ChildListID aListID,
9432 nsIFrame* aOldFrame) {
9433 MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
9435 #ifdef DEBUG
9436 SetDidPushItemsBitIfNeeded(aListID, aOldFrame);
9437 #endif
9439 nsContainerFrame::RemoveFrame(aListID, aOldFrame);
9442 StyleAlignFlags nsGridContainerFrame::CSSAlignmentForAbsPosChild(
9443 const ReflowInput& aChildRI, LogicalAxis aLogicalAxis) const {
9444 MOZ_ASSERT(aChildRI.mFrame->IsAbsolutelyPositioned(),
9445 "This method should only be called for abspos children");
9447 StyleAlignFlags alignment =
9448 (aLogicalAxis == eLogicalAxisInline)
9449 ? aChildRI.mStylePosition->UsedJustifySelf(Style())._0
9450 : aChildRI.mStylePosition->UsedAlignSelf(Style())._0;
9452 // Extract and strip the flag bits
9453 StyleAlignFlags alignmentFlags = alignment & StyleAlignFlags::FLAG_BITS;
9454 alignment &= ~StyleAlignFlags::FLAG_BITS;
9456 if (alignment == StyleAlignFlags::NORMAL) {
9457 // "the 'normal' keyword behaves as 'start' on replaced
9458 // absolutely-positioned boxes, and behaves as 'stretch' on all other
9459 // absolutely-positioned boxes."
9460 // https://drafts.csswg.org/css-align/#align-abspos
9461 // https://drafts.csswg.org/css-align/#justify-abspos
9462 alignment = aChildRI.mFrame->IsFrameOfType(nsIFrame::eReplaced)
9463 ? StyleAlignFlags::START
9464 : StyleAlignFlags::STRETCH;
9465 } else if (alignment == StyleAlignFlags::FLEX_START) {
9466 alignment = StyleAlignFlags::START;
9467 } else if (alignment == StyleAlignFlags::FLEX_END) {
9468 alignment = StyleAlignFlags::END;
9469 } else if (alignment == StyleAlignFlags::LEFT ||
9470 alignment == StyleAlignFlags::RIGHT) {
9471 if (aLogicalAxis == eLogicalAxisInline) {
9472 const bool isLeft = (alignment == StyleAlignFlags::LEFT);
9473 WritingMode wm = GetWritingMode();
9474 alignment = (isLeft == wm.IsBidiLTR()) ? StyleAlignFlags::START
9475 : StyleAlignFlags::END;
9476 } else {
9477 alignment = StyleAlignFlags::START;
9479 } else if (alignment == StyleAlignFlags::BASELINE) {
9480 alignment = StyleAlignFlags::START;
9481 } else if (alignment == StyleAlignFlags::LAST_BASELINE) {
9482 alignment = StyleAlignFlags::END;
9485 return (alignment | alignmentFlags);
9488 nscoord nsGridContainerFrame::SynthesizeBaseline(
9489 const FindItemInGridOrderResult& aGridOrderItem, LogicalAxis aAxis,
9490 BaselineSharingGroup aGroup, const nsSize& aCBPhysicalSize, nscoord aCBSize,
9491 WritingMode aCBWM) {
9492 if (MOZ_UNLIKELY(!aGridOrderItem.mItem)) {
9493 // No item in this fragment - synthesize a baseline from our border-box.
9494 return ::SynthesizeBaselineFromBorderBox(aGroup, aCBWM, aCBSize);
9496 auto GetBBaseline = [](BaselineSharingGroup aGroup, WritingMode aWM,
9497 const nsIFrame* aFrame, nscoord* aBaseline) {
9498 return aGroup == BaselineSharingGroup::First
9499 ? nsLayoutUtils::GetFirstLineBaseline(aWM, aFrame, aBaseline)
9500 : nsLayoutUtils::GetLastLineBaseline(aWM, aFrame, aBaseline);
9502 nsIFrame* child = aGridOrderItem.mItem->mFrame;
9503 nsGridContainerFrame* grid = do_QueryFrame(child);
9504 auto childWM = child->GetWritingMode();
9505 bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
9506 nscoord baseline;
9507 nscoord start;
9508 nscoord size;
9509 if (aAxis == eLogicalAxisBlock) {
9510 start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM);
9511 size = child->BSize(aCBWM);
9512 if (grid && aGridOrderItem.mIsInEdgeTrack) {
9513 isOrthogonal ? grid->GetIBaseline(aGroup, &baseline)
9514 : grid->GetBBaseline(aGroup, &baseline);
9515 } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack) {
9516 baseline =
9517 child->BaselineBOffset(childWM, aGroup, AlignmentContext::Grid);
9518 } else {
9519 baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
9521 } else {
9522 start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).I(aCBWM);
9523 size = child->ISize(aCBWM);
9524 if (grid && aGridOrderItem.mIsInEdgeTrack) {
9525 isOrthogonal ? grid->GetBBaseline(aGroup, &baseline)
9526 : grid->GetIBaseline(aGroup, &baseline);
9527 } else if (isOrthogonal && aGridOrderItem.mIsInEdgeTrack &&
9528 GetBBaseline(aGroup, childWM, child, &baseline)) {
9529 if (aGroup == BaselineSharingGroup::Last) {
9530 baseline = size - baseline; // convert to distance from border-box end
9532 } else {
9533 baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
9536 return aGroup == BaselineSharingGroup::First
9537 ? start + baseline
9538 : aCBSize - start - size + baseline;
9541 void nsGridContainerFrame::CalculateBaselines(
9542 BaselineSet aBaselineSet, CSSOrderAwareFrameIterator* aIter,
9543 const nsTArray<GridItemInfo>* aGridItems, const Tracks& aTracks,
9544 uint32_t aFragmentStartTrack, uint32_t aFirstExcludedTrack, WritingMode aWM,
9545 const nsSize& aCBPhysicalSize, nscoord aCBBorderPaddingStart,
9546 nscoord aCBBorderPaddingEnd, nscoord aCBSize) {
9547 const auto axis = aTracks.mAxis;
9548 auto firstBaseline = aTracks.mBaseline[BaselineSharingGroup::First];
9549 if (!(aBaselineSet & BaselineSet::eFirst)) {
9550 mBaseline[axis][BaselineSharingGroup::First] =
9551 ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::First, aWM,
9552 aCBSize);
9553 } else if (firstBaseline == NS_INTRINSIC_ISIZE_UNKNOWN) {
9554 FindItemInGridOrderResult gridOrderFirstItem = FindFirstItemInGridOrder(
9555 *aIter, *aGridItems,
9556 axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols,
9557 axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows,
9558 aFragmentStartTrack);
9559 mBaseline[axis][BaselineSharingGroup::First] = SynthesizeBaseline(
9560 gridOrderFirstItem, axis, BaselineSharingGroup::First, aCBPhysicalSize,
9561 aCBSize, aWM);
9562 } else {
9563 // We have a 'first baseline' group in the start track in this fragment.
9564 // Convert it from track to grid container border-box coordinates.
9565 MOZ_ASSERT(!aGridItems->IsEmpty());
9566 nscoord gapBeforeStartTrack =
9567 aFragmentStartTrack == 0
9568 ? aTracks.GridLineEdge(aFragmentStartTrack,
9569 GridLineSide::AfterGridGap)
9570 : nscoord(0); // no content gap at start of fragment
9571 mBaseline[axis][BaselineSharingGroup::First] =
9572 aCBBorderPaddingStart + gapBeforeStartTrack + firstBaseline;
9575 auto lastBaseline = aTracks.mBaseline[BaselineSharingGroup::Last];
9576 if (!(aBaselineSet & BaselineSet::eLast)) {
9577 mBaseline[axis][BaselineSharingGroup::Last] =
9578 ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::Last, aWM,
9579 aCBSize);
9580 } else if (lastBaseline == NS_INTRINSIC_ISIZE_UNKNOWN) {
9581 // For finding items for the 'last baseline' we need to create a reverse
9582 // iterator ('aIter' is the forward iterator from the GridReflowInput).
9583 using Iter = ReverseCSSOrderAwareFrameIterator;
9584 auto orderState = aIter->ItemsAreAlreadyInOrder()
9585 ? Iter::OrderState::Ordered
9586 : Iter::OrderState::Unordered;
9587 Iter iter(this, kPrincipalList, Iter::ChildFilter::SkipPlaceholders,
9588 orderState);
9589 iter.SetItemCount(aGridItems->Length());
9590 FindItemInGridOrderResult gridOrderLastItem = FindLastItemInGridOrder(
9591 iter, *aGridItems,
9592 axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols,
9593 axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows,
9594 aFragmentStartTrack, aFirstExcludedTrack);
9595 mBaseline[axis][BaselineSharingGroup::Last] =
9596 SynthesizeBaseline(gridOrderLastItem, axis, BaselineSharingGroup::Last,
9597 aCBPhysicalSize, aCBSize, aWM);
9598 } else {
9599 // We have a 'last baseline' group in the end track in this fragment.
9600 // Convert it from track to grid container border-box coordinates.
9601 MOZ_ASSERT(!aGridItems->IsEmpty());
9602 auto borderBoxStartToEndOfEndTrack =
9603 aCBBorderPaddingStart +
9604 aTracks.GridLineEdge(aFirstExcludedTrack, GridLineSide::BeforeGridGap) -
9605 aTracks.GridLineEdge(aFragmentStartTrack, GridLineSide::BeforeGridGap);
9606 mBaseline[axis][BaselineSharingGroup::Last] =
9607 (aCBSize - borderBoxStartToEndOfEndTrack) + lastBaseline;
9611 #ifdef DEBUG_FRAME_DUMP
9612 nsresult nsGridContainerFrame::GetFrameName(nsAString& aResult) const {
9613 return MakeFrameName(u"GridContainer"_ns, aResult);
9616 void nsGridContainerFrame::ExtraContainerFrameInfo(nsACString& aTo) const {
9617 if (const void* const subgrid = GetProperty(Subgrid::Prop())) {
9618 aTo += nsPrintfCString(" [subgrid=%p]", subgrid);
9622 #endif
9624 /* static */ nsGridContainerFrame::FindItemInGridOrderResult
9625 nsGridContainerFrame::FindFirstItemInGridOrder(
9626 CSSOrderAwareFrameIterator& aIter, const nsTArray<GridItemInfo>& aGridItems,
9627 LineRange GridArea::*aMajor, LineRange GridArea::*aMinor,
9628 uint32_t aFragmentStartTrack) {
9629 FindItemInGridOrderResult result = {nullptr, false};
9630 uint32_t minMajor = kTranslatedMaxLine + 1;
9631 uint32_t minMinor = kTranslatedMaxLine + 1;
9632 aIter.Reset();
9633 for (; !aIter.AtEnd(); aIter.Next()) {
9634 const GridItemInfo& item = aGridItems[aIter.ItemIndex()];
9635 if ((item.mArea.*aMajor).mEnd <= aFragmentStartTrack) {
9636 continue; // item doesn't span any track in this fragment
9638 uint32_t major = (item.mArea.*aMajor).mStart;
9639 uint32_t minor = (item.mArea.*aMinor).mStart;
9640 if (major < minMajor || (major == minMajor && minor < minMinor)) {
9641 minMajor = major;
9642 minMinor = minor;
9643 result.mItem = &item;
9644 result.mIsInEdgeTrack = major == 0U;
9647 return result;
9650 /* static */ nsGridContainerFrame::FindItemInGridOrderResult
9651 nsGridContainerFrame::FindLastItemInGridOrder(
9652 ReverseCSSOrderAwareFrameIterator& aIter,
9653 const nsTArray<GridItemInfo>& aGridItems, LineRange GridArea::*aMajor,
9654 LineRange GridArea::*aMinor, uint32_t aFragmentStartTrack,
9655 uint32_t aFirstExcludedTrack) {
9656 FindItemInGridOrderResult result = {nullptr, false};
9657 int32_t maxMajor = -1;
9658 int32_t maxMinor = -1;
9659 aIter.Reset();
9660 int32_t lastMajorTrack = int32_t(aFirstExcludedTrack) - 1;
9661 for (; !aIter.AtEnd(); aIter.Next()) {
9662 const GridItemInfo& item = aGridItems[aIter.ItemIndex()];
9663 // Subtract 1 from the end line to get the item's last track index.
9664 int32_t major = (item.mArea.*aMajor).mEnd - 1;
9665 // Currently, this method is only called with aFirstExcludedTrack ==
9666 // the first track in the next fragment, so we take the opportunity
9667 // to assert this item really belongs to this fragment.
9668 MOZ_ASSERT((item.mArea.*aMajor).mStart < aFirstExcludedTrack,
9669 "found an item that belongs to some later fragment");
9670 if (major < int32_t(aFragmentStartTrack)) {
9671 continue; // item doesn't span any track in this fragment
9673 int32_t minor = (item.mArea.*aMinor).mEnd - 1;
9674 MOZ_ASSERT(minor >= 0 && major >= 0, "grid item must have span >= 1");
9675 if (major > maxMajor || (major == maxMajor && minor > maxMinor)) {
9676 maxMajor = major;
9677 maxMinor = minor;
9678 result.mItem = &item;
9679 result.mIsInEdgeTrack = major == lastMajorTrack;
9682 return result;
9685 nsGridContainerFrame::UsedTrackSizes* nsGridContainerFrame::GetUsedTrackSizes()
9686 const {
9687 return GetProperty(UsedTrackSizes::Prop());
9690 void nsGridContainerFrame::StoreUsedTrackSizes(
9691 LogicalAxis aAxis, const nsTArray<TrackSize>& aSizes) {
9692 auto* uts = GetUsedTrackSizes();
9693 if (!uts) {
9694 uts = new UsedTrackSizes();
9695 SetProperty(UsedTrackSizes::Prop(), uts);
9697 uts->mSizes[aAxis] = aSizes.Clone();
9698 uts->mCanResolveLineRangeSize[aAxis] = true;
9699 // XXX is resetting these bits necessary?
9700 for (auto& sz : uts->mSizes[aAxis]) {
9701 sz.mState &= ~(TrackSize::eFrozen | TrackSize::eSkipGrowUnlimited |
9702 TrackSize::eInfinitelyGrowable);
9706 #ifdef DEBUG
9707 void nsGridContainerFrame::SetInitialChildList(ChildListID aListID,
9708 nsFrameList& aChildList) {
9709 ChildListIDs supportedLists = {kPrincipalList};
9710 // We don't handle the kBackdropList frames in any way, but it only contains
9711 // a placeholder for ::backdrop which is OK to not reflow (for now anyway).
9712 supportedLists += kBackdropList;
9713 MOZ_ASSERT(supportedLists.contains(aListID), "unexpected child list");
9715 return nsContainerFrame::SetInitialChildList(aListID, aChildList);
9718 void nsGridContainerFrame::TrackSize::DumpStateBits(StateBits aState) {
9719 printf("min:");
9720 if (aState & eAutoMinSizing) {
9721 printf("auto-min ");
9722 } else if (aState & eMinContentMinSizing) {
9723 printf("min-content ");
9724 } else if (aState & eMaxContentMinSizing) {
9725 printf("max-content ");
9727 printf(" max:");
9728 if (aState & eAutoMaxSizing) {
9729 printf("auto ");
9730 } else if (aState & eMinContentMaxSizing) {
9731 printf("min-content ");
9732 } else if (aState & eMaxContentMaxSizing) {
9733 printf("max-content ");
9734 } else if (aState & eFlexMaxSizing) {
9735 printf("flex ");
9737 if (aState & eFrozen) {
9738 printf("frozen ");
9740 if (aState & eModified) {
9741 printf("modified ");
9743 if (aState & eBreakBefore) {
9744 printf("break-before ");
9748 void nsGridContainerFrame::TrackSize::Dump() const {
9749 printf("mPosition=%d mBase=%d mLimit=%d ", mPosition, mBase, mLimit);
9750 DumpStateBits(mState);
9753 #endif // DEBUG
9755 nsGridContainerFrame* nsGridContainerFrame::GetGridContainerFrame(
9756 nsIFrame* aFrame) {
9757 nsGridContainerFrame* gridFrame = nullptr;
9759 if (aFrame) {
9760 nsIFrame* inner = aFrame;
9761 if (MOZ_UNLIKELY(aFrame->IsFieldSetFrame())) {
9762 inner = static_cast<nsFieldSetFrame*>(aFrame)->GetInner();
9764 // Since "Get" methods like GetInner and GetContentInsertionFrame can
9765 // return null, we check the return values before dereferencing. Our
9766 // calling pattern makes this unlikely, but we're being careful.
9767 nsIFrame* insertionFrame =
9768 inner ? inner->GetContentInsertionFrame() : nullptr;
9769 nsIFrame* possibleGridFrame = insertionFrame ? insertionFrame : aFrame;
9770 gridFrame = possibleGridFrame->IsGridContainerFrame()
9771 ? static_cast<nsGridContainerFrame*>(possibleGridFrame)
9772 : nullptr;
9774 return gridFrame;
9777 nsGridContainerFrame* nsGridContainerFrame::GetGridFrameWithComputedInfo(
9778 nsIFrame* aFrame) {
9779 nsGridContainerFrame* gridFrame = GetGridContainerFrame(aFrame);
9780 if (!gridFrame) {
9781 return nullptr;
9784 auto HasComputedInfo = [](const nsGridContainerFrame& aFrame) -> bool {
9785 return aFrame.HasProperty(GridColTrackInfo()) &&
9786 aFrame.HasProperty(GridRowTrackInfo()) &&
9787 aFrame.HasProperty(GridColumnLineInfo()) &&
9788 aFrame.HasProperty(GridRowLineInfo());
9791 if (HasComputedInfo(*gridFrame)) {
9792 return gridFrame;
9795 // Trigger a reflow that generates additional grid property data.
9796 // Hold onto aFrame while we do this, in case reflow destroys it.
9797 AutoWeakFrame weakFrameRef(gridFrame);
9799 RefPtr<mozilla::PresShell> presShell = gridFrame->PresShell();
9800 gridFrame->SetShouldGenerateComputedInfo(true);
9801 presShell->FrameNeedsReflow(gridFrame, IntrinsicDirty::Resize,
9802 NS_FRAME_IS_DIRTY);
9803 presShell->FlushPendingNotifications(FlushType::Layout);
9805 // If the weakFrameRef is no longer valid, then we must bail out.
9806 if (!weakFrameRef.IsAlive()) {
9807 return nullptr;
9810 // This can happen if for some reason we ended up not reflowing, like in print
9811 // preview under some circumstances.
9812 if (MOZ_UNLIKELY(!HasComputedInfo(*gridFrame))) {
9813 return nullptr;
9816 return gridFrame;