Bug 1876335 - use GRADLE_MAVEN_REPOSITORIES in more places. r=owlish,geckoview-review...
[gecko.git] / layout / generic / nsGridContainerFrame.cpp
blob5eb9cde7da23a87bf85e2351b2c1e5803ecb96a8
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 <stdlib.h> // for div()
13 #include <type_traits>
14 #include "gfxContext.h"
15 #include "mozilla/AutoRestore.h"
16 #include "mozilla/Baseline.h"
17 #include "mozilla/ComputedStyle.h"
18 #include "mozilla/CSSAlignUtils.h"
19 #include "mozilla/dom/Grid.h"
20 #include "mozilla/dom/GridBinding.h"
21 #include "mozilla/IntegerRange.h"
22 #include "mozilla/Maybe.h"
23 #include "mozilla/PodOperations.h" // for PodZero
24 #include "mozilla/PresShell.h"
25 #include "mozilla/StaticPrefs_layout.h"
26 #include "nsAbsoluteContainingBlock.h"
27 #include "nsAlgorithm.h" // for clamped()
28 #include "nsCSSFrameConstructor.h"
29 #include "nsDisplayList.h"
30 #include "nsFieldSetFrame.h"
31 #include "nsGfxScrollFrame.h"
32 #include "nsHashKeys.h"
33 #include "nsIFrameInlines.h" // for nsIFrame::GetLogicalNormalPosition (don't remove)
34 #include "nsLayoutUtils.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 // For subgrid, we need to resolve <line-name-list> from each
78 // StyleGenericLineNameListValue, so return empty.
79 return {};
81 MOZ_ASSERT(IsNone() || IsMasonry() || (IsSubgrid() && !aIsSubgrid));
82 return {};
85 template <>
86 inline const StyleTrackBreadth& StyleTrackSize::GetMax() const {
87 if (IsBreadth()) {
88 return AsBreadth();
90 if (IsMinmax()) {
91 return AsMinmax()._1;
93 MOZ_ASSERT(IsFitContent());
94 return AsFitContent();
97 template <>
98 inline const StyleTrackBreadth& StyleTrackSize::GetMin() const {
99 static const StyleTrackBreadth kAuto = StyleTrackBreadth::Auto();
100 if (IsBreadth()) {
101 // <flex> behaves like minmax(auto, <flex>)
102 return AsBreadth().IsFr() ? kAuto : AsBreadth();
104 if (IsMinmax()) {
105 return AsMinmax()._0;
107 MOZ_ASSERT(IsFitContent());
108 return kAuto;
111 } // namespace mozilla
113 static nscoord ClampToCSSMaxBSize(nscoord aSize,
114 const ReflowInput* aReflowInput) {
115 auto maxSize = aReflowInput->ComputedMaxBSize();
116 if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
117 MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
118 aSize = std::min(aSize, maxSize);
120 return aSize;
123 // Same as above and set aStatus INCOMPLETE if aSize wasn't clamped.
124 // (If we clamp aSize it means our size is less than the break point,
125 // i.e. we're effectively breaking in our overflow, so we should leave
126 // aStatus as is (it will likely be set to OVERFLOW_INCOMPLETE later)).
127 static nscoord ClampToCSSMaxBSize(nscoord aSize,
128 const ReflowInput* aReflowInput,
129 nsReflowStatus* aStatus) {
130 auto maxSize = aReflowInput->ComputedMaxBSize();
131 if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
132 MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
133 if (aSize < maxSize) {
134 aStatus->SetIncomplete();
135 } else {
136 aSize = maxSize;
138 } else {
139 aStatus->SetIncomplete();
141 return aSize;
144 template <typename Size>
145 static bool IsPercentOfIndefiniteSize(const Size& aCoord,
146 nscoord aPercentBasis) {
147 return aPercentBasis == NS_UNCONSTRAINEDSIZE && aCoord.HasPercent();
150 static nscoord ResolveToDefiniteSize(const StyleTrackBreadth& aBreadth,
151 nscoord aPercentBasis) {
152 MOZ_ASSERT(aBreadth.IsBreadth());
153 if (::IsPercentOfIndefiniteSize(aBreadth.AsBreadth(), aPercentBasis)) {
154 return nscoord(0);
156 return std::max(nscoord(0), aBreadth.AsBreadth().Resolve(aPercentBasis));
159 // Synthesize a baseline from a border box. For an alphabetical baseline
160 // this is the end edge of the border box. For a central baseline it's
161 // the center of the border box.
162 // https://drafts.csswg.org/css-align-3/#synthesize-baseline
163 // For a 'first baseline' the measure is from the border-box start edge and
164 // for a 'last baseline' the measure is from the border-box end edge.
166 // The 'LogicalAxis aAxis' represents the axis (in terms of aWM) that the
167 // baseline corresponds to. (Typically, baselines are a measurement in the
168 // block axis; e.g. for English horizontal-tb text, a traditional baseline
169 // would be a y-axis measurement. But in some cases (e.g. orthogonal WMs), we
170 // may need to synthesize a baseline in a child's inline axis, which is when
171 // this function might receive an aAxis of eLogicalAxisInline. In that case, we
172 // assume that the writing mode's preference for central vs. alphabetic
173 // baselines is irrelevant, since that's a choice about its block-axis
174 // baselines, and we just unconditionally use the alphabetic baseline
175 // (e.g. border-box bottom edge).
176 static nscoord SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup,
177 WritingMode aWM,
178 LogicalAxis aAxis,
179 nscoord aBorderBoxSize) {
180 const bool useAlphabeticBaseline =
181 (aAxis == eLogicalAxisInline) ? true : aWM.IsAlphabeticalBaseline();
183 if (aGroup == BaselineSharingGroup::First) {
184 return useAlphabeticBaseline ? aBorderBoxSize : aBorderBoxSize / 2;
186 MOZ_ASSERT(aGroup == BaselineSharingGroup::Last);
187 // Round up for central baseline offset, to be consistent with eFirst.
188 return useAlphabeticBaseline ? 0
189 : (aBorderBoxSize / 2) + (aBorderBoxSize % 2);
192 // The input sizes for calculating the number of repeat(auto-fill/fit) tracks.
193 // https://drafts.csswg.org/css-grid/#auto-repeat
194 struct RepeatTrackSizingInput {
195 explicit RepeatTrackSizingInput(WritingMode aWM)
196 : mMin(aWM, 0, 0),
197 mSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
198 mMax(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) {}
199 RepeatTrackSizingInput(const LogicalSize& aMin, const LogicalSize& aSize,
200 const LogicalSize& aMax)
201 : mMin(aMin), mSize(aSize), mMax(aMax) {}
203 // This should be used in intrinsic sizing (i.e. when we can't initialize
204 // the sizes directly from ReflowInput values).
205 void InitFromStyle(LogicalAxis aAxis, WritingMode aWM,
206 const ComputedStyle* aStyle) {
207 const auto& pos = aStyle->StylePosition();
208 const bool borderBoxSizing = pos->mBoxSizing == StyleBoxSizing::Border;
209 nscoord bp = NS_UNCONSTRAINEDSIZE; // a sentinel to calculate it only once
210 auto adjustForBoxSizing = [borderBoxSizing, aWM, aAxis, aStyle,
211 &bp](nscoord aSize) {
212 if (!borderBoxSizing) {
213 return aSize;
215 if (bp == NS_UNCONSTRAINEDSIZE) {
216 const auto& padding = aStyle->StylePadding()->mPadding;
217 LogicalMargin border(aWM, aStyle->StyleBorder()->GetComputedBorder());
218 // We can use zero percentage basis since this is only called from
219 // intrinsic sizing code.
220 const nscoord percentageBasis = 0;
221 if (aAxis == eLogicalAxisInline) {
222 bp = std::max(padding.GetIStart(aWM).Resolve(percentageBasis), 0) +
223 std::max(padding.GetIEnd(aWM).Resolve(percentageBasis), 0) +
224 border.IStartEnd(aWM);
225 } else {
226 bp = std::max(padding.GetBStart(aWM).Resolve(percentageBasis), 0) +
227 std::max(padding.GetBEnd(aWM).Resolve(percentageBasis), 0) +
228 border.BStartEnd(aWM);
231 return std::max(aSize - bp, 0);
233 nscoord& min = mMin.Size(aAxis, aWM);
234 nscoord& size = mSize.Size(aAxis, aWM);
235 nscoord& max = mMax.Size(aAxis, aWM);
236 const auto& minCoord =
237 aAxis == eLogicalAxisInline ? pos->MinISize(aWM) : pos->MinBSize(aWM);
238 if (minCoord.ConvertsToLength()) {
239 min = adjustForBoxSizing(minCoord.ToLength());
241 const auto& maxCoord =
242 aAxis == eLogicalAxisInline ? pos->MaxISize(aWM) : pos->MaxBSize(aWM);
243 if (maxCoord.ConvertsToLength()) {
244 max = std::max(min, adjustForBoxSizing(maxCoord.ToLength()));
246 const auto& sizeCoord =
247 aAxis == eLogicalAxisInline ? pos->ISize(aWM) : pos->BSize(aWM);
248 if (sizeCoord.ConvertsToLength()) {
249 size = Clamp(adjustForBoxSizing(sizeCoord.ToLength()), min, max);
253 LogicalSize mMin;
254 LogicalSize mSize;
255 LogicalSize mMax;
258 enum class GridLineSide {
259 BeforeGridGap,
260 AfterGridGap,
263 struct nsGridContainerFrame::TrackSize {
264 enum StateBits : uint16_t {
265 // clang-format off
266 eAutoMinSizing = 0x1,
267 eMinContentMinSizing = 0x2,
268 eMaxContentMinSizing = 0x4,
269 eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing,
270 eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing,
271 eModified = 0x8,
272 eAutoMaxSizing = 0x10,
273 eMinContentMaxSizing = 0x20,
274 eMaxContentMaxSizing = 0x40,
275 eAutoOrMaxContentMaxSizing = eAutoMaxSizing | eMaxContentMaxSizing,
276 eIntrinsicMaxSizing = eAutoOrMaxContentMaxSizing | eMinContentMaxSizing,
277 eFlexMaxSizing = 0x80,
278 eFrozen = 0x100,
279 eSkipGrowUnlimited1 = 0x200,
280 eSkipGrowUnlimited2 = 0x400,
281 eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2,
282 eBreakBefore = 0x800,
283 eFitContent = 0x1000,
284 eInfinitelyGrowable = 0x2000,
286 // These are only used in the masonry axis. They share the same value
287 // as *MinSizing above, but that's OK because we don't use those in
288 // the masonry axis.
290 // This track corresponds to an item margin-box size that is stretching.
291 eItemStretchSize = 0x1,
292 // This bit says that we should clamp that size to mLimit.
293 eClampToLimit = 0x2,
294 // This bit says that the corresponding item has `auto` margin(s).
295 eItemHasAutoMargin = 0x4,
296 // clang-format on
299 StateBits Initialize(nscoord aPercentageBasis, const StyleTrackSize&);
300 bool IsFrozen() const { return mState & eFrozen; }
301 #ifdef DEBUG
302 static void DumpStateBits(StateBits aState);
303 void Dump() const;
304 #endif
306 static bool IsDefiniteMaxSizing(StateBits aStateBits) {
307 return (aStateBits & (eIntrinsicMaxSizing | eFlexMaxSizing)) == 0;
310 nscoord mBase;
311 nscoord mLimit;
312 nscoord mPosition; // zero until we apply 'align/justify-content'
313 // mBaselineSubtreeSize is the size of a baseline-aligned subtree within
314 // this track. One subtree per baseline-sharing group (per track).
315 PerBaseline<nscoord> mBaselineSubtreeSize;
316 StateBits mState;
319 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits)
321 static_assert(
322 std::is_trivially_copyable<nsGridContainerFrame::TrackSize>::value,
323 "Must be trivially copyable");
324 static_assert(
325 std::is_trivially_destructible<nsGridContainerFrame::TrackSize>::value,
326 "Must be trivially destructible");
328 TrackSize::StateBits nsGridContainerFrame::TrackSize::Initialize(
329 nscoord aPercentageBasis, const StyleTrackSize& aSize) {
330 using Tag = StyleTrackBreadth::Tag;
332 MOZ_ASSERT(mBase == 0 && mLimit == 0 && mState == 0,
333 "track size data is expected to be initialized to zero");
334 mBaselineSubtreeSize[BaselineSharingGroup::First] = nscoord(0);
335 mBaselineSubtreeSize[BaselineSharingGroup::Last] = nscoord(0);
337 auto& min = aSize.GetMin();
338 auto& max = aSize.GetMax();
340 Tag minSizeTag = min.tag;
341 Tag maxSizeTag = max.tag;
342 if (aSize.IsFitContent()) {
343 // In layout, fit-content(size) behaves as minmax(auto, max-content), with
344 // 'size' as an additional upper-bound.
345 mState = eFitContent;
346 minSizeTag = Tag::Auto;
347 maxSizeTag = Tag::MaxContent;
349 if (::IsPercentOfIndefiniteSize(min, aPercentageBasis)) {
350 // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-percentage
351 // "If the inline or block size of the grid container is indefinite,
352 // <percentage> values relative to that size are treated as 'auto'."
353 minSizeTag = Tag::Auto;
355 if (::IsPercentOfIndefiniteSize(max, aPercentageBasis)) {
356 maxSizeTag = Tag::Auto;
359 // http://dev.w3.org/csswg/css-grid/#algo-init
360 switch (minSizeTag) {
361 case Tag::Auto:
362 mState |= eAutoMinSizing;
363 break;
364 case Tag::MinContent:
365 mState |= eMinContentMinSizing;
366 break;
367 case Tag::MaxContent:
368 mState |= eMaxContentMinSizing;
369 break;
370 default:
371 MOZ_ASSERT(!min.IsFr(), "<flex> min-sizing is invalid as a track size");
372 mBase = ::ResolveToDefiniteSize(min, aPercentageBasis);
374 switch (maxSizeTag) {
375 case Tag::Auto:
376 mState |= eAutoMaxSizing;
377 mLimit = NS_UNCONSTRAINEDSIZE;
378 break;
379 case Tag::MinContent:
380 case Tag::MaxContent:
381 mState |= maxSizeTag == Tag::MinContent ? eMinContentMaxSizing
382 : eMaxContentMaxSizing;
383 mLimit = NS_UNCONSTRAINEDSIZE;
384 break;
385 case Tag::Fr:
386 mState |= eFlexMaxSizing;
387 mLimit = mBase;
388 break;
389 default:
390 mLimit = ::ResolveToDefiniteSize(max, aPercentageBasis);
391 if (mLimit < mBase) {
392 mLimit = mBase;
395 return mState;
399 * A LineRange can be definite or auto - when it's definite it represents
400 * a consecutive set of tracks between a starting line and an ending line.
401 * Before it's definite it can also represent an auto position with a span,
402 * where mStart == kAutoLine and mEnd is the (non-zero positive) span.
403 * For normal-flow items, the invariant mStart < mEnd holds when both
404 * lines are definite.
406 * For abs.pos. grid items, mStart and mEnd may both be kAutoLine, meaning
407 * "attach this side to the grid container containing block edge".
408 * Additionally, mStart <= mEnd holds when both are definite (non-kAutoLine),
409 * i.e. the invariant is slightly relaxed compared to normal flow items.
411 struct nsGridContainerFrame::LineRange {
412 LineRange(int32_t aStart, int32_t aEnd)
413 : mUntranslatedStart(aStart), mUntranslatedEnd(aEnd) {
414 #ifdef DEBUG
415 if (!IsAutoAuto()) {
416 if (IsAuto()) {
417 MOZ_ASSERT(aEnd >= kMinLine && aEnd <= kMaxLine, "invalid span");
418 } else {
419 MOZ_ASSERT(aStart >= kMinLine && aStart <= kMaxLine,
420 "invalid start line");
421 MOZ_ASSERT(aEnd == int32_t(kAutoLine) ||
422 (aEnd >= kMinLine && aEnd <= kMaxLine),
423 "invalid end line");
426 #endif
428 bool IsAutoAuto() const { return mStart == kAutoLine && mEnd == kAutoLine; }
429 bool IsAuto() const { return mStart == kAutoLine; }
430 bool IsDefinite() const { return mStart != kAutoLine; }
431 uint32_t Extent() const {
432 MOZ_ASSERT(mEnd != kAutoLine, "Extent is undefined for abs.pos. 'auto'");
433 if (IsAuto()) {
434 MOZ_ASSERT(mEnd >= 1 && mEnd < uint32_t(kMaxLine), "invalid span");
435 return mEnd;
437 return mEnd - mStart;
441 * Return an object suitable for iterating this range.
443 auto Range() const { return IntegerRange<uint32_t>(mStart, mEnd); }
446 * Resolve this auto range to start at aStart, making it definite.
447 * @param aClampMaxLine the maximum allowed line number (zero-based)
448 * Precondition: this range IsAuto()
450 void ResolveAutoPosition(uint32_t aStart, uint32_t aClampMaxLine) {
451 MOZ_ASSERT(IsAuto(), "Why call me?");
452 mStart = aStart;
453 mEnd += aStart;
454 // Clamp to aClampMaxLine, which is where kMaxLine is in the explicit
455 // grid in a non-subgrid axis; this implements clamping per
456 // http://dev.w3.org/csswg/css-grid/#overlarge-grids
457 // In a subgrid axis it's the end of the grid in that axis.
458 if (MOZ_UNLIKELY(mStart >= aClampMaxLine)) {
459 mEnd = aClampMaxLine;
460 mStart = mEnd - 1;
461 } else if (MOZ_UNLIKELY(mEnd > aClampMaxLine)) {
462 mEnd = aClampMaxLine;
466 * Translate the lines to account for (empty) removed tracks. This method
467 * is only for grid items and should only be called after placement.
468 * aNumRemovedTracks contains a count for each line in the grid how many
469 * tracks were removed between the start of the grid and that line.
471 void AdjustForRemovedTracks(const nsTArray<uint32_t>& aNumRemovedTracks) {
472 MOZ_ASSERT(mStart != kAutoLine, "invalid resolved line for a grid item");
473 MOZ_ASSERT(mEnd != kAutoLine, "invalid resolved line for a grid item");
474 uint32_t numRemovedTracks = aNumRemovedTracks[mStart];
475 MOZ_ASSERT(numRemovedTracks == aNumRemovedTracks[mEnd],
476 "tracks that a grid item spans can't be removed");
477 mStart -= numRemovedTracks;
478 mEnd -= numRemovedTracks;
481 * Translate the lines to account for (empty) removed tracks. This method
482 * is only for abs.pos. children and should only be called after placement.
483 * Same as for in-flow items, but we don't touch 'auto' lines here and we
484 * also need to adjust areas that span into the removed tracks.
486 void AdjustAbsPosForRemovedTracks(
487 const nsTArray<uint32_t>& aNumRemovedTracks) {
488 if (mStart != kAutoLine) {
489 mStart -= aNumRemovedTracks[mStart];
491 if (mEnd != kAutoLine) {
492 MOZ_ASSERT(mStart == kAutoLine || mEnd > mStart, "invalid line range");
493 mEnd -= aNumRemovedTracks[mEnd];
497 * Return the contribution of this line range for step 2 in
498 * http://dev.w3.org/csswg/css-grid/#auto-placement-algo
500 uint32_t HypotheticalEnd() const { return mEnd; }
502 * Given an array of track sizes, return the starting position and length
503 * of the tracks in this line range.
505 void ToPositionAndLength(const nsTArray<TrackSize>& aTrackSizes,
506 nscoord* aPos, nscoord* aLength) const;
508 * Given an array of track sizes, return the length of the tracks in this
509 * line range.
511 nscoord ToLength(const nsTArray<TrackSize>& aTrackSizes) const;
513 * Given an array of track sizes and a grid origin coordinate, adjust the
514 * abs.pos. containing block along an axis given by aPos and aLength.
515 * aPos and aLength should already be initialized to the grid container
516 * containing block for this axis before calling this method.
518 void ToPositionAndLengthForAbsPos(const Tracks& aTracks, nscoord aGridOrigin,
519 nscoord* aPos, nscoord* aLength) const;
521 void Translate(int32_t aOffset) {
522 MOZ_ASSERT(IsDefinite());
523 mStart += aOffset;
524 mEnd += aOffset;
527 /** Swap the start/end sides of this range. */
528 void ReverseDirection(uint32_t aGridEnd) {
529 MOZ_ASSERT(IsDefinite());
530 MOZ_ASSERT(aGridEnd >= mEnd);
531 uint32_t newStart = aGridEnd - mEnd;
532 mEnd = aGridEnd - mStart;
533 mStart = newStart;
537 * @note We'll use the signed member while resolving definite positions
538 * to line numbers (1-based), which may become negative for implicit lines
539 * to the top/left of the explicit grid. PlaceGridItems() then translates
540 * the whole grid to a 0,0 origin and we'll use the unsigned member from
541 * there on.
543 union {
544 uint32_t mStart;
545 int32_t mUntranslatedStart;
547 union {
548 uint32_t mEnd;
549 int32_t mUntranslatedEnd;
552 protected:
553 LineRange() : mStart(0), mEnd(0) {}
557 * Helper class to construct a LineRange from translated lines.
558 * The ctor only accepts translated definite line numbers.
560 struct nsGridContainerFrame::TranslatedLineRange : public LineRange {
561 TranslatedLineRange(uint32_t aStart, uint32_t aEnd) {
562 MOZ_ASSERT(aStart < aEnd && aEnd <= kTranslatedMaxLine);
563 mStart = aStart;
564 mEnd = aEnd;
569 * A GridArea is the area in the grid for a grid item.
570 * The area is represented by two LineRanges, both of which can be auto
571 * (@see LineRange) in intermediate steps while the item is being placed.
572 * @see PlaceGridItems
574 struct nsGridContainerFrame::GridArea {
575 GridArea(const LineRange& aCols, const LineRange& aRows)
576 : mCols(aCols), mRows(aRows) {}
577 bool IsDefinite() const { return mCols.IsDefinite() && mRows.IsDefinite(); }
578 LineRange& LineRangeForAxis(LogicalAxis aAxis) {
579 return aAxis == eLogicalAxisInline ? mCols : mRows;
581 const LineRange& LineRangeForAxis(LogicalAxis aAxis) const {
582 return aAxis == eLogicalAxisInline ? mCols : mRows;
584 LineRange mCols;
585 LineRange mRows;
588 struct nsGridContainerFrame::GridItemInfo {
590 * Item state per axis.
592 enum StateBits : uint16_t {
593 // Does the item span a flex track?
594 eIsFlexing = 0x1,
596 // First or last baseline alignment preference. They are mutually exclusive.
597 // This does *NOT* represent the baseline alignment group. See the member
598 // variable for that.
599 // <https://drafts.csswg.org/css-align-3/#baseline-alignment-preference>
600 eFirstBaseline = 0x2,
601 eLastBaseline = 0x4,
602 eIsBaselineAligned = eFirstBaseline | eLastBaseline,
604 // One of e[Self|Content]Baseline is set when eIsBaselineAligned is true
605 eSelfBaseline = 0x8, // is it *-self:[last ]baseline alignment?
606 // Ditto *-content:[last ]baseline. Mutually exclusive w. eSelfBaseline.
607 eContentBaseline = 0x10,
609 // The baseline affects the margin or padding on the item's end side when
610 // this bit is set. In a grid-axis it's always set for eLastBaseline and
611 // always unset for eFirstBaseline. In a masonry-axis, it's set for
612 // baseline groups in the EndStretch set and unset for the StartStretch set.
613 eEndSideBaseline = 0x20,
614 eAllBaselineBits = eIsBaselineAligned | eSelfBaseline | eContentBaseline |
615 eEndSideBaseline,
617 // Should apply Automatic Minimum Size per:
618 // https://drafts.csswg.org/css-grid/#min-size-auto
619 eApplyAutoMinSize = 0x40,
620 // Clamp per https://drafts.csswg.org/css-grid/#min-size-auto
621 eClampMarginBoxMinSize = 0x80,
622 eIsSubgrid = 0x100,
623 // set on subgrids and items in subgrids if they are adjacent to the grid
624 // start/end edge (excluding grid-aligned abs.pos. frames)
625 eStartEdge = 0x200,
626 eEndEdge = 0x400,
627 eEdgeBits = eStartEdge | eEndEdge,
628 // Set if this item was auto-placed in this axis.
629 eAutoPlacement = 0x800,
630 // Set if this item is the last item in its track (masonry layout only)
631 eIsLastItemInMasonryTrack = 0x1000,
634 GridItemInfo(nsIFrame* aFrame, const GridArea& aArea);
636 GridItemInfo(const GridItemInfo& aOther)
637 : mFrame(aOther.mFrame), mArea(aOther.mArea) {
638 mBaselineOffset = aOther.mBaselineOffset;
639 mState = aOther.mState;
642 GridItemInfo& operator=(const GridItemInfo&) = delete;
644 static bool BaselineAlignmentAffectsEndSide(StateBits state) {
645 return state & StateBits::eEndSideBaseline;
649 * Inhibit subgrid layout unless the item is placed in the first "track" in
650 * a parent masonry-axis, or has definite placement or spans all tracks in
651 * the parent grid-axis.
652 * TODO: this is stricter than what the Masonry proposal currently states
653 * (bug 1627581)
655 void MaybeInhibitSubgridInMasonry(nsGridContainerFrame* aParent,
656 uint32_t aGridAxisTrackCount);
659 * Inhibit subgridding in aAxis for this item.
661 void InhibitSubgrid(nsGridContainerFrame* aParent, LogicalAxis aAxis);
664 * Return a copy of this item with its row/column data swapped.
666 GridItemInfo Transpose() const {
667 GridItemInfo info(mFrame, GridArea(mArea.mRows, mArea.mCols));
668 info.mState[eLogicalAxisBlock] = mState[eLogicalAxisInline];
669 info.mState[eLogicalAxisInline] = mState[eLogicalAxisBlock];
670 info.mBaselineOffset[eLogicalAxisBlock] =
671 mBaselineOffset[eLogicalAxisInline];
672 info.mBaselineOffset[eLogicalAxisInline] =
673 mBaselineOffset[eLogicalAxisBlock];
674 return info;
677 /** Swap the start/end sides in aAxis. */
678 inline void ReverseDirection(LogicalAxis aAxis, uint32_t aGridEnd);
680 // Is this item a subgrid in the given container axis?
681 bool IsSubgrid(LogicalAxis aAxis) const {
682 return mState[aAxis] & StateBits::eIsSubgrid;
685 // Is this item a subgrid in either axis?
686 bool IsSubgrid() const {
687 return IsSubgrid(eLogicalAxisInline) || IsSubgrid(eLogicalAxisBlock);
690 // Return the (inner) grid container frame associated with this subgrid item.
691 nsGridContainerFrame* SubgridFrame() const {
692 MOZ_ASSERT(IsSubgrid());
693 nsGridContainerFrame* gridFrame = GetGridContainerFrame(mFrame);
694 MOZ_ASSERT(gridFrame && gridFrame->IsSubgrid());
695 return gridFrame;
699 * Adjust our grid areas to account for removed auto-fit tracks in aAxis.
701 void AdjustForRemovedTracks(LogicalAxis aAxis,
702 const nsTArray<uint32_t>& aNumRemovedTracks);
705 * If the item is [align|justify]-self:[last ]baseline aligned in the given
706 * axis then set aBaselineOffset to the baseline offset and return aAlign.
707 * Otherwise, return a fallback alignment.
709 StyleAlignFlags GetSelfBaseline(StyleAlignFlags aAlign, LogicalAxis aAxis,
710 nscoord* aBaselineOffset) const {
711 MOZ_ASSERT(aAlign == StyleAlignFlags::BASELINE ||
712 aAlign == StyleAlignFlags::LAST_BASELINE);
713 if (!(mState[aAxis] & eSelfBaseline)) {
714 return aAlign == StyleAlignFlags::BASELINE ? StyleAlignFlags::SELF_START
715 : StyleAlignFlags::SELF_END;
717 *aBaselineOffset = mBaselineOffset[aAxis];
718 return aAlign;
721 // Return true if we should apply Automatic Minimum Size to this item.
722 // https://drafts.csswg.org/css-grid/#min-size-auto
723 // @note the caller should also check that the item spans at least one track
724 // that has a min track sizing function that is 'auto' before applying it.
725 bool ShouldApplyAutoMinSize(WritingMode aContainerWM,
726 LogicalAxis aContainerAxis,
727 nscoord aPercentageBasis) const {
728 const bool isInlineAxis = aContainerAxis == eLogicalAxisInline;
729 const auto* pos =
730 mFrame->IsTableWrapperFrame()
731 ? mFrame->PrincipalChildList().FirstChild()->StylePosition()
732 : mFrame->StylePosition();
733 const auto& size =
734 isInlineAxis ? pos->ISize(aContainerWM) : pos->BSize(aContainerWM);
735 // max-content and min-content should behave as initial value in block axis.
736 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
737 // for block size dimension on sizing properties (e.g. height), so we
738 // treat it as `auto`.
739 bool isAuto = size.IsAuto() ||
740 (isInlineAxis ==
741 aContainerWM.IsOrthogonalTo(mFrame->GetWritingMode()) &&
742 size.BehavesLikeInitialValueOnBlockAxis());
743 // NOTE: if we have a definite size then our automatic minimum size
744 // can't affect our size. Excluding these simplifies applying
745 // the clamping in the right cases later.
746 if (!isAuto && !::IsPercentOfIndefiniteSize(size, aPercentageBasis)) {
747 return false;
749 const auto& minSize = isInlineAxis ? pos->MinISize(aContainerWM)
750 : pos->MinBSize(aContainerWM);
751 // max-content and min-content should behave as initial value in block axis.
752 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
753 // for block size dimension on sizing properties (e.g. height), so we
754 // treat it as `auto`.
755 isAuto = minSize.IsAuto() ||
756 (isInlineAxis ==
757 aContainerWM.IsOrthogonalTo(mFrame->GetWritingMode()) &&
758 minSize.BehavesLikeInitialValueOnBlockAxis());
759 return isAuto && !mFrame->StyleDisplay()->IsScrollableOverflow();
762 #ifdef DEBUG
763 void Dump() const;
764 #endif
766 static bool IsStartRowLessThan(const GridItemInfo* a, const GridItemInfo* b) {
767 return a->mArea.mRows.mStart < b->mArea.mRows.mStart;
770 // Sorting functions for 'masonry-auto-flow:next'. We sort the items that
771 // were placed into the first track by the Grid placement algorithm first
772 // (to honor that placement). All other items will be placed by the Masonry
773 // layout algorithm (their Grid placement in the masonry axis is irrelevant).
774 static bool RowMasonryOrdered(const GridItemInfo* a, const GridItemInfo* b) {
775 return a->mArea.mRows.mStart == 0 && b->mArea.mRows.mStart != 0 &&
776 !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
778 static bool ColMasonryOrdered(const GridItemInfo* a, const GridItemInfo* b) {
779 return a->mArea.mCols.mStart == 0 && b->mArea.mCols.mStart != 0 &&
780 !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
783 // Sorting functions for 'masonry-auto-flow:definite-first'. Similar to
784 // the above, but here we also sort items with a definite item placement in
785 // the grid axis in track order before 'auto'-placed items. We also sort all
786 // continuations first since they use the same placement as their
787 // first-in-flow (we treat them as "definite" regardless of eAutoPlacement).
788 static bool RowMasonryDefiniteFirst(const GridItemInfo* a,
789 const GridItemInfo* b) {
790 bool isContinuationA = a->mFrame->GetPrevInFlow();
791 bool isContinuationB = b->mFrame->GetPrevInFlow();
792 if (isContinuationA != isContinuationB) {
793 return isContinuationA;
795 auto masonryA = a->mArea.mRows.mStart;
796 auto gridA = a->mState[eLogicalAxisInline] & StateBits::eAutoPlacement;
797 auto masonryB = b->mArea.mRows.mStart;
798 auto gridB = b->mState[eLogicalAxisInline] & StateBits::eAutoPlacement;
799 return (masonryA == 0 ? masonryB != 0 : (masonryB != 0 && gridA < gridB)) &&
800 !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
802 static bool ColMasonryDefiniteFirst(const GridItemInfo* a,
803 const GridItemInfo* b) {
804 MOZ_ASSERT(!a->mFrame->GetPrevInFlow() && !b->mFrame->GetPrevInFlow(),
805 "fragmentation not supported in inline axis");
806 auto masonryA = a->mArea.mCols.mStart;
807 auto gridA = a->mState[eLogicalAxisBlock] & StateBits::eAutoPlacement;
808 auto masonryB = b->mArea.mCols.mStart;
809 auto gridB = b->mState[eLogicalAxisBlock] & StateBits::eAutoPlacement;
810 return (masonryA == 0 ? masonryB != 0 : (masonryB != 0 && gridA < gridB)) &&
811 !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
814 // Return true if this items block size is dependent on the size of the
815 // container it is in.
816 bool IsBSizeDependentOnContainerSize(WritingMode aContainerWM) const {
817 const auto IsDependentOnContainerSize = [](const auto& size) -> bool {
818 return size.HasPercent() || size.IsMozAvailable();
821 const nsStylePosition* stylePos = mFrame->StylePosition();
822 bool isItemAutoSize =
823 IsDependentOnContainerSize(stylePos->BSize(aContainerWM)) ||
824 IsDependentOnContainerSize(stylePos->MinBSize(aContainerWM)) ||
825 IsDependentOnContainerSize(stylePos->MaxBSize(aContainerWM));
827 return isItemAutoSize;
830 nsIFrame* const mFrame;
831 GridArea mArea;
833 // Offset from the margin edge to the baseline (LogicalAxis index). It's from
834 // the start edge for first baseline sharing group, otherwise from the end
835 // edge.
836 // It's mutable since we update the value fairly late (just before reflowing
837 // the item).
838 mutable PerLogicalAxis<nscoord> mBaselineOffset;
840 // State bits per axis.
841 mutable PerLogicalAxis<StateBits> mState;
844 using GridItemInfo = nsGridContainerFrame::GridItemInfo;
845 using ItemState = GridItemInfo::StateBits;
846 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ItemState)
848 GridItemInfo::GridItemInfo(nsIFrame* aFrame, const GridArea& aArea)
849 : mFrame(aFrame), mArea(aArea), mBaselineOffset{0, 0} {
850 mState[eLogicalAxisBlock] =
851 StateBits(mArea.mRows.mStart == kAutoLine ? eAutoPlacement : 0);
852 mState[eLogicalAxisInline] =
853 StateBits(mArea.mCols.mStart == kAutoLine ? eAutoPlacement : 0);
855 if (auto* gridFrame = GetGridContainerFrame(mFrame)) {
856 auto parentWM = aFrame->GetParent()->GetWritingMode();
857 bool isOrthogonal = parentWM.IsOrthogonalTo(gridFrame->GetWritingMode());
858 if (gridFrame->IsColSubgrid()) {
859 mState[isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline] |=
860 StateBits::eIsSubgrid;
862 if (gridFrame->IsRowSubgrid()) {
863 mState[isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock] |=
864 StateBits::eIsSubgrid;
869 void GridItemInfo::ReverseDirection(LogicalAxis aAxis, uint32_t aGridEnd) {
870 mArea.LineRangeForAxis(aAxis).ReverseDirection(aGridEnd);
871 ItemState& state = mState[aAxis];
872 ItemState newState = state & ~ItemState::eEdgeBits;
873 if (state & ItemState::eStartEdge) {
874 newState |= ItemState::eEndEdge;
876 if (state & ItemState::eEndEdge) {
877 newState |= ItemState::eStartEdge;
879 state = newState;
882 void GridItemInfo::InhibitSubgrid(nsGridContainerFrame* aParent,
883 LogicalAxis aAxis) {
884 MOZ_ASSERT(IsSubgrid(aAxis));
885 auto bit = NS_STATE_GRID_IS_COL_SUBGRID;
886 if (aParent->GetWritingMode().IsOrthogonalTo(mFrame->GetWritingMode()) !=
887 (aAxis == eLogicalAxisBlock)) {
888 bit = NS_STATE_GRID_IS_ROW_SUBGRID;
890 MOZ_ASSERT(SubgridFrame()->HasAnyStateBits(bit));
891 SubgridFrame()->RemoveStateBits(bit);
892 mState[aAxis] &= StateBits(~StateBits::eIsSubgrid);
895 void GridItemInfo::MaybeInhibitSubgridInMasonry(nsGridContainerFrame* aParent,
896 uint32_t aGridAxisTrackCount) {
897 if (IsSubgrid(eLogicalAxisInline) && aParent->IsMasonry(eLogicalAxisBlock) &&
898 mArea.mRows.mStart != 0 && mArea.mCols.Extent() != aGridAxisTrackCount &&
899 (mState[eLogicalAxisInline] & eAutoPlacement)) {
900 InhibitSubgrid(aParent, eLogicalAxisInline);
901 return;
903 if (IsSubgrid(eLogicalAxisBlock) && aParent->IsMasonry(eLogicalAxisInline) &&
904 mArea.mCols.mStart != 0 && mArea.mRows.Extent() != aGridAxisTrackCount &&
905 (mState[eLogicalAxisBlock] & eAutoPlacement)) {
906 InhibitSubgrid(aParent, eLogicalAxisBlock);
910 // Each subgrid stores this data about its items etc on a frame property.
911 struct nsGridContainerFrame::Subgrid {
912 Subgrid(const GridArea& aArea, bool aIsOrthogonal, WritingMode aCBWM)
913 : mArea(aArea),
914 mGridColEnd(0),
915 mGridRowEnd(0),
916 mMarginBorderPadding(aCBWM),
917 mIsOrthogonal(aIsOrthogonal) {}
919 // Return the relevant line range for the subgrid column axis.
920 const LineRange& SubgridCols() const {
921 return mIsOrthogonal ? mArea.mRows : mArea.mCols;
923 // Return the relevant line range for the subgrid row axis.
924 const LineRange& SubgridRows() const {
925 return mIsOrthogonal ? mArea.mCols : mArea.mRows;
928 // The subgrid's items.
929 nsTArray<GridItemInfo> mGridItems;
930 // The subgrid's abs.pos. items.
931 nsTArray<GridItemInfo> mAbsPosItems;
932 // The subgrid's area as a grid item, i.e. in its parent's grid space.
933 GridArea mArea;
934 // The (inner) grid size for the subgrid, zero-based.
935 uint32_t mGridColEnd;
936 uint32_t mGridRowEnd;
937 // The margin+border+padding for the subgrid box in its parent grid's WM.
938 // (This also includes the size of any scrollbars.)
939 LogicalMargin mMarginBorderPadding;
940 // Does the subgrid frame have orthogonal writing-mode to its parent grid
941 // container?
942 bool mIsOrthogonal;
944 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, Subgrid)
946 using Subgrid = nsGridContainerFrame::Subgrid;
948 void GridItemInfo::AdjustForRemovedTracks(
949 LogicalAxis aAxis, const nsTArray<uint32_t>& aNumRemovedTracks) {
950 const bool abspos = mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
951 auto& lines = mArea.LineRangeForAxis(aAxis);
952 if (abspos) {
953 lines.AdjustAbsPosForRemovedTracks(aNumRemovedTracks);
954 } else {
955 lines.AdjustForRemovedTracks(aNumRemovedTracks);
957 if (IsSubgrid()) {
958 auto* subgrid = SubgridFrame()->GetProperty(Subgrid::Prop());
959 if (subgrid) {
960 auto& lines = subgrid->mArea.LineRangeForAxis(aAxis);
961 if (abspos) {
962 lines.AdjustAbsPosForRemovedTracks(aNumRemovedTracks);
963 } else {
964 lines.AdjustForRemovedTracks(aNumRemovedTracks);
971 * Track size data for use by subgrids (which don't do sizing of their own
972 * in a subgridded axis). A non-subgrid container stores its resolved sizes,
973 * but only if it has any subgrid children. A subgrid always stores one.
974 * In a subgridded axis, we copy the parent's sizes (see CopyUsedTrackSizes).
976 * This struct us stored on a frame property, which may be null before the track
977 * sizing step for the given container. A null property is semantically
978 * equivalent to mCanResolveLineRangeSize being false in both axes.
979 * @note the axis used to access this data is in the grid container's own
980 * writing-mode, same as in other track-sizing functions.
982 struct nsGridContainerFrame::UsedTrackSizes {
983 UsedTrackSizes() : mCanResolveLineRangeSize{false, false} {}
986 * Setup mSizes by copying track sizes from aFrame's grid container
987 * parent when aAxis is subgridded (and recurse if the parent is a subgrid
988 * that doesn't have sizes yet), or by running the Track Sizing Algo when
989 * the axis is not subgridded (for a subgrid).
990 * Set mCanResolveLineRangeSize[aAxis] to true once we have obtained
991 * sizes for an axis (if it's already true then this method is a NOP).
993 void ResolveTrackSizesForAxis(nsGridContainerFrame* aFrame, LogicalAxis aAxis,
994 gfxContext& aRC);
996 /** Helper function for the above method */
997 void ResolveSubgridTrackSizesForAxis(nsGridContainerFrame* aFrame,
998 LogicalAxis aAxis, Subgrid* aSubgrid,
999 gfxContext& aRC,
1000 nscoord aContentBoxSize);
1002 // This only has valid sizes when mCanResolveLineRangeSize is true in
1003 // the same axis. It may have zero tracks (a grid with only abs.pos.
1004 // subgrids/items may have zero tracks).
1005 PerLogicalAxis<nsTArray<TrackSize>> mSizes;
1006 // True if mSizes can be used to resolve line range sizes in an axis.
1007 PerLogicalAxis<bool> mCanResolveLineRangeSize;
1009 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, UsedTrackSizes)
1011 using UsedTrackSizes = nsGridContainerFrame::UsedTrackSizes;
1013 #ifdef DEBUG
1014 void nsGridContainerFrame::GridItemInfo::Dump() const {
1015 auto Dump1 = [this](const char* aMsg, LogicalAxis aAxis) {
1016 auto state = mState[aAxis];
1017 if (!state) {
1018 return;
1020 printf("%s", aMsg);
1021 if (state & ItemState::eEdgeBits) {
1022 printf("subgrid-adjacent-edges(");
1023 if (state & ItemState::eStartEdge) {
1024 printf("start ");
1026 if (state & ItemState::eEndEdge) {
1027 printf("end");
1029 printf(") ");
1031 if (state & ItemState::eAutoPlacement) {
1032 printf("masonry-auto ");
1034 if (state & ItemState::eIsSubgrid) {
1035 printf("subgrid ");
1037 if (state & ItemState::eIsFlexing) {
1038 printf("flexing ");
1040 if (state & ItemState::eApplyAutoMinSize) {
1041 printf("auto-min-size ");
1043 if (state & ItemState::eClampMarginBoxMinSize) {
1044 printf("clamp ");
1046 if (state & ItemState::eIsLastItemInMasonryTrack) {
1047 printf("last-in-track ");
1049 if (state & ItemState::eFirstBaseline) {
1050 printf("first baseline %s-alignment ",
1051 (state & ItemState::eSelfBaseline) ? "self" : "content");
1053 if (state & ItemState::eLastBaseline) {
1054 printf("last baseline %s-alignment ",
1055 (state & ItemState::eSelfBaseline) ? "self" : "content");
1057 if (state & ItemState::eIsBaselineAligned) {
1058 printf("%.2fpx", NSAppUnitsToFloatPixels(mBaselineOffset[aAxis],
1059 AppUnitsPerCSSPixel()));
1061 printf("\n");
1063 printf("grid-row: %d %d\n", mArea.mRows.mStart, mArea.mRows.mEnd);
1064 Dump1(" grid block-axis: ", eLogicalAxisBlock);
1065 printf("grid-column: %d %d\n", mArea.mCols.mStart, mArea.mCols.mEnd);
1066 Dump1(" grid inline-axis: ", eLogicalAxisInline);
1068 #endif
1071 * Encapsulates CSS track-sizing functions.
1073 struct nsGridContainerFrame::TrackSizingFunctions {
1074 private:
1075 TrackSizingFunctions(const GridTemplate& aTemplate,
1076 const StyleImplicitGridTracks& aAutoSizing,
1077 const Maybe<size_t>& aRepeatAutoIndex, bool aIsSubgrid)
1078 : mTemplate(aTemplate),
1079 mTrackListValues(aTemplate.TrackListValues()),
1080 mAutoSizing(aAutoSizing),
1081 mExplicitGridOffset(0),
1082 mRepeatAutoStart(aRepeatAutoIndex.valueOr(0)),
1083 mRepeatAutoEnd(mRepeatAutoStart),
1084 mHasRepeatAuto(aRepeatAutoIndex.isSome()) {
1085 MOZ_ASSERT(!mHasRepeatAuto || !aIsSubgrid,
1086 "a track-list for a subgrid can't have an <auto-repeat> track");
1087 if (!aIsSubgrid) {
1088 ExpandNonRepeatAutoTracks();
1091 #ifdef DEBUG
1092 if (mHasRepeatAuto) {
1093 MOZ_ASSERT(mExpandedTracks.Length() >= 1);
1094 const unsigned maxTrack = kMaxLine - 1;
1095 // If the exanded tracks are out of range of the maximum track, we
1096 // can't compare the repeat-auto start. It will be removed later during
1097 // grid item placement in that situation.
1098 if (mExpandedTracks.Length() < maxTrack) {
1099 MOZ_ASSERT(mRepeatAutoStart < mExpandedTracks.Length());
1102 #endif
1105 public:
1106 TrackSizingFunctions(const GridTemplate& aGridTemplate,
1107 const StyleImplicitGridTracks& aAutoSizing,
1108 bool aIsSubgrid)
1109 : TrackSizingFunctions(aGridTemplate, aAutoSizing,
1110 aGridTemplate.RepeatAutoIndex(), aIsSubgrid) {}
1112 private:
1113 enum { ForSubgridFallbackTag };
1114 TrackSizingFunctions(const GridTemplate& aGridTemplate,
1115 const StyleImplicitGridTracks& aAutoSizing,
1116 decltype(ForSubgridFallbackTag))
1117 : TrackSizingFunctions(aGridTemplate, aAutoSizing, Nothing(),
1118 /* aIsSubgrid */ true) {}
1120 public:
1122 * This is used in a subgridded axis to resolve sizes before its parent's
1123 * sizes are known for intrinsic sizing purposes. It copies the slice of
1124 * the nearest non-subgridded axis' track sizing functions spanned by
1125 * the subgrid.
1127 * FIXME: this was written before there was a spec... the spec now says:
1128 * "If calculating the layout of a grid item in this step depends on
1129 * the available space in the block axis, assume the available space
1130 * that it would have if any row with a definite max track sizing
1131 * function had that size and all other rows were infinite."
1132 * https://drafts.csswg.org/css-grid-2/#subgrid-sizing
1134 static TrackSizingFunctions ForSubgridFallback(
1135 nsGridContainerFrame* aSubgridFrame, const Subgrid* aSubgrid,
1136 nsGridContainerFrame* aParentGridContainer, LogicalAxis aParentAxis) {
1137 MOZ_ASSERT(aSubgrid);
1138 MOZ_ASSERT(aSubgridFrame->IsSubgrid(aSubgrid->mIsOrthogonal
1139 ? GetOrthogonalAxis(aParentAxis)
1140 : aParentAxis));
1141 nsGridContainerFrame* parent = aParentGridContainer;
1142 auto parentAxis = aParentAxis;
1143 LineRange range = aSubgrid->mArea.LineRangeForAxis(parentAxis);
1144 // Find our nearest non-subgridded axis and use its track sizing functions.
1145 while (parent->IsSubgrid(parentAxis)) {
1146 const auto* parentSubgrid = parent->GetProperty(Subgrid::Prop());
1147 auto* grandParent = parent->ParentGridContainerForSubgrid();
1148 auto grandParentWM = grandParent->GetWritingMode();
1149 bool isSameDirInAxis =
1150 parent->GetWritingMode().ParallelAxisStartsOnSameSide(parentAxis,
1151 grandParentWM);
1152 if (MOZ_UNLIKELY(!isSameDirInAxis)) {
1153 auto end = parentAxis == eLogicalAxisBlock ? parentSubgrid->mGridRowEnd
1154 : parentSubgrid->mGridColEnd;
1155 range.ReverseDirection(end);
1156 // range is now in the same direction as the grand-parent's axis
1158 auto grandParentAxis = parentSubgrid->mIsOrthogonal
1159 ? GetOrthogonalAxis(parentAxis)
1160 : parentAxis;
1161 const auto& parentRange =
1162 parentSubgrid->mArea.LineRangeForAxis(grandParentAxis);
1163 range.Translate(parentRange.mStart);
1164 // range is now in the grand-parent's coordinates
1165 parentAxis = grandParentAxis;
1166 parent = grandParent;
1168 const auto* pos = parent->StylePosition();
1169 const auto isInlineAxis = parentAxis == eLogicalAxisInline;
1170 const auto& szf =
1171 isInlineAxis ? pos->mGridTemplateRows : pos->mGridTemplateColumns;
1172 const auto& autoSizing =
1173 isInlineAxis ? pos->mGridAutoColumns : pos->mGridAutoRows;
1174 return TrackSizingFunctions(szf, autoSizing, ForSubgridFallbackTag);
1178 * Initialize the number of auto-fill/fit tracks to use.
1179 * This can be zero if no auto-fill/fit track was specified, or if the repeat
1180 * begins after the maximum allowed track.
1182 void InitRepeatTracks(const NonNegativeLengthPercentageOrNormal& aGridGap,
1183 nscoord aMinSize, nscoord aSize, nscoord aMaxSize) {
1184 const uint32_t maxTrack = kMaxLine - 1;
1185 // Check for a repeat after the maximum allowed track.
1186 if (MOZ_UNLIKELY(mRepeatAutoStart >= maxTrack)) {
1187 mHasRepeatAuto = false;
1188 mRepeatAutoStart = 0;
1189 mRepeatAutoEnd = 0;
1190 return;
1192 uint32_t repeatTracks =
1193 CalculateRepeatFillCount(aGridGap, aMinSize, aSize, aMaxSize) *
1194 NumRepeatTracks();
1195 // Clamp the number of repeat tracks to the maximum possible track.
1196 repeatTracks = std::min(repeatTracks, maxTrack - mRepeatAutoStart);
1197 SetNumRepeatTracks(repeatTracks);
1198 // Blank out the removed flags for each of these tracks.
1199 mRemovedRepeatTracks.SetLength(repeatTracks);
1200 for (auto& track : mRemovedRepeatTracks) {
1201 track = false;
1205 uint32_t CalculateRepeatFillCount(
1206 const NonNegativeLengthPercentageOrNormal& aGridGap, nscoord aMinSize,
1207 nscoord aSize, nscoord aMaxSize) const {
1208 if (!mHasRepeatAuto) {
1209 return 0;
1211 // At this point no tracks will have been collapsed, so the RepeatEndDelta
1212 // should not be negative.
1213 MOZ_ASSERT(RepeatEndDelta() >= 0);
1214 // Note that this uses NumRepeatTracks and mRepeatAutoStart/End, although
1215 // the result of this method is used to change those values to a fully
1216 // expanded value. Spec quotes are from
1217 // https://drafts.csswg.org/css-grid/#repeat-notation
1218 const uint32_t numTracks = mExpandedTracks.Length() + RepeatEndDelta();
1219 MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track");
1220 if (MOZ_UNLIKELY(numTracks >= kMaxLine)) {
1221 // The fixed tracks plus an entire repetition is either larger or as
1222 // large as the maximum track, so we do not need to measure how many
1223 // repetitions will fit. This also avoids needing to check for if
1224 // kMaxLine - numTracks would underflow at the end where we clamp the
1225 // result.
1226 return 1;
1228 nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize;
1229 if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) {
1230 // "Otherwise, the specified track list repeats only once."
1231 return 1;
1233 nscoord repeatTrackSum = 0;
1234 // Note that one repeat() track size is included in |sum| in this loop.
1235 nscoord sum = 0;
1236 const nscoord percentBasis = aSize;
1237 for (uint32_t i = 0; i < numTracks; ++i) {
1238 // "treating each track as its max track sizing function if that is
1239 // definite or as its minimum track sizing function otherwise"
1240 // https://drafts.csswg.org/css-grid/#valdef-repeat-auto-fill
1241 const auto& sizingFunction = SizingFor(i);
1242 const auto& maxCoord = sizingFunction.GetMax();
1243 const auto* coord = &maxCoord;
1244 if (!coord->IsBreadth()) {
1245 coord = &sizingFunction.GetMin();
1246 if (!coord->IsBreadth()) {
1247 return 1;
1250 nscoord trackSize = ::ResolveToDefiniteSize(*coord, percentBasis);
1251 if (i >= mRepeatAutoStart && i < mRepeatAutoEnd) {
1252 // Use a minimum 1px for the repeat() track-size.
1253 if (trackSize < AppUnitsPerCSSPixel()) {
1254 trackSize = AppUnitsPerCSSPixel();
1256 repeatTrackSum += trackSize;
1258 sum += trackSize;
1260 nscoord gridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aSize);
1261 if (numTracks > 1) {
1262 // Add grid-gaps for all the tracks including the repeat() track.
1263 sum += gridGap * (numTracks - 1);
1265 // Calculate the max number of tracks that fits without overflow.
1266 nscoord available = maxFill != NS_UNCONSTRAINEDSIZE ? maxFill : aMinSize;
1267 nscoord spaceToFill = available - sum;
1268 if (spaceToFill <= 0) {
1269 // "if any number of repetitions would overflow, then 1 repetition"
1270 return 1;
1272 // Calculate the max number of tracks that fits without overflow.
1273 // Since we already have one repetition in sum, we can simply add one grid
1274 // gap for each element in the repeat.
1275 div_t q = div(spaceToFill, repeatTrackSum + gridGap * NumRepeatTracks());
1276 // The +1 here is for the one repeat track we already accounted for above.
1277 uint32_t numRepeatTracks = q.quot + 1;
1278 if (q.rem != 0 && maxFill == NS_UNCONSTRAINEDSIZE) {
1279 // "Otherwise, if the grid container has a definite min size in
1280 // the relevant axis, the number of repetitions is the largest possible
1281 // positive integer that fulfills that minimum requirement."
1282 ++numRepeatTracks; // one more to ensure the grid is at least min-size
1284 // Clamp the number of repeat tracks so that the last line <= kMaxLine.
1285 // (note that |numTracks| already includes one repeat() track)
1286 MOZ_ASSERT(numTracks >= NumRepeatTracks());
1287 const uint32_t maxRepeatTrackCount = kMaxLine - numTracks;
1288 const uint32_t maxRepetitions = maxRepeatTrackCount / NumRepeatTracks();
1289 return std::min(numRepeatTracks, maxRepetitions);
1293 * Compute the explicit grid end line number (in a zero-based grid).
1294 * @param aGridTemplateAreasEnd 'grid-template-areas' end line in this axis
1296 uint32_t ComputeExplicitGridEnd(uint32_t aGridTemplateAreasEnd) {
1297 uint32_t end = NumExplicitTracks() + 1;
1298 end = std::max(end, aGridTemplateAreasEnd);
1299 end = std::min(end, uint32_t(kMaxLine));
1300 return end;
1302 const StyleTrackSize& SizingFor(uint32_t aTrackIndex) const {
1303 static const StyleTrackSize kAutoTrackSize =
1304 StyleTrackSize::Breadth(StyleTrackBreadth::Auto());
1305 // |aIndex| is the relative index to mAutoSizing. A negative value means it
1306 // is the last Nth element.
1307 auto getImplicitSize = [this](int32_t aIndex) -> const StyleTrackSize& {
1308 MOZ_ASSERT(!(mAutoSizing.Length() == 1 &&
1309 mAutoSizing.AsSpan()[0] == kAutoTrackSize),
1310 "It's impossible to have one track with auto value because we "
1311 "filter out this case during parsing");
1313 if (mAutoSizing.IsEmpty()) {
1314 return kAutoTrackSize;
1317 // If multiple track sizes are given, the pattern is repeated as necessary
1318 // to find the size of the implicit tracks.
1319 int32_t i = aIndex % int32_t(mAutoSizing.Length());
1320 if (i < 0) {
1321 i += mAutoSizing.Length();
1323 return mAutoSizing.AsSpan()[i];
1326 if (MOZ_UNLIKELY(aTrackIndex < mExplicitGridOffset)) {
1327 // The last implicit grid track before the explicit grid receives the
1328 // last specified size, and so on backwards. Therefore we pass the
1329 // negative relative index to imply that we should get the implicit size
1330 // from the last Nth specified grid auto size.
1331 return getImplicitSize(int32_t(aTrackIndex) -
1332 int32_t(mExplicitGridOffset));
1334 uint32_t index = aTrackIndex - mExplicitGridOffset;
1335 MOZ_ASSERT(mRepeatAutoStart <= mRepeatAutoEnd);
1337 if (index >= mRepeatAutoStart) {
1338 if (index < mRepeatAutoEnd) {
1339 // Expand the repeat tracks.
1340 const auto& indices = mExpandedTracks[mRepeatAutoStart];
1341 const TrackListValue& value = mTrackListValues[indices.first];
1343 // We expect the default to be used for all track repeats.
1344 MOZ_ASSERT(indices.second == 0);
1346 const auto& repeatTracks = value.AsTrackRepeat().track_sizes.AsSpan();
1348 // Find the repeat track to use, skipping over any collapsed tracks.
1349 const uint32_t finalRepeatIndex = (index - mRepeatAutoStart);
1350 uint32_t repeatWithCollapsed = 0;
1351 // NOTE: We need SizingFor before the final collapsed tracks are known.
1352 // We know that it's invalid to have empty mRemovedRepeatTracks when
1353 // there are any repeat tracks, so we can detect that situation here.
1354 if (mRemovedRepeatTracks.IsEmpty()) {
1355 repeatWithCollapsed = finalRepeatIndex;
1356 } else {
1357 // Count up through the repeat tracks, until we have seen
1358 // finalRepeatIndex number of non-collapsed tracks.
1359 for (uint32_t repeatNoCollapsed = 0;
1360 repeatNoCollapsed < finalRepeatIndex; repeatWithCollapsed++) {
1361 if (!mRemovedRepeatTracks[repeatWithCollapsed]) {
1362 repeatNoCollapsed++;
1365 // If we stopped iterating on a collapsed track, continue to the next
1366 // non-collapsed track.
1367 while (mRemovedRepeatTracks[repeatWithCollapsed]) {
1368 repeatWithCollapsed++;
1371 return repeatTracks[repeatWithCollapsed % repeatTracks.Length()];
1372 } else {
1373 // The index is after the repeat auto range, adjust it to skip over the
1374 // repeat value. This will have no effect if there is no auto repeat,
1375 // since then RepeatEndDelta will return zero.
1376 index -= RepeatEndDelta();
1379 if (index >= mExpandedTracks.Length()) {
1380 return getImplicitSize(index - mExpandedTracks.Length());
1382 auto& indices = mExpandedTracks[index];
1383 const TrackListValue& value = mTrackListValues[indices.first];
1384 if (value.IsTrackSize()) {
1385 MOZ_ASSERT(indices.second == 0);
1386 return value.AsTrackSize();
1388 return value.AsTrackRepeat().track_sizes.AsSpan()[indices.second];
1390 const StyleTrackBreadth& MaxSizingFor(uint32_t aTrackIndex) const {
1391 return SizingFor(aTrackIndex).GetMax();
1393 const StyleTrackBreadth& MinSizingFor(uint32_t aTrackIndex) const {
1394 return SizingFor(aTrackIndex).GetMin();
1396 uint32_t NumExplicitTracks() const {
1397 return mExpandedTracks.Length() + RepeatEndDelta();
1399 uint32_t NumRepeatTracks() const { return mRepeatAutoEnd - mRepeatAutoStart; }
1400 // The difference between mExplicitGridEnd and mSizingFunctions.Length().
1401 int32_t RepeatEndDelta() const {
1402 return mHasRepeatAuto ? int32_t(NumRepeatTracks()) - 1 : 0;
1404 void SetNumRepeatTracks(uint32_t aNumRepeatTracks) {
1405 MOZ_ASSERT(mHasRepeatAuto || aNumRepeatTracks == 0);
1406 mRepeatAutoEnd = mRepeatAutoStart + aNumRepeatTracks;
1409 // Store mTrackListValues into mExpandedTracks with `repeat(INTEGER, ...)`
1410 // tracks expanded.
1411 void ExpandNonRepeatAutoTracks() {
1412 for (size_t i = 0; i < mTrackListValues.Length(); ++i) {
1413 auto& value = mTrackListValues[i];
1414 if (value.IsTrackSize()) {
1415 mExpandedTracks.EmplaceBack(i, 0);
1416 continue;
1418 auto& repeat = value.AsTrackRepeat();
1419 if (!repeat.count.IsNumber()) {
1420 MOZ_ASSERT(i == mRepeatAutoStart);
1421 mRepeatAutoStart = mExpandedTracks.Length();
1422 mRepeatAutoEnd = mRepeatAutoStart + repeat.track_sizes.Length();
1423 mExpandedTracks.EmplaceBack(i, 0);
1424 continue;
1426 for (auto j : IntegerRange(repeat.count.AsNumber())) {
1427 Unused << j;
1428 size_t trackSizesCount = repeat.track_sizes.Length();
1429 for (auto k : IntegerRange(trackSizesCount)) {
1430 mExpandedTracks.EmplaceBack(i, k);
1434 if (MOZ_UNLIKELY(mExpandedTracks.Length() > kMaxLine - 1)) {
1435 mExpandedTracks.TruncateLength(kMaxLine - 1);
1436 if (mHasRepeatAuto && mRepeatAutoStart > kMaxLine - 1) {
1437 // The `repeat(auto-fill/fit)` track is outside the clamped grid.
1438 mHasRepeatAuto = false;
1443 // Some style data references, for easy access.
1444 const GridTemplate& mTemplate;
1445 const Span<const TrackListValue> mTrackListValues;
1446 const StyleImplicitGridTracks& mAutoSizing;
1447 // An array from expanded track sizes (without expanding auto-repeat, which is
1448 // included just once at `mRepeatAutoStart`).
1450 // Each entry contains two indices, the first into mTrackListValues, and a
1451 // second one inside mTrackListValues' repeat value, if any, or zero
1452 // otherwise.
1453 nsTArray<std::pair<size_t, size_t>> mExpandedTracks;
1454 // Offset from the start of the implicit grid to the first explicit track.
1455 uint32_t mExplicitGridOffset;
1456 // The index of the repeat(auto-fill/fit) track, or zero if there is none.
1457 // Relative to mExplicitGridOffset (repeat tracks are explicit by definition).
1458 uint32_t mRepeatAutoStart;
1459 // The (hypothetical) index of the last such repeat() track.
1460 uint32_t mRepeatAutoEnd;
1461 // True if there is a specified repeat(auto-fill/fit) track.
1462 bool mHasRepeatAuto;
1463 // True if this track (relative to mRepeatAutoStart) is a removed auto-fit.
1464 // Indexed relative to mExplicitGridOffset + mRepeatAutoStart.
1465 nsTArray<bool> mRemovedRepeatTracks;
1469 * Utility class to find line names. It provides an interface to lookup line
1470 * names with a dynamic number of repeat(auto-fill/fit) tracks taken into
1471 * account.
1473 class MOZ_STACK_CLASS nsGridContainerFrame::LineNameMap {
1474 public:
1476 * Create a LineNameMap.
1477 * @param aStylePosition the style for the grid container
1478 * @param aImplicitNamedAreas the implicit areas for the grid container
1479 * @param aGridTemplate is the grid-template-rows/columns data for this axis
1480 * @param aParentLineNameMap the parent grid's map parallel to this map, or
1481 * null if this map isn't for a subgrid
1482 * @param aRange the subgrid's range in the parent grid, or null
1483 * @param aIsSameDirection true if our axis progresses in the same direction
1484 * in the subgrid and parent
1486 LineNameMap(const nsStylePosition* aStylePosition,
1487 const ImplicitNamedAreas* aImplicitNamedAreas,
1488 const TrackSizingFunctions& aTracks,
1489 const LineNameMap* aParentLineNameMap, const LineRange* aRange,
1490 bool aIsSameDirection)
1491 : mStylePosition(aStylePosition),
1492 mAreas(aImplicitNamedAreas),
1493 mRepeatAutoStart(aTracks.mRepeatAutoStart),
1494 mRepeatAutoEnd(aTracks.mRepeatAutoEnd),
1495 mRepeatEndDelta(aTracks.RepeatEndDelta()),
1496 mParentLineNameMap(aParentLineNameMap),
1497 mRange(aRange),
1498 mIsSameDirection(aIsSameDirection),
1499 mHasRepeatAuto(aTracks.mHasRepeatAuto) {
1500 if (MOZ_UNLIKELY(aRange)) { // subgrid case
1501 mClampMinLine = 1;
1502 mClampMaxLine = 1 + aRange->Extent();
1503 MOZ_ASSERT(aTracks.mTemplate.IsSubgrid(), "Should be subgrid type");
1504 ExpandRepeatLineNamesForSubgrid(*aTracks.mTemplate.AsSubgrid());
1505 // we've expanded all subgrid auto-fill lines in
1506 // ExpandRepeatLineNamesForSubgrid()
1507 mRepeatAutoStart = 0;
1508 mRepeatAutoEnd = mRepeatAutoStart;
1509 mHasRepeatAuto = false;
1510 } else {
1511 mClampMinLine = kMinLine;
1512 mClampMaxLine = kMaxLine;
1513 if (mHasRepeatAuto) {
1514 mTrackAutoRepeatLineNames =
1515 aTracks.mTemplate.GetRepeatAutoValue()->line_names.AsSpan();
1517 ExpandRepeatLineNames(aTracks);
1519 if (mHasRepeatAuto) {
1520 // We need mTemplateLinesEnd to be after all line names.
1521 // mExpandedLineNames has one repetition of the repeat(auto-fit/fill)
1522 // track name lists already, so we must subtract the number of repeat
1523 // track name lists to get to the number of non-repeat tracks, minus 2
1524 // because the first and last line name lists are shared with the
1525 // preceding and following non-repeat line name lists. We then add
1526 // mRepeatEndDelta to include the interior line name lists from repeat
1527 // tracks.
1528 mTemplateLinesEnd = mExpandedLineNames.Length() -
1529 (mTrackAutoRepeatLineNames.Length() - 2) +
1530 mRepeatEndDelta;
1531 } else {
1532 mTemplateLinesEnd = mExpandedLineNames.Length();
1534 MOZ_ASSERT(mHasRepeatAuto || mRepeatEndDelta <= 0);
1535 MOZ_ASSERT(!mHasRepeatAuto || aRange ||
1536 (mExpandedLineNames.Length() >= 2 &&
1537 mRepeatAutoStart <= mExpandedLineNames.Length()));
1540 // Store line names into mExpandedLineNames with `repeat(INTEGER, ...)`
1541 // expanded for non-subgrid.
1542 void ExpandRepeatLineNames(const TrackSizingFunctions& aTracks) {
1543 auto lineNameLists = aTracks.mTemplate.LineNameLists(false);
1545 const auto& trackListValues = aTracks.mTrackListValues;
1546 const NameList* nameListToMerge = nullptr;
1547 // NOTE(emilio): We rely on std::move clearing out the array.
1548 SmallPointerArray<const NameList> names;
1549 const uint32_t end =
1550 std::min<uint32_t>(lineNameLists.Length(), mClampMaxLine + 1);
1551 for (uint32_t i = 0; i < end; ++i) {
1552 if (nameListToMerge) {
1553 names.AppendElement(nameListToMerge);
1554 nameListToMerge = nullptr;
1556 names.AppendElement(&lineNameLists[i]);
1557 if (i >= trackListValues.Length()) {
1558 mExpandedLineNames.AppendElement(std::move(names));
1559 continue;
1561 const auto& value = trackListValues[i];
1562 if (value.IsTrackSize()) {
1563 mExpandedLineNames.AppendElement(std::move(names));
1564 continue;
1566 const auto& repeat = value.AsTrackRepeat();
1567 if (!repeat.count.IsNumber()) {
1568 const auto repeatNames = repeat.line_names.AsSpan();
1569 // If the repeat was truncated due to more than kMaxLine tracks, then
1570 // the repeat will no longer be set on mRepeatAutoStart).
1571 MOZ_ASSERT(!mHasRepeatAuto ||
1572 mRepeatAutoStart == mExpandedLineNames.Length());
1573 MOZ_ASSERT(repeatNames.Length() >= 2);
1574 for (const auto j : IntegerRange(repeatNames.Length() - 1)) {
1575 names.AppendElement(&repeatNames[j]);
1576 mExpandedLineNames.AppendElement(std::move(names));
1578 nameListToMerge = &repeatNames[repeatNames.Length() - 1];
1579 continue;
1581 for (auto j : IntegerRange(repeat.count.AsNumber())) {
1582 Unused << j;
1583 if (nameListToMerge) {
1584 names.AppendElement(nameListToMerge);
1585 nameListToMerge = nullptr;
1587 size_t trackSizesCount = repeat.track_sizes.Length();
1588 auto repeatLineNames = repeat.line_names.AsSpan();
1589 MOZ_ASSERT(repeatLineNames.Length() == trackSizesCount ||
1590 repeatLineNames.Length() == trackSizesCount + 1);
1591 for (auto k : IntegerRange(trackSizesCount)) {
1592 names.AppendElement(&repeatLineNames[k]);
1593 mExpandedLineNames.AppendElement(std::move(names));
1595 if (repeatLineNames.Length() == trackSizesCount + 1) {
1596 nameListToMerge = &repeatLineNames[trackSizesCount];
1601 if (MOZ_UNLIKELY(mExpandedLineNames.Length() > uint32_t(mClampMaxLine))) {
1602 mExpandedLineNames.TruncateLength(mClampMaxLine);
1606 // Store line names into mExpandedLineNames with `repeat(INTEGER, ...)`
1607 // expanded, and all `repeat(...)` expanded for subgrid.
1608 // https://drafts.csswg.org/css-grid/#resolved-track-list-subgrid
1609 void ExpandRepeatLineNamesForSubgrid(
1610 const StyleGenericLineNameList<StyleInteger>& aStyleLineNameList) {
1611 const auto& lineNameList = aStyleLineNameList.line_names.AsSpan();
1612 const uint32_t maxCount = mClampMaxLine + 1;
1613 const uint32_t end = lineNameList.Length();
1614 for (uint32_t i = 0; i < end && mExpandedLineNames.Length() < maxCount;
1615 ++i) {
1616 const auto& item = lineNameList[i];
1617 if (item.IsLineNames()) {
1618 // <line-names> case. Just copy it.
1619 SmallPointerArray<const NameList> names;
1620 names.AppendElement(&item.AsLineNames());
1621 mExpandedLineNames.AppendElement(std::move(names));
1622 continue;
1625 MOZ_ASSERT(item.IsRepeat());
1626 const auto& repeat = item.AsRepeat();
1627 const auto repeatLineNames = repeat.line_names.AsSpan();
1629 if (repeat.count.IsNumber()) {
1630 // Clone all <line-names>+ (repeated by N) into
1631 // |mExpandedLineNames|.
1632 for (uint32_t repeatCount = 0;
1633 repeatCount < (uint32_t)repeat.count.AsNumber(); ++repeatCount) {
1634 for (const NameList& lineNames : repeatLineNames) {
1635 SmallPointerArray<const NameList> names;
1636 names.AppendElement(&lineNames);
1637 mExpandedLineNames.AppendElement(std::move(names));
1638 if (mExpandedLineNames.Length() >= maxCount) {
1639 break;
1643 continue;
1646 MOZ_ASSERT(repeat.count.IsAutoFill(),
1647 "RepeatCount of subgrid is number or auto-fill");
1649 const size_t fillLen = repeatLineNames.Length();
1650 const int32_t extraAutoFillLineCount =
1651 mClampMaxLine -
1652 (int32_t)aStyleLineNameList.expanded_line_names_length;
1653 // Maximum possible number of repeat name lists.
1654 // Note: |expanded_line_names_length| doesn't include auto repeat.
1655 const uint32_t possibleRepeatLength =
1656 std::max<int32_t>(0, extraAutoFillLineCount);
1657 const uint32_t repeatRemainder = possibleRepeatLength % fillLen;
1659 // Note: Expand 'auto-fill' names for subgrid for now since
1660 // HasNameAt() only deals with auto-repeat **tracks** currently.
1661 const size_t len = possibleRepeatLength - repeatRemainder;
1662 for (size_t j = 0; j < len; ++j) {
1663 SmallPointerArray<const NameList> names;
1664 names.AppendElement(&repeatLineNames[j % fillLen]);
1665 mExpandedLineNames.AppendElement(std::move(names));
1666 if (mExpandedLineNames.Length() >= maxCount) {
1667 break;
1672 if (MOZ_UNLIKELY(mExpandedLineNames.Length() > uint32_t(mClampMaxLine))) {
1673 mExpandedLineNames.TruncateLength(mClampMaxLine);
1678 * Find the aNth occurrence of aName, searching forward if aNth is positive,
1679 * and in reverse if aNth is negative (aNth == 0 is invalid), starting from
1680 * aFromIndex (not inclusive), and return a 1-based line number.
1681 * Also take into account there is an unconditional match at the lines in
1682 * aImplicitLines.
1683 * Return zero if aNth occurrences can't be found. In that case, aNth has
1684 * been decremented with the number of occurrences that were found (if any).
1686 * E.g. to search for "A 2" forward from the start of the grid: aName is "A"
1687 * aNth is 2 and aFromIndex is zero. To search for "A -2", aNth is -2 and
1688 * aFromIndex is ExplicitGridEnd + 1 (which is the line "before" the last
1689 * line when we're searching in reverse). For "span A 2", aNth is 2 when
1690 * used on a grid-[row|column]-end property and -2 for a *-start property,
1691 * and aFromIndex is the line (which we should skip) on the opposite property.
1693 uint32_t FindNamedLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
1694 const nsTArray<uint32_t>& aImplicitLines) const {
1695 MOZ_ASSERT(aName);
1696 MOZ_ASSERT(!aName->IsEmpty());
1697 MOZ_ASSERT(aNth && *aNth != 0);
1698 if (*aNth > 0) {
1699 return FindLine(aName, aNth, aFromIndex, aImplicitLines);
1701 int32_t nth = -*aNth;
1702 int32_t line = RFindLine(aName, &nth, aFromIndex, aImplicitLines);
1703 *aNth = -nth;
1704 return line;
1708 * Return a set of lines in aImplicitLines which matches the area name aName
1709 * on aSide. For example, for aName "a" and aSide being an end side, it
1710 * returns the line numbers which would match "a-end" in the relevant axis.
1711 * For subgrids it includes searching the relevant axis in all ancestor
1712 * grids too (within this subgrid's spanned area). If an ancestor has
1713 * opposite direction, we switch aSide to the opposite logical side so we
1714 * match on the same physical side as the original subgrid we're resolving
1715 * the name for.
1717 void FindNamedAreas(nsAtom* aName, LogicalSide aSide,
1718 nsTArray<uint32_t>& aImplicitLines) const {
1719 // True if we're currently in a map that has the same direction as 'this'.
1720 bool sameDirectionAsThis = true;
1721 uint32_t min = !mParentLineNameMap ? 1 : mClampMinLine;
1722 uint32_t max = mClampMaxLine;
1723 for (auto* map = this; true;) {
1724 uint32_t line = map->FindNamedArea(aName, aSide, min, max);
1725 if (line > 0) {
1726 if (MOZ_LIKELY(sameDirectionAsThis)) {
1727 line -= min - 1;
1728 } else {
1729 line = max - line + 1;
1731 aImplicitLines.AppendElement(line);
1733 auto* parent = map->mParentLineNameMap;
1734 if (!parent) {
1735 if (MOZ_UNLIKELY(aImplicitLines.Length() > 1)) {
1736 // Remove duplicates and sort in ascending order.
1737 aImplicitLines.Sort();
1738 for (size_t i = 0; i < aImplicitLines.Length(); ++i) {
1739 uint32_t prev = aImplicitLines[i];
1740 auto j = i + 1;
1741 const auto start = j;
1742 while (j < aImplicitLines.Length() && aImplicitLines[j] == prev) {
1743 ++j;
1745 if (j != start) {
1746 aImplicitLines.RemoveElementsAt(start, j - start);
1750 return;
1752 if (MOZ_UNLIKELY(!map->mIsSameDirection)) {
1753 aSide = GetOppositeSide(aSide);
1754 sameDirectionAsThis = !sameDirectionAsThis;
1756 min = map->TranslateToParentMap(min);
1757 max = map->TranslateToParentMap(max);
1758 if (min > max) {
1759 MOZ_ASSERT(!map->mIsSameDirection);
1760 std::swap(min, max);
1762 map = parent;
1767 * Return true if any implicit named areas match aName, in this map or
1768 * in any of our ancestor maps.
1770 bool HasImplicitNamedArea(nsAtom* aName) const {
1771 const auto* map = this;
1772 do {
1773 if (map->mAreas && map->mAreas->has(aName)) {
1774 return true;
1776 map = map->mParentLineNameMap;
1777 } while (map);
1778 return false;
1781 // For generating line name data for devtools.
1782 nsTArray<nsTArray<StyleCustomIdent>>
1783 GetResolvedLineNamesForComputedGridTrackInfo() const {
1784 nsTArray<nsTArray<StyleCustomIdent>> result;
1785 for (auto& expandedLine : mExpandedLineNames) {
1786 nsTArray<StyleCustomIdent> line;
1787 for (auto* chunk : expandedLine) {
1788 for (auto& name : chunk->AsSpan()) {
1789 line.AppendElement(name);
1792 result.AppendElement(std::move(line));
1794 return result;
1797 nsTArray<RefPtr<nsAtom>> GetExplicitLineNamesAtIndex(uint32_t aIndex) const {
1798 nsTArray<RefPtr<nsAtom>> lineNames;
1799 if (aIndex < mTemplateLinesEnd) {
1800 const auto nameLists = GetLineNamesAt(aIndex);
1801 for (const NameList* nameList : nameLists) {
1802 for (const auto& name : nameList->AsSpan()) {
1803 lineNames.AppendElement(name.AsAtom());
1807 return lineNames;
1810 const nsTArray<SmallPointerArray<const NameList>>& ExpandedLineNames() const {
1811 return mExpandedLineNames;
1813 const Span<const StyleOwnedSlice<StyleCustomIdent>>&
1814 TrackAutoRepeatLineNames() const {
1815 return mTrackAutoRepeatLineNames;
1817 bool HasRepeatAuto() const { return mHasRepeatAuto; }
1818 uint32_t NumRepeatTracks() const { return mRepeatAutoEnd - mRepeatAutoStart; }
1819 uint32_t RepeatAutoStart() const { return mRepeatAutoStart; }
1821 // The min/max line number (1-based) for clamping.
1822 int32_t mClampMinLine;
1823 int32_t mClampMaxLine;
1825 private:
1826 // Return true if this map represents a subgridded axis.
1827 bool IsSubgridded() const { return mParentLineNameMap != nullptr; }
1830 * @see FindNamedLine, this function searches forward.
1832 uint32_t FindLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
1833 const nsTArray<uint32_t>& aImplicitLines) const {
1834 MOZ_ASSERT(aNth && *aNth > 0);
1835 int32_t nth = *aNth;
1836 // For a subgrid we need to search to the end of the grid rather than
1837 // the end of the local name list, since ancestors might match.
1838 const uint32_t end = IsSubgridded() ? mClampMaxLine : mTemplateLinesEnd;
1839 uint32_t line;
1840 uint32_t i = aFromIndex;
1841 for (; i < end; i = line) {
1842 line = i + 1;
1843 if (Contains(i, aName) || aImplicitLines.Contains(line)) {
1844 if (--nth == 0) {
1845 return line;
1849 for (auto implicitLine : aImplicitLines) {
1850 if (implicitLine > i) {
1851 // implicitLine is after the lines we searched above so it's last.
1852 // (grid-template-areas has more tracks than
1853 // grid-template-[rows|columns])
1854 if (--nth == 0) {
1855 return implicitLine;
1859 MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
1860 *aNth = nth;
1861 return 0;
1865 * @see FindNamedLine, this function searches in reverse.
1867 uint32_t RFindLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
1868 const nsTArray<uint32_t>& aImplicitLines) const {
1869 MOZ_ASSERT(aNth && *aNth > 0);
1870 if (MOZ_UNLIKELY(aFromIndex == 0)) {
1871 return 0; // There are no named lines beyond the start of the explicit
1872 // grid.
1874 --aFromIndex; // (shift aFromIndex so we can treat it as inclusive)
1875 int32_t nth = *aNth;
1876 // Implicit lines may be beyond the explicit grid so we match those
1877 // first if it's within the mTemplateLinesEnd..aFromIndex range.
1878 // aImplicitLines is presumed sorted.
1879 // For a subgrid we need to search to the end of the grid rather than
1880 // the end of the local name list, since ancestors might match.
1881 const uint32_t end = IsSubgridded() ? mClampMaxLine : mTemplateLinesEnd;
1882 for (auto implicitLine : Reversed(aImplicitLines)) {
1883 if (implicitLine <= end) {
1884 break;
1886 if (implicitLine < aFromIndex) {
1887 if (--nth == 0) {
1888 return implicitLine;
1892 for (uint32_t i = std::min(aFromIndex, end); i; --i) {
1893 if (Contains(i - 1, aName) || aImplicitLines.Contains(i)) {
1894 if (--nth == 0) {
1895 return i;
1899 MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
1900 *aNth = nth;
1901 return 0;
1904 // Return true if aName exists at aIndex in this map or any parent map.
1905 bool Contains(uint32_t aIndex, nsAtom* aName) const {
1906 const auto* map = this;
1907 while (true) {
1908 if (aIndex < map->mTemplateLinesEnd && map->HasNameAt(aIndex, aName)) {
1909 return true;
1911 auto* parent = map->mParentLineNameMap;
1912 if (!parent) {
1913 return false;
1915 uint32_t line = map->TranslateToParentMap(aIndex + 1);
1916 MOZ_ASSERT(line >= 1, "expected a 1-based line number");
1917 aIndex = line - 1;
1918 map = parent;
1920 MOZ_ASSERT_UNREACHABLE("we always return from inside the loop above");
1923 static bool Contains(Span<const StyleCustomIdent> aNames, nsAtom* aName) {
1924 for (auto& name : aNames) {
1925 if (name.AsAtom() == aName) {
1926 return true;
1929 return false;
1932 // Return true if aName exists at aIndex in this map.
1933 bool HasNameAt(const uint32_t aIndex, nsAtom* const aName) const {
1934 const auto nameLists = GetLineNamesAt(aIndex);
1935 for (const NameList* nameList : nameLists) {
1936 if (Contains(nameList->AsSpan(), aName)) {
1937 return true;
1940 return false;
1943 // Get the line names at an index.
1944 // This accounts for auto repeat. The results may be spread over multiple name
1945 // lists returned in the array, which is done to avoid unneccessarily copying
1946 // the arrays to concatenate them.
1947 SmallPointerArray<const NameList> GetLineNamesAt(
1948 const uint32_t aIndex) const {
1949 SmallPointerArray<const NameList> names;
1950 // The index into mExpandedLineNames to use, if aIndex doesn't point to a
1951 // name inside of a auto repeat.
1952 uint32_t repeatAdjustedIndex = aIndex;
1953 // Note: For subgrid, |mHasRepeatAuto| is always false because we have
1954 // expanded it in the constructor of LineNameMap.
1955 if (mHasRepeatAuto) {
1956 // If the index is inside of the auto repeat, use the repeat line
1957 // names. Otherwise, if the index is past the end of the repeat it must
1958 // be adjusted to acount for the repeat tracks.
1959 // mExpandedLineNames has the first and last line name lists from the
1960 // repeat in it already, so we can just ignore aIndex == mRepeatAutoStart
1961 // and treat when aIndex == mRepeatAutoEnd the same as any line after the
1962 // the repeat.
1963 const uint32_t maxRepeatLine = mTrackAutoRepeatLineNames.Length() - 1;
1964 if (aIndex > mRepeatAutoStart && aIndex < mRepeatAutoEnd) {
1965 // The index is inside the auto repeat. Calculate the lines to use,
1966 // including the previous repetitions final names when we roll over
1967 // from one repetition to the next.
1968 const uint32_t repeatIndex =
1969 (aIndex - mRepeatAutoStart) % maxRepeatLine;
1970 if (repeatIndex == 0) {
1971 // The index is at the start of a new repetition. The start of the
1972 // first repetition is intentionally ignored above, so this will
1973 // consider both the end of the previous repetition and the start
1974 // the one that contains aIndex.
1975 names.AppendElement(&mTrackAutoRepeatLineNames[maxRepeatLine]);
1977 names.AppendElement(&mTrackAutoRepeatLineNames[repeatIndex]);
1978 return names;
1980 if (aIndex != mRepeatAutoStart && aIndex >= mRepeatAutoEnd) {
1981 // Adjust the index to account for the line names of the repeat.
1982 repeatAdjustedIndex -= mRepeatEndDelta;
1983 repeatAdjustedIndex += mTrackAutoRepeatLineNames.Length() - 2;
1986 MOZ_ASSERT(repeatAdjustedIndex < mExpandedLineNames.Length(),
1987 "Incorrect repeatedAdjustedIndex");
1988 MOZ_ASSERT(names.IsEmpty());
1989 // The index is not inside the repeat tracks, or no repeat tracks exist.
1990 const auto& nameLists = mExpandedLineNames[repeatAdjustedIndex];
1991 for (const NameList* nameList : nameLists) {
1992 names.AppendElement(nameList);
1994 return names;
1997 // Translate a subgrid line (1-based) to a parent line (1-based).
1998 uint32_t TranslateToParentMap(uint32_t aLine) const {
1999 if (MOZ_LIKELY(mIsSameDirection)) {
2000 return aLine + mRange->mStart;
2002 MOZ_ASSERT(mRange->mEnd + 1 >= aLine);
2003 return mRange->mEnd - (aLine - 1) + 1;
2007 * Return the 1-based line that match aName in 'grid-template-areas'
2008 * on the side aSide. Clamp the result to aMin..aMax but require
2009 * that some part of the area is inside for it to match.
2010 * Return zero if there is no match.
2012 uint32_t FindNamedArea(nsAtom* aName, LogicalSide aSide, int32_t aMin,
2013 int32_t aMax) const {
2014 if (const NamedArea* area = FindNamedArea(aName)) {
2015 int32_t start = IsBlock(aSide) ? area->rows.start : area->columns.start;
2016 int32_t end = IsBlock(aSide) ? area->rows.end : area->columns.end;
2017 if (IsStart(aSide)) {
2018 if (start >= aMin) {
2019 if (start <= aMax) {
2020 return start;
2022 } else if (end >= aMin) {
2023 return aMin;
2025 } else {
2026 if (end <= aMax) {
2027 if (end >= aMin) {
2028 return end;
2030 } else if (start <= aMax) {
2031 return aMax;
2035 return 0; // no match
2039 * A convenience method to lookup a name in 'grid-template-areas'.
2040 * @return null if not found
2042 const NamedArea* FindNamedArea(nsAtom* aName) const {
2043 if (mStylePosition->mGridTemplateAreas.IsNone()) {
2044 return nullptr;
2046 const auto areas = mStylePosition->mGridTemplateAreas.AsAreas();
2047 for (const NamedArea& area : areas->areas.AsSpan()) {
2048 if (area.name.AsAtom() == aName) {
2049 return &area;
2052 return nullptr;
2055 // Some style data references, for easy access.
2056 const nsStylePosition* mStylePosition;
2057 const ImplicitNamedAreas* mAreas;
2058 // The expanded list of line-names. Each entry is usually a single NameList,
2059 // but can be multiple in the case where repeat() expands to something that
2060 // has a line name list at the end.
2061 nsTArray<SmallPointerArray<const NameList>> mExpandedLineNames;
2062 // The repeat(auto-fill/fit) track value, if any. (always empty for subgrid)
2063 Span<const StyleOwnedSlice<StyleCustomIdent>> mTrackAutoRepeatLineNames;
2064 // The index of the repeat(auto-fill/fit) track, or zero if there is none.
2065 uint32_t mRepeatAutoStart;
2066 // The index one past the end of the repeat(auto-fill/fit) tracks. Equal to
2067 // mRepeatAutoStart if there are no repeat(auto-fill/fit) tracks.
2068 uint32_t mRepeatAutoEnd;
2069 // The total number of repeat tracks minus 1.
2070 int32_t mRepeatEndDelta;
2071 // The end of the line name lists with repeat(auto-fill/fit) tracks accounted
2072 // for.
2073 uint32_t mTemplateLinesEnd;
2075 // The parent line map, or null if this map isn't for a subgrid.
2076 const LineNameMap* mParentLineNameMap;
2077 // The subgrid's range, or null if this map isn't for a subgrid.
2078 const LineRange* mRange;
2079 // True if the subgrid/parent axes progresses in the same direction.
2080 const bool mIsSameDirection;
2082 // True if there is a specified repeat(auto-fill/fit) track.
2083 bool mHasRepeatAuto;
2087 * State for the tracks in one dimension.
2089 struct nsGridContainerFrame::Tracks {
2090 explicit Tracks(LogicalAxis aAxis)
2091 : mContentBoxSize(NS_UNCONSTRAINEDSIZE),
2092 mGridGap(NS_UNCONSTRAINEDSIZE),
2093 mStateUnion(TrackSize::StateBits{0}),
2094 mAxis(aAxis),
2095 mCanResolveLineRangeSize(false),
2096 mIsMasonry(false) {
2097 mBaselineSubtreeAlign[BaselineSharingGroup::First] = StyleAlignFlags::AUTO;
2098 mBaselineSubtreeAlign[BaselineSharingGroup::Last] = StyleAlignFlags::AUTO;
2099 mBaseline[BaselineSharingGroup::First] = NS_INTRINSIC_ISIZE_UNKNOWN;
2100 mBaseline[BaselineSharingGroup::Last] = NS_INTRINSIC_ISIZE_UNKNOWN;
2103 void Initialize(const TrackSizingFunctions& aFunctions,
2104 const NonNegativeLengthPercentageOrNormal& aGridGap,
2105 uint32_t aNumTracks, nscoord aContentBoxSize);
2108 * Return the union of the state bits for the tracks in aRange.
2110 TrackSize::StateBits StateBitsForRange(const LineRange& aRange) const;
2112 // Some data we collect for aligning baseline-aligned items.
2113 struct ItemBaselineData {
2114 uint32_t mBaselineTrack;
2115 nscoord mBaseline;
2116 nscoord mSize;
2117 GridItemInfo* mGridItem;
2118 static bool IsBaselineTrackLessThan(const ItemBaselineData& a,
2119 const ItemBaselineData& b) {
2120 return a.mBaselineTrack < b.mBaselineTrack;
2125 * Calculate baseline offsets for the given set of items.
2126 * Helper for InitialzeItemBaselines.
2128 void CalculateItemBaselines(nsTArray<ItemBaselineData>& aBaselineItems,
2129 BaselineSharingGroup aBaselineGroup);
2132 * Initialize grid item baseline state and offsets.
2134 void InitializeItemBaselines(GridReflowInput& aState,
2135 nsTArray<GridItemInfo>& aGridItems);
2138 * A masonry axis has four baseline alignment sets and each set can have
2139 * a first- and last-baseline alignment group, for a total of eight possible
2140 * baseline alignment groups, as follows:
2141 * set 1: the first item in each `start` or `stretch` grid track
2142 * set 2: the last item in each `start` grid track
2143 * set 3: the last item in each `end` or `stretch` grid track
2144 * set 4: the first item in each `end` grid track
2145 * (`start`/`end`/`stretch` refers to the relevant `align/justify-tracks`
2146 * value of the (grid-axis) start track for the item) Baseline-alignment for
2147 * set 1 and 2 always adjusts the item's padding or margin on the start side,
2148 * and set 3 and 4 on the end side, for both first- and last-baseline groups
2149 * in the set. (This is similar to regular grid which always adjusts
2150 * first-baseline groups on the start side and last-baseline groups on the
2151 * end-side. The crux is that those groups are always aligned to the track's
2152 * start/end side respectively.)
2154 struct BaselineAlignmentSet {
2155 bool MatchTrackAlignment(StyleAlignFlags aTrackAlignment) const {
2156 if (mTrackAlignmentSet == BaselineAlignmentSet::StartStretch) {
2157 return aTrackAlignment == StyleAlignFlags::START ||
2158 (aTrackAlignment == StyleAlignFlags::STRETCH &&
2159 mItemSet == BaselineAlignmentSet::FirstItems);
2161 return aTrackAlignment == StyleAlignFlags::END ||
2162 (aTrackAlignment == StyleAlignFlags::STRETCH &&
2163 mItemSet == BaselineAlignmentSet::LastItems);
2166 enum ItemSet { FirstItems, LastItems };
2167 ItemSet mItemSet = FirstItems;
2168 enum TrackAlignmentSet { StartStretch, EndStretch };
2169 TrackAlignmentSet mTrackAlignmentSet = StartStretch;
2171 void InitializeItemBaselinesInMasonryAxis(
2172 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
2173 BaselineAlignmentSet aSet, const nsSize& aContainerSize,
2174 nsTArray<nscoord>& aTrackSizes,
2175 nsTArray<ItemBaselineData>& aFirstBaselineItems,
2176 nsTArray<ItemBaselineData>& aLastBaselineItems);
2179 * Apply the additional alignment needed to align the baseline-aligned subtree
2180 * the item belongs to within its baseline track.
2182 void AlignBaselineSubtree(const GridItemInfo& aGridItem) const;
2184 enum class TrackSizingPhase {
2185 IntrinsicMinimums,
2186 ContentBasedMinimums,
2187 MaxContentMinimums,
2188 IntrinsicMaximums,
2189 MaxContentMaximums,
2192 // Some data we collect on each item that spans more than one track for step 3
2193 // and 4 of the Track Sizing Algorithm in ResolveIntrinsicSize below.
2194 // https://w3c.github.io/csswg-drafts/css-grid-1/#algo-spanning-items
2195 struct SpanningItemData final {
2196 uint32_t mSpan;
2197 TrackSize::StateBits mState;
2198 LineRange mLineRange;
2199 nscoord mMinSize;
2200 nscoord mMinContentContribution;
2201 nscoord mMaxContentContribution;
2202 nsIFrame* mFrame;
2204 static bool IsSpanLessThan(const SpanningItemData& a,
2205 const SpanningItemData& b) {
2206 return a.mSpan < b.mSpan;
2209 template <TrackSizingPhase phase>
2210 nscoord SizeContributionForPhase() const {
2211 switch (phase) {
2212 case TrackSizingPhase::IntrinsicMinimums:
2213 return mMinSize;
2214 case TrackSizingPhase::ContentBasedMinimums:
2215 case TrackSizingPhase::IntrinsicMaximums:
2216 return mMinContentContribution;
2217 case TrackSizingPhase::MaxContentMinimums:
2218 case TrackSizingPhase::MaxContentMaximums:
2219 return mMaxContentContribution;
2221 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
2224 #ifdef DEBUG
2225 void Dump() const {
2226 printf(
2227 "SpanningItemData { mSpan: %d, mState: %d, mLineRange: (%d, %d), "
2228 "mMinSize: %d, mMinContentContribution: %d, mMaxContentContribution: "
2229 "%d, mFrame: %p\n",
2230 mSpan, mState, mLineRange.mStart, mLineRange.mEnd, mMinSize,
2231 mMinContentContribution, mMaxContentContribution, mFrame);
2233 #endif
2236 using FitContentClamper =
2237 std::function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
2239 // Helper method for ResolveIntrinsicSize.
2240 template <TrackSizingPhase phase>
2241 bool GrowSizeForSpanningItems(
2242 nsTArray<SpanningItemData>::iterator aIter,
2243 nsTArray<SpanningItemData>::iterator aIterEnd,
2244 nsTArray<uint32_t>& aTracks, nsTArray<TrackSize>& aPlan,
2245 nsTArray<TrackSize>& aItemPlan, TrackSize::StateBits aSelector,
2246 const FitContentClamper& aFitContentClamper = nullptr,
2247 bool aNeedInfinitelyGrowableFlag = false);
2249 * Resolve Intrinsic Track Sizes.
2250 * http://dev.w3.org/csswg/css-grid/#algo-content
2252 void ResolveIntrinsicSize(GridReflowInput& aState,
2253 nsTArray<GridItemInfo>& aGridItems,
2254 const TrackSizingFunctions& aFunctions,
2255 LineRange GridArea::*aRange,
2256 nscoord aPercentageBasis,
2257 SizingConstraint aConstraint);
2260 * Helper for ResolveIntrinsicSize. It implements step 1 "size tracks to fit
2261 * non-spanning items" in the spec. Return true if the track has a <flex>
2262 * max-sizing function, false otherwise.
2264 bool ResolveIntrinsicSizeForNonSpanningItems(
2265 GridReflowInput& aState, const TrackSizingFunctions& aFunctions,
2266 nscoord aPercentageBasis, SizingConstraint aConstraint,
2267 const LineRange& aRange, const GridItemInfo& aGridItem);
2269 // Helper method that returns the track size to use in §11.5.1.2
2270 // https://drafts.csswg.org/css-grid/#extra-space
2271 template <TrackSizingPhase phase>
2272 static nscoord StartSizeInDistribution(const TrackSize& aSize) {
2273 switch (phase) {
2274 case TrackSizingPhase::IntrinsicMinimums:
2275 case TrackSizingPhase::ContentBasedMinimums:
2276 case TrackSizingPhase::MaxContentMinimums:
2277 return aSize.mBase;
2278 case TrackSizingPhase::IntrinsicMaximums:
2279 case TrackSizingPhase::MaxContentMaximums:
2280 if (aSize.mLimit == NS_UNCONSTRAINEDSIZE) {
2281 return aSize.mBase;
2283 return aSize.mLimit;
2285 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
2289 * Collect the tracks which are growable (matching aSelector) into
2290 * aGrowableTracks, and return the amount of space that can be used
2291 * to grow those tracks. This method implements CSS Grid §11.5.1.2.
2292 * https://drafts.csswg.org/css-grid/#extra-space
2294 template <TrackSizingPhase phase>
2295 nscoord CollectGrowable(nscoord aAvailableSpace, const LineRange& aRange,
2296 TrackSize::StateBits aSelector,
2297 nsTArray<uint32_t>& aGrowableTracks) const {
2298 MOZ_ASSERT(aAvailableSpace > 0, "why call me?");
2299 nscoord space = aAvailableSpace - mGridGap * (aRange.Extent() - 1);
2300 for (auto i : aRange.Range()) {
2301 const TrackSize& sz = mSizes[i];
2302 space -= StartSizeInDistribution<phase>(sz);
2303 if (space <= 0) {
2304 return 0;
2306 if (sz.mState & aSelector) {
2307 aGrowableTracks.AppendElement(i);
2310 return aGrowableTracks.IsEmpty() ? 0 : space;
2313 template <TrackSizingPhase phase>
2314 void InitializeItemPlan(nsTArray<TrackSize>& aItemPlan,
2315 const nsTArray<uint32_t>& aTracks) const {
2316 for (uint32_t track : aTracks) {
2317 auto& plan = aItemPlan[track];
2318 const TrackSize& sz = mSizes[track];
2319 plan.mBase = StartSizeInDistribution<phase>(sz);
2320 bool unlimited = sz.mState & TrackSize::eInfinitelyGrowable;
2321 plan.mLimit = unlimited ? NS_UNCONSTRAINEDSIZE : sz.mLimit;
2322 plan.mState = sz.mState;
2326 template <TrackSizingPhase phase>
2327 void InitializePlan(nsTArray<TrackSize>& aPlan) const {
2328 for (size_t i = 0, len = aPlan.Length(); i < len; ++i) {
2329 auto& plan = aPlan[i];
2330 const auto& sz = mSizes[i];
2331 plan.mBase = StartSizeInDistribution<phase>(sz);
2332 MOZ_ASSERT(phase == TrackSizingPhase::MaxContentMaximums ||
2333 !(sz.mState & TrackSize::eInfinitelyGrowable),
2334 "forgot to reset the eInfinitelyGrowable bit?");
2335 plan.mState = sz.mState;
2339 template <TrackSizingPhase phase>
2340 void CopyPlanToSize(const nsTArray<TrackSize>& aPlan,
2341 bool aNeedInfinitelyGrowableFlag = false) {
2342 for (size_t i = 0, len = mSizes.Length(); i < len; ++i) {
2343 const auto& plan = aPlan[i];
2344 MOZ_ASSERT(plan.mBase >= 0);
2345 auto& sz = mSizes[i];
2346 switch (phase) {
2347 case TrackSizingPhase::IntrinsicMinimums:
2348 case TrackSizingPhase::ContentBasedMinimums:
2349 case TrackSizingPhase::MaxContentMinimums:
2350 sz.mBase = plan.mBase;
2351 break;
2352 case TrackSizingPhase::IntrinsicMaximums:
2353 if (plan.mState & TrackSize::eModified) {
2354 if (sz.mLimit == NS_UNCONSTRAINEDSIZE &&
2355 aNeedInfinitelyGrowableFlag) {
2356 sz.mState |= TrackSize::eInfinitelyGrowable;
2358 sz.mLimit = plan.mBase;
2360 break;
2361 case TrackSizingPhase::MaxContentMaximums:
2362 if (plan.mState & TrackSize::eModified) {
2363 sz.mLimit = plan.mBase;
2365 sz.mState &= ~TrackSize::eInfinitelyGrowable;
2366 break;
2372 * Grow the planned size for tracks in aGrowableTracks up to their limit
2373 * and then freeze them (all aGrowableTracks must be unfrozen on entry).
2374 * Subtract the space added from aAvailableSpace and return that.
2376 nscoord GrowTracksToLimit(nscoord aAvailableSpace, nsTArray<TrackSize>& aPlan,
2377 const nsTArray<uint32_t>& aGrowableTracks,
2378 const FitContentClamper& aFitContentClamper) const {
2379 MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0);
2380 nscoord space = aAvailableSpace;
2381 uint32_t numGrowable = aGrowableTracks.Length();
2382 while (true) {
2383 nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
2384 for (uint32_t track : aGrowableTracks) {
2385 TrackSize& sz = aPlan[track];
2386 if (sz.IsFrozen()) {
2387 continue;
2389 nscoord newBase = sz.mBase + spacePerTrack;
2390 nscoord limit = sz.mLimit;
2391 if (MOZ_UNLIKELY((sz.mState & TrackSize::eFitContent) &&
2392 aFitContentClamper)) {
2393 // Clamp the limit to the fit-content() size, for §12.5.2 step 5/6.
2394 aFitContentClamper(track, sz.mBase, &limit);
2396 if (newBase > limit) {
2397 nscoord consumed = limit - sz.mBase;
2398 if (consumed > 0) {
2399 space -= consumed;
2400 sz.mBase = limit;
2402 sz.mState |= TrackSize::eFrozen;
2403 if (--numGrowable == 0) {
2404 return space;
2406 } else {
2407 sz.mBase = newBase;
2408 space -= spacePerTrack;
2410 MOZ_ASSERT(space >= 0);
2411 if (space == 0) {
2412 return 0;
2416 MOZ_ASSERT_UNREACHABLE("we don't exit the loop above except by return");
2417 return 0;
2421 * Helper for GrowSelectedTracksUnlimited. For the set of tracks (S) that
2422 * match aMinSizingSelector: if a track in S doesn't match aMaxSizingSelector
2423 * then mark it with aSkipFlag. If all tracks in S were marked then unmark
2424 * them. Return aNumGrowable minus the number of tracks marked. It is
2425 * assumed that aPlan have no aSkipFlag set for tracks in aGrowableTracks
2426 * on entry to this method.
2428 static uint32_t MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
2429 uint32_t aNumGrowable,
2430 const nsTArray<uint32_t>& aGrowableTracks,
2431 TrackSize::StateBits aMinSizingSelector,
2432 TrackSize::StateBits aMaxSizingSelector,
2433 TrackSize::StateBits aSkipFlag) {
2434 bool foundOneSelected = false;
2435 bool foundOneGrowable = false;
2436 uint32_t numGrowable = aNumGrowable;
2437 for (uint32_t track : aGrowableTracks) {
2438 TrackSize& sz = aPlan[track];
2439 const auto state = sz.mState;
2440 if (state & aMinSizingSelector) {
2441 foundOneSelected = true;
2442 if (state & aMaxSizingSelector) {
2443 foundOneGrowable = true;
2444 continue;
2446 sz.mState |= aSkipFlag;
2447 MOZ_ASSERT(numGrowable != 0);
2448 --numGrowable;
2451 // 12.5 "if there are no such tracks, then all affected tracks"
2452 if (foundOneSelected && !foundOneGrowable) {
2453 for (uint32_t track : aGrowableTracks) {
2454 aPlan[track].mState &= ~aSkipFlag;
2456 numGrowable = aNumGrowable;
2458 return numGrowable;
2462 * Mark all tracks in aGrowableTracks with an eSkipGrowUnlimited bit if
2463 * they *shouldn't* grow unlimited in §11.5.1.2.3 "Distribute space beyond
2464 * growth limits" https://drafts.csswg.org/css-grid/#extra-space
2465 * Return the number of tracks that are still growable.
2467 template <TrackSizingPhase phase>
2468 static uint32_t MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
2469 const nsTArray<uint32_t>& aGrowableTracks,
2470 TrackSize::StateBits aSelector) {
2471 uint32_t numGrowable = aGrowableTracks.Length();
2472 if (phase == TrackSizingPhase::IntrinsicMaximums ||
2473 phase == TrackSizingPhase::MaxContentMaximums) {
2474 // "when handling any intrinsic growth limit: all affected tracks"
2475 return numGrowable;
2477 MOZ_ASSERT(aSelector == (aSelector & TrackSize::eIntrinsicMinSizing) &&
2478 (aSelector & TrackSize::eMaxContentMinSizing),
2479 "Should only get here for track sizing steps 2.1 to 2.3");
2480 // Note that eMaxContentMinSizing is always included. We do those first:
2481 numGrowable = MarkExcludedTracks(
2482 aPlan, numGrowable, aGrowableTracks, TrackSize::eMaxContentMinSizing,
2483 TrackSize::eMaxContentMaxSizing, TrackSize::eSkipGrowUnlimited1);
2484 // Now mark min-content/auto min-sizing tracks if requested.
2485 auto minOrAutoSelector = aSelector & ~TrackSize::eMaxContentMinSizing;
2486 if (minOrAutoSelector) {
2487 numGrowable = MarkExcludedTracks(
2488 aPlan, numGrowable, aGrowableTracks, minOrAutoSelector,
2489 TrackSize::eIntrinsicMaxSizing, TrackSize::eSkipGrowUnlimited2);
2491 return numGrowable;
2495 * Increase the planned size for tracks in aGrowableTracks that aren't
2496 * marked with a eSkipGrowUnlimited flag beyond their limit.
2497 * This implements the "Distribute space beyond growth limits" step in
2498 * https://drafts.csswg.org/css-grid/#distribute-extra-space
2500 void GrowSelectedTracksUnlimited(
2501 nscoord aAvailableSpace, nsTArray<TrackSize>& aPlan,
2502 const nsTArray<uint32_t>& aGrowableTracks, uint32_t aNumGrowable,
2503 const FitContentClamper& aFitContentClamper) const {
2504 MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0 &&
2505 aNumGrowable <= aGrowableTracks.Length());
2506 nscoord space = aAvailableSpace;
2507 DebugOnly<bool> didClamp = false;
2508 while (aNumGrowable) {
2509 nscoord spacePerTrack = std::max<nscoord>(space / aNumGrowable, 1);
2510 for (uint32_t track : aGrowableTracks) {
2511 TrackSize& sz = aPlan[track];
2512 if (sz.mState & TrackSize::eSkipGrowUnlimited) {
2513 continue; // an excluded track
2515 nscoord delta = spacePerTrack;
2516 nscoord newBase = sz.mBase + delta;
2517 if (MOZ_UNLIKELY((sz.mState & TrackSize::eFitContent) &&
2518 aFitContentClamper)) {
2519 // Clamp newBase to the fit-content() size, for §12.5.2 step 5/6.
2520 if (aFitContentClamper(track, sz.mBase, &newBase)) {
2521 didClamp = true;
2522 delta = newBase - sz.mBase;
2523 MOZ_ASSERT(delta >= 0, "track size shouldn't shrink");
2524 sz.mState |= TrackSize::eSkipGrowUnlimited1;
2525 --aNumGrowable;
2528 sz.mBase = newBase;
2529 space -= delta;
2530 MOZ_ASSERT(space >= 0);
2531 if (space == 0) {
2532 return;
2536 MOZ_ASSERT(didClamp,
2537 "we don't exit the loop above except by return, "
2538 "unless we clamped some track's size");
2542 * Distribute aAvailableSpace to the planned base size for aGrowableTracks
2543 * up to their limits, then distribute the remaining space beyond the limits.
2545 template <TrackSizingPhase phase>
2546 void DistributeToTrackSizes(nscoord aAvailableSpace,
2547 nsTArray<TrackSize>& aPlan,
2548 nsTArray<TrackSize>& aItemPlan,
2549 nsTArray<uint32_t>& aGrowableTracks,
2550 TrackSize::StateBits aSelector,
2551 const FitContentClamper& aFitContentClamper) {
2552 InitializeItemPlan<phase>(aItemPlan, aGrowableTracks);
2553 nscoord space = GrowTracksToLimit(aAvailableSpace, aItemPlan,
2554 aGrowableTracks, aFitContentClamper);
2555 if (space > 0) {
2556 uint32_t numGrowable =
2557 MarkExcludedTracks<phase>(aItemPlan, aGrowableTracks, aSelector);
2558 GrowSelectedTracksUnlimited(space, aItemPlan, aGrowableTracks,
2559 numGrowable, aFitContentClamper);
2561 for (uint32_t track : aGrowableTracks) {
2562 nscoord& plannedSize = aPlan[track].mBase;
2563 nscoord itemIncurredSize = aItemPlan[track].mBase;
2564 if (plannedSize < itemIncurredSize) {
2565 plannedSize = itemIncurredSize;
2571 * Distribute aAvailableSize to the tracks. This implements 12.6 at:
2572 * http://dev.w3.org/csswg/css-grid/#algo-grow-tracks
2574 void DistributeFreeSpace(nscoord aAvailableSize) {
2575 const uint32_t numTracks = mSizes.Length();
2576 if (MOZ_UNLIKELY(numTracks == 0 || aAvailableSize <= 0)) {
2577 return;
2579 if (aAvailableSize == NS_UNCONSTRAINEDSIZE) {
2580 for (TrackSize& sz : mSizes) {
2581 sz.mBase = sz.mLimit;
2583 } else {
2584 // Compute free space and count growable tracks.
2585 nscoord space = aAvailableSize;
2586 uint32_t numGrowable = numTracks;
2587 for (const TrackSize& sz : mSizes) {
2588 space -= sz.mBase;
2589 MOZ_ASSERT(sz.mBase <= sz.mLimit);
2590 if (sz.mBase == sz.mLimit) {
2591 --numGrowable;
2594 // Distribute the free space evenly to the growable tracks. If not exactly
2595 // divisable the remainder is added to the leading tracks.
2596 while (space > 0 && numGrowable) {
2597 nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
2598 for (uint32_t i = 0; i < numTracks && space > 0; ++i) {
2599 TrackSize& sz = mSizes[i];
2600 if (sz.mBase == sz.mLimit) {
2601 continue;
2603 nscoord newBase = sz.mBase + spacePerTrack;
2604 if (newBase >= sz.mLimit) {
2605 space -= sz.mLimit - sz.mBase;
2606 sz.mBase = sz.mLimit;
2607 --numGrowable;
2608 } else {
2609 space -= spacePerTrack;
2610 sz.mBase = newBase;
2618 * Implements "12.7.1. Find the Size of an 'fr'".
2619 * http://dev.w3.org/csswg/css-grid/#algo-find-fr-size
2620 * (The returned value is a 'nscoord' divided by a factor - a floating type
2621 * is used to avoid intermediary rounding errors.)
2623 float FindFrUnitSize(const LineRange& aRange,
2624 const nsTArray<uint32_t>& aFlexTracks,
2625 const TrackSizingFunctions& aFunctions,
2626 nscoord aSpaceToFill) const;
2629 * Implements the "find the used flex fraction" part of StretchFlexibleTracks.
2630 * (The returned value is a 'nscoord' divided by a factor - a floating type
2631 * is used to avoid intermediary rounding errors.)
2633 float FindUsedFlexFraction(GridReflowInput& aState,
2634 nsTArray<GridItemInfo>& aGridItems,
2635 const nsTArray<uint32_t>& aFlexTracks,
2636 const TrackSizingFunctions& aFunctions,
2637 nscoord aAvailableSize) const;
2640 * Implements "12.7. Stretch Flexible Tracks"
2641 * http://dev.w3.org/csswg/css-grid/#algo-flex-tracks
2643 void StretchFlexibleTracks(GridReflowInput& aState,
2644 nsTArray<GridItemInfo>& aGridItems,
2645 const TrackSizingFunctions& aFunctions,
2646 nscoord aAvailableSize);
2649 * Implements "12.3. Track Sizing Algorithm"
2650 * http://dev.w3.org/csswg/css-grid/#algo-track-sizing
2652 void CalculateSizes(GridReflowInput& aState,
2653 nsTArray<GridItemInfo>& aGridItems,
2654 const TrackSizingFunctions& aFunctions,
2655 nscoord aContentBoxSize, LineRange GridArea::*aRange,
2656 SizingConstraint aConstraint);
2659 * Apply 'align/justify-content', whichever is relevant for this axis.
2660 * https://drafts.csswg.org/css-align-3/#propdef-align-content
2662 void AlignJustifyContent(const nsStylePosition* aStyle,
2663 StyleContentDistribution aAligmentStyleValue,
2664 WritingMode aWM, nscoord aContentBoxSize,
2665 bool aIsSubgridded);
2667 nscoord GridLineEdge(uint32_t aLine, GridLineSide aSide) const {
2668 if (MOZ_UNLIKELY(mSizes.IsEmpty())) {
2669 // https://drafts.csswg.org/css-grid/#grid-definition
2670 // "... the explicit grid still contains one grid line in each axis."
2671 MOZ_ASSERT(aLine == 0, "We should only resolve line 1 in an empty grid");
2672 return nscoord(0);
2674 MOZ_ASSERT(aLine <= mSizes.Length(), "mSizes is too small");
2675 if (aSide == GridLineSide::BeforeGridGap) {
2676 if (aLine == 0) {
2677 return nscoord(0);
2679 const TrackSize& sz = mSizes[aLine - 1];
2680 return sz.mPosition + sz.mBase;
2682 if (aLine == mSizes.Length()) {
2683 return mContentBoxSize;
2685 return mSizes[aLine].mPosition;
2688 nscoord SumOfGridTracksAndGaps() {
2689 return SumOfGridTracks() + SumOfGridGaps();
2692 nscoord SumOfGridTracks() const {
2693 nscoord result = 0;
2694 for (const TrackSize& size : mSizes) {
2695 result += size.mBase;
2697 return result;
2700 nscoord SumOfGridGaps() const {
2701 auto len = mSizes.Length();
2702 return MOZ_LIKELY(len > 1) ? (len - 1) * mGridGap : 0;
2706 * Break before aRow, i.e. set the eBreakBefore flag on aRow and set the grid
2707 * gap before aRow to zero (and shift all rows after it by the removed gap).
2709 void BreakBeforeRow(uint32_t aRow) {
2710 MOZ_ASSERT(mAxis == eLogicalAxisBlock,
2711 "Should only be fragmenting in the block axis (between rows)");
2712 nscoord prevRowEndPos = 0;
2713 if (aRow != 0) {
2714 auto& prevSz = mSizes[aRow - 1];
2715 prevRowEndPos = prevSz.mPosition + prevSz.mBase;
2717 auto& sz = mSizes[aRow];
2718 const nscoord gap = sz.mPosition - prevRowEndPos;
2719 sz.mState |= TrackSize::eBreakBefore;
2720 if (gap != 0) {
2721 for (uint32_t i = aRow, len = mSizes.Length(); i < len; ++i) {
2722 mSizes[i].mPosition -= gap;
2728 * Set the size of aRow to aSize and adjust the position of all rows after it.
2730 void ResizeRow(uint32_t aRow, nscoord aNewSize) {
2731 MOZ_ASSERT(mAxis == eLogicalAxisBlock,
2732 "Should only be fragmenting in the block axis (between rows)");
2733 MOZ_ASSERT(aNewSize >= 0);
2734 auto& sz = mSizes[aRow];
2735 nscoord delta = aNewSize - sz.mBase;
2736 NS_WARNING_ASSERTION(delta != nscoord(0), "Useless call to ResizeRow");
2737 sz.mBase = aNewSize;
2738 const uint32_t numRows = mSizes.Length();
2739 for (uint32_t r = aRow + 1; r < numRows; ++r) {
2740 mSizes[r].mPosition += delta;
2744 nscoord ResolveSize(const LineRange& aRange) const {
2745 MOZ_ASSERT(mCanResolveLineRangeSize);
2746 MOZ_ASSERT(aRange.Extent() > 0, "grid items cover at least one track");
2747 nscoord pos, size;
2748 aRange.ToPositionAndLength(mSizes, &pos, &size);
2749 return size;
2752 #ifdef DEBUG
2753 void Dump() const;
2754 #endif
2756 CopyableAutoTArray<TrackSize, 32> mSizes;
2757 nscoord mContentBoxSize;
2758 nscoord mGridGap;
2759 // The first(last)-baseline for the first(last) track in this axis.
2760 PerBaseline<nscoord> mBaseline;
2761 // The union of the track min/max-sizing state bits in this axis.
2762 TrackSize::StateBits mStateUnion;
2763 LogicalAxis mAxis;
2764 // Used for aligning a baseline-aligned subtree of items. The only possible
2765 // values are StyleAlignFlags::{START,END,CENTER,AUTO}. AUTO means there are
2766 // no baseline-aligned items in any track in that axis.
2767 // There is one alignment value for each BaselineSharingGroup.
2768 PerBaseline<StyleAlignFlags> mBaselineSubtreeAlign;
2769 // True if track positions and sizes are final in this axis.
2770 bool mCanResolveLineRangeSize;
2771 // True if this axis has masonry layout.
2772 bool mIsMasonry;
2775 #ifdef DEBUG
2776 void nsGridContainerFrame::Tracks::Dump() const {
2777 printf("%zu %s %s ", mSizes.Length(), mIsMasonry ? "masonry" : "grid",
2778 mAxis == eLogicalAxisBlock ? "rows" : "columns");
2779 TrackSize::DumpStateBits(mStateUnion);
2780 printf("\n");
2781 for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
2782 printf(" %d: ", i);
2783 mSizes[i].Dump();
2784 printf("\n");
2786 double px = AppUnitsPerCSSPixel();
2787 printf("Baselines: %.2fpx %2fpx\n",
2788 mBaseline[BaselineSharingGroup::First] / px,
2789 mBaseline[BaselineSharingGroup::Last] / px);
2790 printf("Gap: %.2fpx\n", mGridGap / px);
2791 printf("ContentBoxSize: %.2fpx\n", mContentBoxSize / px);
2793 #endif
2796 * Grid data shared by all continuations, owned by the first-in-flow.
2797 * The data is initialized from the first-in-flow's GridReflowInput at
2798 * the end of its reflow. Fragmentation will modify mRows.mSizes -
2799 * the mPosition to remove the row gap at the break boundary, the mState
2800 * by setting the eBreakBefore flag, and mBase is modified when we decide
2801 * to grow a row. mOriginalRowData is setup by the first-in-flow and
2802 * not modified after that. It's used for undoing the changes to mRows.
2803 * mCols, mGridItems, mAbsPosItems are used for initializing the grid
2804 * reflow input for continuations, see GridReflowInput::Initialize below.
2806 struct nsGridContainerFrame::SharedGridData {
2807 SharedGridData()
2808 : mCols(eLogicalAxisInline),
2809 mRows(eLogicalAxisBlock),
2810 mGenerateComputedGridInfo(false) {}
2811 Tracks mCols;
2812 Tracks mRows;
2813 struct RowData {
2814 nscoord mBase; // the original track size
2815 nscoord mGap; // the original gap before a track
2817 nsTArray<RowData> mOriginalRowData;
2818 nsTArray<GridItemInfo> mGridItems;
2819 nsTArray<GridItemInfo> mAbsPosItems;
2820 bool mGenerateComputedGridInfo;
2823 * Only set on the first-in-flow. Continuations will Initialize() their
2824 * GridReflowInput from it.
2826 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, SharedGridData)
2829 struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput {
2830 GridReflowInput(nsGridContainerFrame* aFrame, const ReflowInput& aRI)
2831 : GridReflowInput(aFrame, *aRI.mRenderingContext, &aRI,
2832 aRI.mStylePosition, aRI.GetWritingMode()) {}
2833 GridReflowInput(nsGridContainerFrame* aFrame, gfxContext& aRC)
2834 : GridReflowInput(aFrame, aRC, nullptr, aFrame->StylePosition(),
2835 aFrame->GetWritingMode()) {}
2838 * Initialize our track sizes and grid item info using the shared
2839 * state from aGridContainerFrame first-in-flow.
2841 void InitializeForContinuation(nsGridContainerFrame* aGridContainerFrame,
2842 nscoord aConsumedBSize) {
2843 MOZ_ASSERT(aGridContainerFrame->GetPrevInFlow(),
2844 "don't call this on the first-in-flow");
2845 MOZ_ASSERT(mGridItems.IsEmpty() && mAbsPosItems.IsEmpty(),
2846 "shouldn't have any item data yet");
2848 // Get the SharedGridData from the first-in-flow. Also calculate the number
2849 // of fragments before this so that we can figure out our start row below.
2850 uint32_t fragment = 0;
2851 nsIFrame* firstInFlow = aGridContainerFrame;
2852 for (auto pif = aGridContainerFrame->GetPrevInFlow(); pif;
2853 pif = pif->GetPrevInFlow()) {
2854 ++fragment;
2855 firstInFlow = pif;
2857 mSharedGridData = firstInFlow->GetProperty(SharedGridData::Prop());
2858 MOZ_ASSERT(mSharedGridData, "first-in-flow must have SharedGridData");
2860 // Find the start row for this fragment and undo breaks after that row
2861 // since the breaks might be different from the last reflow.
2862 auto& rowSizes = mSharedGridData->mRows.mSizes;
2863 const uint32_t numRows = rowSizes.Length();
2864 mStartRow = numRows;
2865 for (uint32_t row = 0, breakCount = 0; row < numRows; ++row) {
2866 if (rowSizes[row].mState & TrackSize::eBreakBefore) {
2867 if (fragment == ++breakCount) {
2868 mStartRow = row;
2869 mFragBStart = rowSizes[row].mPosition;
2870 // Restore the original size for |row| and grid gaps / state after it.
2871 const auto& origRowData = mSharedGridData->mOriginalRowData;
2872 rowSizes[row].mBase = origRowData[row].mBase;
2873 nscoord prevEndPos = rowSizes[row].mPosition + rowSizes[row].mBase;
2874 while (++row < numRows) {
2875 auto& sz = rowSizes[row];
2876 const auto& orig = origRowData[row];
2877 sz.mPosition = prevEndPos + orig.mGap;
2878 sz.mBase = orig.mBase;
2879 sz.mState &= ~TrackSize::eBreakBefore;
2880 prevEndPos = sz.mPosition + sz.mBase;
2882 break;
2886 if (mStartRow == numRows ||
2887 aGridContainerFrame->IsMasonry(eLogicalAxisBlock)) {
2888 // All of the grid's rows fit inside of previous grid-container fragments,
2889 // or it's a masonry axis.
2890 mFragBStart = aConsumedBSize;
2893 // Copy the shared track state.
2894 // XXX consider temporarily swapping the array elements instead and swapping
2895 // XXX them back after we're done reflowing, for better performance.
2896 // XXX (bug 1252002)
2897 mCols = mSharedGridData->mCols;
2898 mRows = mSharedGridData->mRows;
2900 if (firstInFlow->GetProperty(UsedTrackSizes::Prop())) {
2901 auto* prop = aGridContainerFrame->GetProperty(UsedTrackSizes::Prop());
2902 if (!prop) {
2903 prop = new UsedTrackSizes();
2904 aGridContainerFrame->SetProperty(UsedTrackSizes::Prop(), prop);
2906 prop->mCanResolveLineRangeSize = {true, true};
2907 prop->mSizes[eLogicalAxisInline].Assign(mCols.mSizes);
2908 prop->mSizes[eLogicalAxisBlock].Assign(mRows.mSizes);
2911 // Copy item data from each child's first-in-flow data in mSharedGridData.
2912 // XXX NOTE: This is O(n^2) in the number of items. (bug 1252186)
2913 mIter.Reset();
2914 for (; !mIter.AtEnd(); mIter.Next()) {
2915 nsIFrame* child = *mIter;
2916 nsIFrame* childFirstInFlow = child->FirstInFlow();
2917 DebugOnly<size_t> len = mGridItems.Length();
2918 for (auto& itemInfo : mSharedGridData->mGridItems) {
2919 if (itemInfo.mFrame == childFirstInFlow) {
2920 auto item =
2921 mGridItems.AppendElement(GridItemInfo(child, itemInfo.mArea));
2922 // Copy the item's baseline data so that the item's last fragment can
2923 // do 'last baseline' alignment if necessary.
2924 item->mState[eLogicalAxisBlock] |=
2925 itemInfo.mState[eLogicalAxisBlock] & ItemState::eAllBaselineBits;
2926 item->mState[eLogicalAxisInline] |=
2927 itemInfo.mState[eLogicalAxisInline] & ItemState::eAllBaselineBits;
2928 item->mBaselineOffset[eLogicalAxisBlock] =
2929 itemInfo.mBaselineOffset[eLogicalAxisBlock];
2930 item->mBaselineOffset[eLogicalAxisInline] =
2931 itemInfo.mBaselineOffset[eLogicalAxisInline];
2932 item->mState[eLogicalAxisBlock] |=
2933 itemInfo.mState[eLogicalAxisBlock] & ItemState::eAutoPlacement;
2934 item->mState[eLogicalAxisInline] |=
2935 itemInfo.mState[eLogicalAxisInline] & ItemState::eAutoPlacement;
2936 break;
2939 MOZ_ASSERT(mGridItems.Length() == len + 1, "can't find GridItemInfo");
2942 // XXX NOTE: This is O(n^2) in the number of abs.pos. items. (bug 1252186)
2943 const nsFrameList& absPosChildren = aGridContainerFrame->GetChildList(
2944 aGridContainerFrame->GetAbsoluteListID());
2945 for (auto f : absPosChildren) {
2946 nsIFrame* childFirstInFlow = f->FirstInFlow();
2947 DebugOnly<size_t> len = mAbsPosItems.Length();
2948 for (auto& itemInfo : mSharedGridData->mAbsPosItems) {
2949 if (itemInfo.mFrame == childFirstInFlow) {
2950 mAbsPosItems.AppendElement(GridItemInfo(f, itemInfo.mArea));
2951 break;
2954 MOZ_ASSERT(mAbsPosItems.Length() == len + 1, "can't find GridItemInfo");
2957 // Copy in the computed grid info state bit
2958 if (mSharedGridData->mGenerateComputedGridInfo) {
2959 aGridContainerFrame->AddStateBits(NS_STATE_GRID_COMPUTED_INFO);
2964 * Calculate our track sizes in the given axis.
2966 void CalculateTrackSizesForAxis(LogicalAxis aAxis, const Grid& aGrid,
2967 nscoord aCBSize,
2968 SizingConstraint aConstraint);
2971 * Calculate our track sizes.
2973 void CalculateTrackSizes(const Grid& aGrid, const LogicalSize& aContentBox,
2974 SizingConstraint aConstraint);
2977 * Return the percentage basis for a grid item in its writing-mode.
2978 * If aAxis is eLogicalAxisInline then we return NS_UNCONSTRAINEDSIZE in
2979 * both axes since we know all track sizes are indefinite at this point
2980 * (we calculate column sizes before row sizes). Otherwise, assert that
2981 * column sizes are known and calculate the size for aGridItem.mArea.mCols
2982 * and use NS_UNCONSTRAINEDSIZE in the other axis.
2983 * @param aAxis the axis we're currently calculating track sizes for
2985 LogicalSize PercentageBasisFor(LogicalAxis aAxis,
2986 const GridItemInfo& aGridItem) const;
2989 * Return the containing block for a grid item occupying aArea.
2991 LogicalRect ContainingBlockFor(const GridArea& aArea) const;
2994 * Return the containing block for an abs.pos. grid item occupying aArea.
2995 * Any 'auto' lines in the grid area will be aligned with grid container
2996 * containing block on that side.
2997 * @param aGridOrigin the origin of the grid
2998 * @param aGridCB the grid container containing block (its padding area)
3000 LogicalRect ContainingBlockForAbsPos(const GridArea& aArea,
3001 const LogicalPoint& aGridOrigin,
3002 const LogicalRect& aGridCB) const;
3005 * Apply `align/justify-content` alignment in our masonry axis.
3006 * This aligns the "masonry box" within our content box size.
3008 void AlignJustifyContentInMasonryAxis(nscoord aMasonryBoxSize,
3009 nscoord aContentBoxSize);
3011 * Apply `align/justify-tracks` alignment in our masonry axis.
3013 void AlignJustifyTracksInMasonryAxis(const LogicalSize& aContentSize,
3014 const nsSize& aContainerSize);
3016 // Recursive helper for CollectSubgridItemsForAxis().
3017 static void CollectSubgridItemsForAxisHelper(
3018 LogicalAxis aAxis, WritingMode aContainerWM,
3019 const LineRange& aRangeInAxis, const LineRange& aRangeInOppositeAxis,
3020 const GridItemInfo& aItem, const nsTArray<GridItemInfo>& aItems,
3021 nsTArray<GridItemInfo>& aResult) {
3022 const auto oppositeAxis = GetOrthogonalAxis(aAxis);
3023 bool itemIsSubgridInOppositeAxis = aItem.IsSubgrid(oppositeAxis);
3024 auto subgridWM = aItem.mFrame->GetWritingMode();
3025 bool isOrthogonal = subgridWM.IsOrthogonalTo(aContainerWM);
3026 bool isSameDirInAxis =
3027 subgridWM.ParallelAxisStartsOnSameSide(aAxis, aContainerWM);
3028 bool isSameDirInOppositeAxis =
3029 subgridWM.ParallelAxisStartsOnSameSide(oppositeAxis, aContainerWM);
3030 if (isOrthogonal) {
3031 // We'll Transpose the area below so these needs to be transposed as well.
3032 std::swap(isSameDirInAxis, isSameDirInOppositeAxis);
3034 uint32_t offsetInAxis = aRangeInAxis.mStart;
3035 uint32_t gridEndInAxis = aRangeInAxis.Extent();
3036 uint32_t offsetInOppositeAxis = aRangeInOppositeAxis.mStart;
3037 uint32_t gridEndInOppositeAxis = aRangeInOppositeAxis.Extent();
3038 for (const auto& subgridItem : aItems) {
3039 auto newItem = aResult.AppendElement(
3040 isOrthogonal ? subgridItem.Transpose() : subgridItem);
3041 if (MOZ_UNLIKELY(!isSameDirInAxis)) {
3042 newItem->ReverseDirection(aAxis, gridEndInAxis);
3044 newItem->mArea.LineRangeForAxis(aAxis).Translate(offsetInAxis);
3045 if (itemIsSubgridInOppositeAxis) {
3046 if (MOZ_UNLIKELY(!isSameDirInOppositeAxis)) {
3047 newItem->ReverseDirection(oppositeAxis, gridEndInOppositeAxis);
3049 LineRange& range = newItem->mArea.LineRangeForAxis(oppositeAxis);
3050 range.Translate(offsetInOppositeAxis);
3052 if (newItem->IsSubgrid(aAxis)) {
3053 auto* subgrid =
3054 subgridItem.SubgridFrame()->GetProperty(Subgrid::Prop());
3055 CollectSubgridItemsForAxisHelper(
3056 aAxis, aContainerWM, newItem->mArea.LineRangeForAxis(aAxis),
3057 newItem->mArea.LineRangeForAxis(oppositeAxis), *newItem,
3058 subgrid->mGridItems, aResult);
3063 // Copy all descendant items from all our subgrid children that are subgridded
3064 // in aAxis recursively into aResult. All item grid area's and state are
3065 // translated to our coordinates.
3066 void CollectSubgridItemsForAxis(LogicalAxis aAxis,
3067 nsTArray<GridItemInfo>& aResult) const {
3068 for (const auto& item : mGridItems) {
3069 if (item.IsSubgrid(aAxis)) {
3070 const auto oppositeAxis = GetOrthogonalAxis(aAxis);
3071 auto* subgrid = item.SubgridFrame()->GetProperty(Subgrid::Prop());
3072 CollectSubgridItemsForAxisHelper(
3073 aAxis, mWM, item.mArea.LineRangeForAxis(aAxis),
3074 item.mArea.LineRangeForAxis(oppositeAxis), item,
3075 subgrid->mGridItems, aResult);
3081 * Recursive helper for CopyBaselineMetricsToSubgridItems().
3083 * @param aAxis The LogicalAxis for the axis whose baseline metrics we're
3084 * copying here (with respect to the outermost parent grid's
3085 * writing mode).
3086 * @param aContainerWM The writing mode of that outermost parent grid.
3087 * @param aSubgridFrame The subgrid whose subgrid-items we're considering
3088 * in this recursive traversal (whose items we're copying over
3089 * baseline-alignment metrics for).
3090 * @param aContainerGridItems The outermost parent grid's array of
3091 * GridItemInfo objects. (The final portion of this array is
3092 * all for subgrid items, and that's the portion that we're
3093 * recursively iterating over.)
3094 * @param aContainerGridItemsIdx [in/out] The index for the item that we're
3095 * currently considering in aContainerGridItemsIdx. When
3096 * this function returns, this will be the index just beyond the
3097 * last item that we handled here, i.e. the index of the next
3098 * item to be handled.
3100 static void CopyBaselineMetricsToSubgridItemsHelper(
3101 LogicalAxis aAxis, WritingMode aContainerWM, nsIFrame* aSubgridFrame,
3102 const nsTArray<GridItemInfo>& aContainerGridItems,
3103 size_t& aContainerGridItemsIdx) {
3104 // Get the canonical GridItemInfo structs for the grid items that live
3105 // inside of aSubgridFrame:
3106 Subgrid* subgridProp = aSubgridFrame->GetProperty(Subgrid::Prop());
3107 nsTArray<GridItemInfo>& subgridItems = subgridProp->mGridItems;
3109 // Use aSubgridFrame's writing-mode to determine subgridAxis.
3110 // Grids & subgrids store various data on a per-LogicalAxis basis, with
3111 // respect to their own WritingMode. Here, subgridAxis is aSubgridFrame's
3112 // axis that maps to the same physical axis that aAxis does for the
3113 // outermost parent grid.
3114 auto subgridWM = aSubgridFrame->GetWritingMode();
3115 bool isOrthogonal = subgridWM.IsOrthogonalTo(aContainerWM);
3116 LogicalAxis subgridAxis = isOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
3118 // Do a parallel walk through (1) subgridItems and (2) the portion of
3119 // aContainerGridItems that starts at offset aContainerGridItems,
3120 // descending to traverse child subgrids own items as we encounter them in
3121 // subgridItems. We expect to have an exact correspondence, because this
3122 // is precisely how we built up this portion of aContainerGridItems in
3123 // CollectSubgridItemsForAxis. (But if we happen to overstep the end of an
3124 // array, or find a GridItemInfo for a frame that we don't expect, we
3125 // gracefully bail out.)
3126 for (auto& subgridItem : subgridItems) {
3127 if (MOZ_UNLIKELY(aContainerGridItemsIdx >=
3128 aContainerGridItems.Length())) {
3129 // We failed to make the same traversal as CollectSubgridItemsForAxis;
3130 // whoops! This shouldn't happen; but if it does, we gracefully bail
3131 // out, instead of crashing.
3132 MOZ_ASSERT_UNREACHABLE("Out-of-bounds aContainerGridItemsIdx");
3133 return;
3135 const auto& itemFromContainer =
3136 aContainerGridItems[aContainerGridItemsIdx];
3137 aContainerGridItemsIdx++;
3139 if (MOZ_UNLIKELY(subgridItem.mFrame != itemFromContainer.mFrame)) {
3140 // We failed to make the same traversal as CollectSubgridItemsForAxis;
3141 // whoops! This shouldn't happen; but if it does, we gracefully bail
3142 // out, instead of copying baseline-alignment data for the wrong frame.
3143 MOZ_ASSERT_UNREACHABLE("Found unexpected frame during traversal");
3144 return;
3147 // This pattern of bits will be truthy if the item is baseline-aligned in
3148 // this axis (in which case the exact pattern of bits will have some
3149 // additional significance that doesn't matter here, but we do need to
3150 // copy it over).
3151 const auto baselineStateBits =
3152 itemFromContainer.mState[aAxis] & ItemState::eAllBaselineBits;
3154 if (subgridItem.IsSubgrid(subgridAxis)) {
3155 // This item is in fact a nested subgrid. It shouldn't itself be
3156 // baseline-aligned, but we need to make a recursive call to copy
3157 // baseline metrics to its items.
3158 MOZ_ASSERT(!baselineStateBits,
3159 "subgrids themselves can't be baseline-aligned "
3160 "(or self-aligned in any way) in their subgrid axis");
3161 CopyBaselineMetricsToSubgridItemsHelper(
3162 aAxis, aContainerWM, subgridItem.SubgridFrame(),
3163 aContainerGridItems, aContainerGridItemsIdx);
3164 } else if (baselineStateBits) {
3165 // This item is a baseline-aligned grid item (in the subgrid that we're
3166 // traversing). Copy over its baseline metrics.
3167 subgridItem.mState[subgridAxis] |= baselineStateBits;
3168 subgridItem.mBaselineOffset[subgridAxis] =
3169 itemFromContainer.mBaselineOffset[aAxis];
3175 * This function here is responsible for propagating baseline-alignment
3176 * metrics for subgrid-items from mGridItems over to the "canonical"
3177 * GridItemInfo structs for those grid items (which live on the subgrid that
3178 * owns them). The outermost parent grid *computes* those metrics as part of
3179 * doing track sizing, but it does this using *temporary* GridItemInfo
3180 * objects for any grid items that live in subgrids (aka subgrid items). So
3181 * that's why we need to rescue this baseline-alignment information before
3182 * those temporary objects are discarded.
3184 * (The temporary subgrid-items all live at the end of mGridItems; they were
3185 * appended there by CollectSubgridItemsForAxis(). So, it's important that
3186 * we perform the exact same traversal that CollectSubgridItemsForAxis() did,
3187 * in order to properly match up the temporary & canonical GridItemInfo
3188 * objects for these subgrid items.)
3190 // traversal that CollectSubgridItemsForAxis (and its recursive helper) does.
3191 void CopyBaselineMetricsToSubgridItems(LogicalAxis aAxis,
3192 size_t aOriginalLength) {
3193 MOZ_ASSERT(aOriginalLength <= mGridItems.Length(),
3194 "aOriginalLength is the length that mGridItems had *before* we "
3195 "appended temporary copies of subgrid items to it, so it's not "
3196 "possible for it to be more than the current length");
3198 // This index 'subgridItemIdx' traverses the final portion of mGridItems,
3199 // the portion that currently has temporary GridItemInfo structs that we
3200 // built for the items that live in our subgrids. (Our caller is about to
3201 // discard this temporary portion of mGridItems, and we're trying to
3202 // transfer some baseline-alignment data to the canonical GridItemInfo
3203 // structs before that happens.)
3205 // Our recursive helper updates subgridItemIdx internally. When this index
3206 // reaches mGridItems.Length(), we can stop looping; that means we've
3207 // finished copying out all the data from these temporary structs.
3208 size_t subgridItemIdx = aOriginalLength;
3210 for (size_t i = 0;
3211 (i < aOriginalLength && subgridItemIdx < mGridItems.Length()); i++) {
3212 const auto& item = mGridItems[i];
3213 if (item.IsSubgrid(aAxis)) {
3214 CopyBaselineMetricsToSubgridItemsHelper(aAxis, mWM, item.SubgridFrame(),
3215 mGridItems, subgridItemIdx);
3220 Tracks& TracksFor(LogicalAxis aAxis) {
3221 return aAxis == eLogicalAxisBlock ? mRows : mCols;
3223 const Tracks& TracksFor(LogicalAxis aAxis) const {
3224 return aAxis == eLogicalAxisBlock ? mRows : mCols;
3227 CSSOrderAwareFrameIterator mIter;
3228 const nsStylePosition* const mGridStyle;
3229 Tracks mCols;
3230 Tracks mRows;
3231 TrackSizingFunctions mColFunctions;
3232 TrackSizingFunctions mRowFunctions;
3234 * Info about each (normal flow) grid item.
3236 nsTArray<GridItemInfo> mGridItems;
3238 * Info about each grid-aligned abs.pos. child.
3240 nsTArray<GridItemInfo> mAbsPosItems;
3243 * @note mReflowInput may be null when using the 2nd ctor above. In this case
3244 * we'll construct a dummy parent reflow input if we need it to calculate
3245 * min/max-content contributions when sizing tracks.
3247 const ReflowInput* const mReflowInput;
3248 gfxContext& mRenderingContext;
3249 nsGridContainerFrame* const mFrame;
3250 SharedGridData* mSharedGridData; // [weak] owned by mFrame's first-in-flow.
3251 /** Computed border+padding with mSkipSides applied. */
3252 LogicalMargin mBorderPadding;
3254 * BStart of this fragment in "grid space" (i.e. the concatenation of content
3255 * areas of all fragments). Equal to mRows.mSizes[mStartRow].mPosition,
3256 * or, if this fragment starts after the last row, the ConsumedBSize().
3258 nscoord mFragBStart;
3259 /** The start row for this fragment. */
3260 uint32_t mStartRow;
3262 * The start row for the next fragment, if any. If mNextFragmentStartRow ==
3263 * mStartRow then there are no rows in this fragment.
3265 uint32_t mNextFragmentStartRow;
3266 /** Our tentative ApplySkipSides bits. */
3267 LogicalSides mSkipSides;
3268 const WritingMode mWM;
3269 /** Initialized lazily, when we find the fragmentainer. */
3270 bool mInFragmentainer;
3272 private:
3273 GridReflowInput(nsGridContainerFrame* aFrame, gfxContext& aRenderingContext,
3274 const ReflowInput* aReflowInput,
3275 const nsStylePosition* aGridStyle, const WritingMode& aWM)
3276 : mIter(aFrame, FrameChildListID::Principal),
3277 mGridStyle(aGridStyle),
3278 mCols(eLogicalAxisInline),
3279 mRows(eLogicalAxisBlock),
3280 mColFunctions(mGridStyle->mGridTemplateColumns,
3281 mGridStyle->mGridAutoColumns,
3282 aFrame->IsSubgrid(eLogicalAxisInline)),
3283 mRowFunctions(mGridStyle->mGridTemplateRows, mGridStyle->mGridAutoRows,
3284 aFrame->IsSubgrid(eLogicalAxisBlock)),
3285 mReflowInput(aReflowInput),
3286 mRenderingContext(aRenderingContext),
3287 mFrame(aFrame),
3288 mSharedGridData(nullptr),
3289 mBorderPadding(aWM),
3290 mFragBStart(0),
3291 mStartRow(0),
3292 mNextFragmentStartRow(0),
3293 mSkipSides(aFrame->GetWritingMode()),
3294 mWM(aWM),
3295 mInFragmentainer(false) {
3296 MOZ_ASSERT(!aReflowInput || aReflowInput->mFrame == mFrame);
3297 if (aReflowInput) {
3298 mBorderPadding = aReflowInput->ComputedLogicalBorderPadding(mWM);
3299 mSkipSides = aFrame->PreReflowBlockLevelLogicalSkipSides();
3300 mBorderPadding.ApplySkipSides(mSkipSides);
3302 mCols.mIsMasonry = aFrame->IsMasonry(eLogicalAxisInline);
3303 mRows.mIsMasonry = aFrame->IsMasonry(eLogicalAxisBlock);
3304 MOZ_ASSERT(!(mCols.mIsMasonry && mRows.mIsMasonry),
3305 "can't have masonry layout in both axes");
3309 using GridReflowInput = nsGridContainerFrame::GridReflowInput;
3312 * The Grid implements grid item placement and the state of the grid -
3313 * the size of the explicit/implicit grid, which cells are occupied etc.
3315 struct MOZ_STACK_CLASS nsGridContainerFrame::Grid {
3316 explicit Grid(const Grid* aParentGrid = nullptr) : mParentGrid(aParentGrid) {}
3319 * Place all child frames into the grid and expand the (implicit) grid as
3320 * needed. The allocated GridAreas are stored in the GridAreaProperty
3321 * frame property on the child frame.
3322 * @param aRepeatSizing the container's [min-|max-]*size - used to determine
3323 * the number of repeat(auto-fill/fit) tracks.
3325 void PlaceGridItems(GridReflowInput& aState,
3326 const RepeatTrackSizingInput& aRepeatSizing);
3328 void SubgridPlaceGridItems(GridReflowInput& aParentState, Grid* aParentGrid,
3329 const GridItemInfo& aGridItem);
3332 * As above but for an abs.pos. child. Any 'auto' lines will be represented
3333 * by kAutoLine in the LineRange result.
3334 * @param aGridStart the first line in the final, but untranslated grid
3335 * @param aGridEnd the last line in the final, but untranslated grid
3337 LineRange ResolveAbsPosLineRange(const StyleGridLine& aStart,
3338 const StyleGridLine& aEnd,
3339 const LineNameMap& aNameMap,
3340 LogicalAxis aAxis, uint32_t aExplicitGridEnd,
3341 int32_t aGridStart, int32_t aGridEnd,
3342 const nsStylePosition* aStyle);
3345 * Return a GridArea for abs.pos. item with non-auto lines placed at
3346 * a definite line (1-based) with placement errors resolved. One or both
3347 * positions may still be 'auto'.
3348 * @param aChild the abs.pos. grid item to place
3349 * @param aStyle the StylePosition() for the grid container
3351 GridArea PlaceAbsPos(nsIFrame* aChild, const LineNameMap& aColLineNameMap,
3352 const LineNameMap& aRowLineNameMap,
3353 const nsStylePosition* aStyle);
3356 * Find the first column in row aLockedRow starting at aStartCol where aArea
3357 * could be placed without overlapping other items. The returned column may
3358 * cause aArea to overflow the current implicit grid bounds if placed there.
3360 uint32_t FindAutoCol(uint32_t aStartCol, uint32_t aLockedRow,
3361 const GridArea* aArea) const;
3364 * Place aArea in the first column (in row aArea->mRows.mStart) starting at
3365 * aStartCol without overlapping other items. The resulting aArea may
3366 * overflow the current implicit grid bounds.
3367 * @param aClampMaxColLine the maximum allowed column line number (zero-based)
3368 * Pre-condition: aArea->mRows.IsDefinite() is true.
3369 * Post-condition: aArea->IsDefinite() is true.
3371 void PlaceAutoCol(uint32_t aStartCol, GridArea* aArea,
3372 uint32_t aClampMaxColLine) const;
3375 * Find the first row in column aLockedCol starting at aStartRow where aArea
3376 * could be placed without overlapping other items. The returned row may
3377 * cause aArea to overflow the current implicit grid bounds if placed there.
3379 uint32_t FindAutoRow(uint32_t aLockedCol, uint32_t aStartRow,
3380 const GridArea* aArea) const;
3383 * Place aArea in the first row (in column aArea->mCols.mStart) starting at
3384 * aStartRow without overlapping other items. The resulting aArea may
3385 * overflow the current implicit grid bounds.
3386 * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
3387 * Pre-condition: aArea->mCols.IsDefinite() is true.
3388 * Post-condition: aArea->IsDefinite() is true.
3390 void PlaceAutoRow(uint32_t aStartRow, GridArea* aArea,
3391 uint32_t aClampMaxRowLine) const;
3394 * Place aArea in the first column starting at aStartCol,aStartRow without
3395 * causing it to overlap other items or overflow mGridColEnd.
3396 * If there's no such column in aStartRow, continue in position 1,aStartRow+1.
3397 * @param aClampMaxColLine the maximum allowed column line number (zero-based)
3398 * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
3399 * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
3400 * Post-condition: aArea->IsDefinite() is true.
3402 void PlaceAutoAutoInRowOrder(uint32_t aStartCol, uint32_t aStartRow,
3403 GridArea* aArea, uint32_t aClampMaxColLine,
3404 uint32_t aClampMaxRowLine) const;
3407 * Place aArea in the first row starting at aStartCol,aStartRow without
3408 * causing it to overlap other items or overflow mGridRowEnd.
3409 * If there's no such row in aStartCol, continue in position aStartCol+1,1.
3410 * @param aClampMaxColLine the maximum allowed column line number (zero-based)
3411 * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
3412 * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
3413 * Post-condition: aArea->IsDefinite() is true.
3415 void PlaceAutoAutoInColOrder(uint32_t aStartCol, uint32_t aStartRow,
3416 GridArea* aArea, uint32_t aClampMaxColLine,
3417 uint32_t aClampMaxRowLine) const;
3420 * Return aLine if it's inside the aMin..aMax range (inclusive),
3421 * otherwise return kAutoLine.
3423 static int32_t AutoIfOutside(int32_t aLine, int32_t aMin, int32_t aMax) {
3424 MOZ_ASSERT(aMin <= aMax);
3425 if (aLine < aMin || aLine > aMax) {
3426 return kAutoLine;
3428 return aLine;
3432 * Inflate the implicit grid to include aArea.
3433 * @param aArea may be definite or auto
3435 void InflateGridFor(const GridArea& aArea) {
3436 mGridColEnd = std::max(mGridColEnd, aArea.mCols.HypotheticalEnd());
3437 mGridRowEnd = std::max(mGridRowEnd, aArea.mRows.HypotheticalEnd());
3438 MOZ_ASSERT(mGridColEnd <= kTranslatedMaxLine &&
3439 mGridRowEnd <= kTranslatedMaxLine);
3443 * Calculates the empty tracks in a repeat(auto-fit).
3444 * @param aOutNumEmptyLines Outputs the number of tracks which are empty.
3445 * @param aSizingFunctions Sizing functions for the relevant axis.
3446 * @param aNumGridLines Number of grid lines for the relevant axis.
3447 * @param aIsEmptyFunc Functor to check if a cell is empty. This should be
3448 * mCellMap.IsColEmpty or mCellMap.IsRowEmpty, depending on the axis.
3450 template <typename IsEmptyFuncT>
3451 static Maybe<nsTArray<uint32_t>> CalculateAdjustForAutoFitElements(
3452 uint32_t* aOutNumEmptyTracks, TrackSizingFunctions& aSizingFunctions,
3453 uint32_t aNumGridLines, IsEmptyFuncT aIsEmptyFunc);
3456 * Return a line number for (non-auto) aLine, per:
3457 * http://dev.w3.org/csswg/css-grid/#line-placement
3458 * @param aLine style data for the line (must be non-auto)
3459 * @param aNth a number of lines to find from aFromIndex, negative if the
3460 * search should be in reverse order. In the case aLine has
3461 * a specified line name, it's permitted to pass in zero which
3462 * will be treated as one.
3463 * @param aFromIndex the zero-based index to start counting from
3464 * @param aLineNameList the explicit named lines
3465 * @param aSide the axis+edge we're resolving names for (e.g. if we're
3466 resolving a grid-row-start line, pass eLogicalSideBStart)
3467 * @param aExplicitGridEnd the last line in the explicit grid
3468 * @param aStyle the StylePosition() for the grid container
3469 * @return a definite line (1-based), clamped to
3470 * the mClampMinLine..mClampMaxLine range
3472 int32_t ResolveLine(const StyleGridLine& aLine, int32_t aNth,
3473 uint32_t aFromIndex, const LineNameMap& aNameMap,
3474 LogicalSide aSide, uint32_t aExplicitGridEnd,
3475 const nsStylePosition* aStyle);
3478 * Helper method for ResolveLineRange.
3479 * @see ResolveLineRange
3480 * @return a pair (start,end) of lines
3482 typedef std::pair<int32_t, int32_t> LinePair;
3483 LinePair ResolveLineRangeHelper(const StyleGridLine& aStart,
3484 const StyleGridLine& aEnd,
3485 const LineNameMap& aNameMap,
3486 LogicalAxis aAxis, uint32_t aExplicitGridEnd,
3487 const nsStylePosition* aStyle);
3490 * Return a LineRange based on the given style data. Non-auto lines
3491 * are resolved to a definite line number (1-based) per:
3492 * http://dev.w3.org/csswg/css-grid/#line-placement
3493 * with placement errors corrected per:
3494 * http://dev.w3.org/csswg/css-grid/#grid-placement-errors
3495 * @param aStyle the StylePosition() for the grid container
3496 * @param aStart style data for the start line
3497 * @param aEnd style data for the end line
3498 * @param aLineNameList the explicit named lines
3499 * @param aAxis the axis we're resolving names in
3500 * @param aExplicitGridEnd the last line in the explicit grid
3501 * @param aStyle the StylePosition() for the grid container
3503 LineRange ResolveLineRange(const StyleGridLine& aStart,
3504 const StyleGridLine& aEnd,
3505 const LineNameMap& aNameMap, LogicalAxis aAxis,
3506 uint32_t aExplicitGridEnd,
3507 const nsStylePosition* aStyle);
3510 * Return a GridArea with non-auto lines placed at a definite line (1-based)
3511 * with placement errors resolved. One or both positions may still
3512 * be 'auto'.
3513 * @param aChild the grid item
3514 * @param aStyle the StylePosition() for the grid container
3516 GridArea PlaceDefinite(nsIFrame* aChild, const LineNameMap& aColLineNameMap,
3517 const LineNameMap& aRowLineNameMap,
3518 const nsStylePosition* aStyle);
3520 bool HasImplicitNamedArea(nsAtom* aName) const {
3521 return mAreas && mAreas->has(aName);
3524 // Return true if aString ends in aSuffix and has at least one character
3525 // before the suffix. Assign aIndex to where the suffix starts.
3526 static bool IsNameWithSuffix(nsAtom* aString, const nsString& aSuffix,
3527 uint32_t* aIndex) {
3528 if (StringEndsWith(nsDependentAtomString(aString), aSuffix)) {
3529 *aIndex = aString->GetLength() - aSuffix.Length();
3530 return *aIndex != 0;
3532 return false;
3535 static bool IsNameWithEndSuffix(nsAtom* aString, uint32_t* aIndex) {
3536 return IsNameWithSuffix(aString, u"-end"_ns, aIndex);
3539 static bool IsNameWithStartSuffix(nsAtom* aString, uint32_t* aIndex) {
3540 return IsNameWithSuffix(aString, u"-start"_ns, aIndex);
3543 // Return the relevant parent LineNameMap for the given subgrid axis aAxis.
3544 const LineNameMap* ParentLineMapForAxis(bool aIsOrthogonal,
3545 LogicalAxis aAxis) const {
3546 if (!mParentGrid) {
3547 return nullptr;
3549 bool isRows = aIsOrthogonal == (aAxis == eLogicalAxisInline);
3550 return isRows ? mParentGrid->mRowNameMap : mParentGrid->mColNameMap;
3553 void SetLineMaps(const LineNameMap* aColNameMap,
3554 const LineNameMap* aRowNameMap) {
3555 mColNameMap = aColNameMap;
3556 mRowNameMap = aRowNameMap;
3560 * A CellMap holds state for each cell in the grid.
3561 * It's row major. It's sparse in the sense that it only has enough rows to
3562 * cover the last row that has a grid item. Each row only has enough entries
3563 * to cover columns that are occupied *on that row*, i.e. it's not a full
3564 * matrix covering the entire implicit grid. An absent Cell means that it's
3565 * unoccupied by any grid item.
3567 struct CellMap {
3568 struct Cell {
3569 constexpr Cell() : mIsOccupied(false) {}
3570 bool mIsOccupied : 1;
3573 void Fill(const GridArea& aGridArea) {
3574 MOZ_ASSERT(aGridArea.IsDefinite());
3575 MOZ_ASSERT(aGridArea.mRows.mStart < aGridArea.mRows.mEnd);
3576 MOZ_ASSERT(aGridArea.mCols.mStart < aGridArea.mCols.mEnd);
3577 const auto numRows = aGridArea.mRows.mEnd;
3578 const auto numCols = aGridArea.mCols.mEnd;
3579 mCells.EnsureLengthAtLeast(numRows);
3580 for (auto i = aGridArea.mRows.mStart; i < numRows; ++i) {
3581 nsTArray<Cell>& cellsInRow = mCells[i];
3582 cellsInRow.EnsureLengthAtLeast(numCols);
3583 for (auto j = aGridArea.mCols.mStart; j < numCols; ++j) {
3584 cellsInRow[j].mIsOccupied = true;
3589 uint32_t IsEmptyCol(uint32_t aCol) const {
3590 for (auto& row : mCells) {
3591 if (aCol < row.Length() && row[aCol].mIsOccupied) {
3592 return false;
3595 return true;
3597 uint32_t IsEmptyRow(uint32_t aRow) const {
3598 if (aRow >= mCells.Length()) {
3599 return true;
3601 for (const Cell& cell : mCells[aRow]) {
3602 if (cell.mIsOccupied) {
3603 return false;
3606 return true;
3608 #ifdef DEBUG
3609 void Dump() const {
3610 const size_t numRows = mCells.Length();
3611 for (size_t i = 0; i < numRows; ++i) {
3612 const nsTArray<Cell>& cellsInRow = mCells[i];
3613 const size_t numCols = cellsInRow.Length();
3614 printf("%lu:\t", (unsigned long)i + 1);
3615 for (size_t j = 0; j < numCols; ++j) {
3616 printf(cellsInRow[j].mIsOccupied ? "X " : ". ");
3618 printf("\n");
3621 #endif
3623 nsTArray<nsTArray<Cell>> mCells;
3627 * State for each cell in the grid.
3629 CellMap mCellMap;
3631 * @see HasImplicitNamedArea.
3633 ImplicitNamedAreas* mAreas;
3635 * The last column grid line (1-based) in the explicit grid.
3636 * (i.e. the number of explicit columns + 1)
3638 uint32_t mExplicitGridColEnd;
3640 * The last row grid line (1-based) in the explicit grid.
3641 * (i.e. the number of explicit rows + 1)
3643 uint32_t mExplicitGridRowEnd;
3644 // Same for the implicit grid, except these become zero-based after
3645 // resolving definite lines.
3646 uint32_t mGridColEnd;
3647 uint32_t mGridRowEnd;
3650 * Offsets from the start of the implicit grid to the start of the translated
3651 * explicit grid. They are zero if there are no implicit lines before 1,1.
3652 * e.g. "grid-column: span 3 / 1" makes mExplicitGridOffsetCol = 3 and the
3653 * corresponding GridArea::mCols will be 0 / 3 in the zero-based translated
3654 * grid.
3656 uint32_t mExplicitGridOffsetCol;
3657 uint32_t mExplicitGridOffsetRow;
3660 * Our parent grid if any.
3662 const Grid* mParentGrid;
3665 * Our LineNameMaps.
3667 const LineNameMap* mColNameMap;
3668 const LineNameMap* mRowNameMap;
3672 * Compute margin+border+padding for aGridItem.mFrame (a subgrid) and store it
3673 * on its Subgrid property (and return that property).
3674 * aPercentageBasis is in the grid item's writing-mode.
3676 static Subgrid* SubgridComputeMarginBorderPadding(
3677 const GridItemInfo& aGridItem, const LogicalSize& aPercentageBasis) {
3678 auto* subgridFrame = aGridItem.SubgridFrame();
3679 auto cbWM = aGridItem.mFrame->GetParent()->GetWritingMode();
3680 auto* subgrid = subgridFrame->GetProperty(Subgrid::Prop());
3681 auto wm = subgridFrame->GetWritingMode();
3682 auto pmPercentageBasis = cbWM.IsOrthogonalTo(wm) ? aPercentageBasis.BSize(wm)
3683 : aPercentageBasis.ISize(wm);
3684 SizeComputationInput sz(subgridFrame, nullptr, cbWM, pmPercentageBasis);
3685 subgrid->mMarginBorderPadding =
3686 sz.ComputedLogicalMargin(cbWM) + sz.ComputedLogicalBorderPadding(cbWM);
3688 if (aGridItem.mFrame != subgridFrame) {
3689 nsHTMLScrollFrame* scrollFrame =
3690 do_QueryFrame(aGridItem.mFrame->GetScrollTargetFrame());
3691 if (scrollFrame) {
3692 MOZ_ASSERT(
3693 sz.ComputedLogicalMargin(cbWM) == LogicalMargin(cbWM) &&
3694 sz.ComputedLogicalBorder(cbWM) == LogicalMargin(cbWM),
3695 "A scrolled inner frame should not have any margin or border!");
3697 // Add the margin and border from the (outer) scroll frame.
3698 SizeComputationInput szScrollFrame(aGridItem.mFrame, nullptr, cbWM,
3699 pmPercentageBasis);
3700 subgrid->mMarginBorderPadding +=
3701 szScrollFrame.ComputedLogicalMargin(cbWM) +
3702 szScrollFrame.ComputedLogicalBorder(cbWM);
3704 nsMargin ssz = scrollFrame->IntrinsicScrollbarGutterSize();
3705 subgrid->mMarginBorderPadding += LogicalMargin(cbWM, ssz);
3708 if (aGridItem.mFrame->IsFieldSetFrame()) {
3709 const auto* f = static_cast<nsFieldSetFrame*>(aGridItem.mFrame);
3710 const auto* inner = f->GetInner();
3711 auto wm = inner->GetWritingMode();
3712 LogicalPoint pos = inner->GetLogicalPosition(aGridItem.mFrame->GetSize());
3713 // The legend is always on the BStart side and it inflates the fieldset's
3714 // "border area" size. The inner frame's b-start pos equals that size.
3715 LogicalMargin offsets(wm, pos.B(wm), 0, 0, 0);
3716 subgrid->mMarginBorderPadding += offsets.ConvertTo(cbWM, wm);
3719 return subgrid;
3722 static void CopyUsedTrackSizes(nsTArray<TrackSize>& aResult,
3723 const nsGridContainerFrame* aUsedTrackSizesFrame,
3724 const UsedTrackSizes* aUsedTrackSizes,
3725 const nsGridContainerFrame* aSubgridFrame,
3726 const Subgrid* aSubgrid,
3727 LogicalAxis aSubgridAxis) {
3728 MOZ_ASSERT(aSubgridFrame->ParentGridContainerForSubgrid() ==
3729 aUsedTrackSizesFrame);
3730 aResult.SetLength(aSubgridAxis == eLogicalAxisInline ? aSubgrid->mGridColEnd
3731 : aSubgrid->mGridRowEnd);
3732 auto parentAxis =
3733 aSubgrid->mIsOrthogonal ? GetOrthogonalAxis(aSubgridAxis) : aSubgridAxis;
3734 const auto& parentSizes = aUsedTrackSizes->mSizes[parentAxis];
3735 MOZ_ASSERT(aUsedTrackSizes->mCanResolveLineRangeSize[parentAxis]);
3736 if (parentSizes.IsEmpty()) {
3737 return;
3739 const auto& range = aSubgrid->mArea.LineRangeForAxis(parentAxis);
3740 const auto cbwm = aUsedTrackSizesFrame->GetWritingMode();
3741 const auto wm = aSubgridFrame->GetWritingMode();
3742 // Recompute the MBP to resolve percentages against the resolved track sizes.
3743 if (parentAxis == eLogicalAxisInline) {
3744 // Find the subgrid's grid item frame in its parent grid container. This
3745 // is usually the same as aSubgridFrame but it may also have a ScrollFrame,
3746 // FieldSetFrame etc. We just loop until we see the first ancestor
3747 // GridContainerFrame and pick the last frame we saw before that.
3748 // Note that all subgrids are inside a parent (sub)grid container.
3749 const nsIFrame* outerGridItemFrame = aSubgridFrame;
3750 for (nsIFrame* parent = aSubgridFrame->GetParent();
3751 parent != aUsedTrackSizesFrame; parent = parent->GetParent()) {
3752 MOZ_ASSERT(!parent->IsGridContainerFrame());
3753 outerGridItemFrame = parent;
3755 auto sizeInAxis = range.ToLength(aUsedTrackSizes->mSizes[parentAxis]);
3756 LogicalSize pmPercentageBasis =
3757 aSubgrid->mIsOrthogonal ? LogicalSize(wm, nscoord(0), sizeInAxis)
3758 : LogicalSize(wm, sizeInAxis, nscoord(0));
3759 GridItemInfo info(const_cast<nsIFrame*>(outerGridItemFrame),
3760 aSubgrid->mArea);
3761 SubgridComputeMarginBorderPadding(info, pmPercentageBasis);
3763 const LogicalMargin& mbp = aSubgrid->mMarginBorderPadding;
3764 nscoord startMBP;
3765 nscoord endMBP;
3766 if (MOZ_LIKELY(cbwm.ParallelAxisStartsOnSameSide(parentAxis, wm))) {
3767 startMBP = mbp.Start(parentAxis, cbwm);
3768 endMBP = mbp.End(parentAxis, cbwm);
3769 uint32_t i = range.mStart;
3770 nscoord startPos = parentSizes[i].mPosition + startMBP;
3771 for (auto& sz : aResult) {
3772 sz = parentSizes[i++];
3773 sz.mPosition -= startPos;
3775 } else {
3776 startMBP = mbp.End(parentAxis, cbwm);
3777 endMBP = mbp.Start(parentAxis, cbwm);
3778 uint32_t i = range.mEnd - 1;
3779 const auto& parentEnd = parentSizes[i];
3780 nscoord parentEndPos = parentEnd.mPosition + parentEnd.mBase - startMBP;
3781 for (auto& sz : aResult) {
3782 sz = parentSizes[i--];
3783 sz.mPosition = parentEndPos - (sz.mPosition + sz.mBase);
3786 auto& startTrack = aResult[0];
3787 startTrack.mPosition = 0;
3788 startTrack.mBase -= startMBP;
3789 if (MOZ_UNLIKELY(startTrack.mBase < nscoord(0))) {
3790 // Our MBP doesn't fit in the start track. Adjust the track position
3791 // to maintain track alignment with our parent.
3792 startTrack.mPosition = startTrack.mBase;
3793 startTrack.mBase = nscoord(0);
3795 auto& endTrack = aResult.LastElement();
3796 endTrack.mBase -= endMBP;
3797 if (MOZ_UNLIKELY(endTrack.mBase < nscoord(0))) {
3798 endTrack.mBase = nscoord(0);
3802 void nsGridContainerFrame::UsedTrackSizes::ResolveTrackSizesForAxis(
3803 nsGridContainerFrame* aFrame, LogicalAxis aAxis, gfxContext& aRC) {
3804 if (mCanResolveLineRangeSize[aAxis]) {
3805 return;
3807 if (!aFrame->IsSubgrid()) {
3808 // We can't resolve sizes in this axis at this point. aFrame is the top grid
3809 // container, which will store its final track sizes later once they're
3810 // resolved in this axis (in GridReflowInput::CalculateTrackSizesForAxis).
3811 // The single caller of this method only needs track sizes for
3812 // calculating a CB size and it will treat it as indefinite when
3813 // this happens.
3814 return;
3816 auto* parent = aFrame->ParentGridContainerForSubgrid();
3817 auto* parentSizes = parent->GetUsedTrackSizes();
3818 if (!parentSizes) {
3819 parentSizes = new UsedTrackSizes();
3820 parent->SetProperty(UsedTrackSizes::Prop(), parentSizes);
3822 auto* subgrid = aFrame->GetProperty(Subgrid::Prop());
3823 const auto parentAxis =
3824 subgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
3825 parentSizes->ResolveTrackSizesForAxis(parent, parentAxis, aRC);
3826 if (!parentSizes->mCanResolveLineRangeSize[parentAxis]) {
3827 if (aFrame->IsSubgrid(aAxis)) {
3828 ResolveSubgridTrackSizesForAxis(aFrame, aAxis, subgrid, aRC,
3829 NS_UNCONSTRAINEDSIZE);
3831 return;
3833 if (aFrame->IsSubgrid(aAxis)) {
3834 CopyUsedTrackSizes(mSizes[aAxis], parent, parentSizes, aFrame, subgrid,
3835 aAxis);
3836 mCanResolveLineRangeSize[aAxis] = true;
3837 } else {
3838 const auto& range = subgrid->mArea.LineRangeForAxis(parentAxis);
3839 nscoord contentBoxSize = range.ToLength(parentSizes->mSizes[parentAxis]);
3840 auto parentWM = aFrame->GetParent()->GetWritingMode();
3841 contentBoxSize -=
3842 subgrid->mMarginBorderPadding.StartEnd(parentAxis, parentWM);
3843 contentBoxSize = std::max(nscoord(0), contentBoxSize);
3844 ResolveSubgridTrackSizesForAxis(aFrame, aAxis, subgrid, aRC,
3845 contentBoxSize);
3849 void nsGridContainerFrame::UsedTrackSizes::ResolveSubgridTrackSizesForAxis(
3850 nsGridContainerFrame* aFrame, LogicalAxis aAxis, Subgrid* aSubgrid,
3851 gfxContext& aRC, nscoord aContentBoxSize) {
3852 GridReflowInput state(aFrame, aRC);
3853 state.mGridItems = aSubgrid->mGridItems.Clone();
3854 Grid grid;
3855 grid.mGridColEnd = aSubgrid->mGridColEnd;
3856 grid.mGridRowEnd = aSubgrid->mGridRowEnd;
3857 state.CalculateTrackSizesForAxis(aAxis, grid, aContentBoxSize,
3858 SizingConstraint::NoConstraint);
3859 const auto& tracks = aAxis == eLogicalAxisInline ? state.mCols : state.mRows;
3860 mSizes[aAxis].Assign(tracks.mSizes);
3861 mCanResolveLineRangeSize[aAxis] = tracks.mCanResolveLineRangeSize;
3862 MOZ_ASSERT(mCanResolveLineRangeSize[aAxis]);
3865 void nsGridContainerFrame::GridReflowInput::CalculateTrackSizesForAxis(
3866 LogicalAxis aAxis, const Grid& aGrid, nscoord aContentBoxSize,
3867 SizingConstraint aConstraint) {
3868 auto& tracks = aAxis == eLogicalAxisInline ? mCols : mRows;
3869 const auto& sizingFunctions =
3870 aAxis == eLogicalAxisInline ? mColFunctions : mRowFunctions;
3871 const auto& gapStyle = aAxis == eLogicalAxisInline ? mGridStyle->mColumnGap
3872 : mGridStyle->mRowGap;
3873 if (tracks.mIsMasonry) {
3874 // See comment on nsGridContainerFrame::MasonryLayout().
3875 tracks.Initialize(sizingFunctions, gapStyle, 2, aContentBoxSize);
3876 tracks.mCanResolveLineRangeSize = true;
3877 return;
3879 uint32_t gridEnd =
3880 aAxis == eLogicalAxisInline ? aGrid.mGridColEnd : aGrid.mGridRowEnd;
3881 Maybe<TrackSizingFunctions> fallbackTrackSizing;
3883 bool useParentGaps = false;
3884 const bool isSubgriddedAxis = mFrame->IsSubgrid(aAxis);
3885 if (MOZ_LIKELY(!isSubgriddedAxis)) {
3886 tracks.Initialize(sizingFunctions, gapStyle, gridEnd, aContentBoxSize);
3887 } else {
3888 tracks.mGridGap =
3889 nsLayoutUtils::ResolveGapToLength(gapStyle, aContentBoxSize);
3890 tracks.mContentBoxSize = aContentBoxSize;
3891 const auto* subgrid = mFrame->GetProperty(Subgrid::Prop());
3892 tracks.mSizes.SetLength(gridEnd);
3893 auto* parent = mFrame->ParentGridContainerForSubgrid();
3894 auto parentAxis = subgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
3895 const auto* parentSizes = parent->GetUsedTrackSizes();
3896 if (parentSizes && parentSizes->mCanResolveLineRangeSize[parentAxis]) {
3897 CopyUsedTrackSizes(tracks.mSizes, parent, parentSizes, mFrame, subgrid,
3898 aAxis);
3899 useParentGaps = gapStyle.IsNormal();
3900 } else {
3901 fallbackTrackSizing.emplace(TrackSizingFunctions::ForSubgridFallback(
3902 mFrame, subgrid, parent, parentAxis));
3903 tracks.Initialize(*fallbackTrackSizing, gapStyle, gridEnd,
3904 aContentBoxSize);
3908 // We run the Track Sizing Algorithm in non-subgridded axes, and in some
3909 // cases in a subgridded axis when our parent track sizes aren't resolved yet.
3910 if (MOZ_LIKELY(!isSubgriddedAxis) || fallbackTrackSizing.isSome()) {
3911 const size_t origGridItemCount = mGridItems.Length();
3912 const bool hasSubgridItems = mFrame->HasSubgridItems(aAxis);
3913 if (hasSubgridItems) {
3914 AutoTArray<GridItemInfo, 8> collectedItems;
3915 CollectSubgridItemsForAxis(aAxis, collectedItems);
3916 mGridItems.AppendElements(collectedItems);
3918 tracks.CalculateSizes(
3919 *this, mGridItems,
3920 fallbackTrackSizing ? *fallbackTrackSizing : sizingFunctions,
3921 aContentBoxSize,
3922 aAxis == eLogicalAxisInline ? &GridArea::mCols : &GridArea::mRows,
3923 aConstraint);
3925 if (hasSubgridItems &&
3926 StaticPrefs::layout_css_grid_subgrid_baselines_enabled()) {
3927 // If any of the subgrid items are baseline-aligned, we've just recorded
3928 // their baseline-alignment offsets in our own copy of their GridItemInfo
3929 // structs. Before we get rid of those copies (via TruncateLength), we
3930 // have to copy these offsets back to the subgrids' versions of the
3931 // GridItemInfo structs.
3933 // XXXdholbert This new behavior is behind a pref due to bug 1871719.
3934 CopyBaselineMetricsToSubgridItems(aAxis, origGridItemCount);
3936 mGridItems.TruncateLength(origGridItemCount);
3938 if (isSubgriddedAxis) {
3939 // XXXdholbert This is a bit hacky, but this is something that
3940 // tracks.CalculateSizes does internally (unconditionally, if there are
3941 // baseline-aligned items), and it seems like subgrids need to do it too,
3942 // or else they hit the "unexpected baseline subtree alignment"
3943 // fatal-assert when aligning their children with the baseline-alignment
3944 // information that they received from the outer grid.
3945 // (This might be entirely unnecessary? Aside from the default ::AUTO
3946 // value, it looks like the ::First entry is always set to ::START and
3947 // the ::Last entry is always set to ::END...)
3948 tracks.mBaselineSubtreeAlign[BaselineSharingGroup::First] =
3949 StyleAlignFlags::START;
3950 tracks.mBaselineSubtreeAlign[BaselineSharingGroup::Last] =
3951 StyleAlignFlags::END;
3954 if (aContentBoxSize != NS_UNCONSTRAINEDSIZE) {
3955 auto alignment = mGridStyle->UsedContentAlignment(tracks.mAxis);
3956 tracks.AlignJustifyContent(mGridStyle, alignment, mWM, aContentBoxSize,
3957 isSubgriddedAxis);
3958 } else if (!useParentGaps) {
3959 const nscoord gridGap = tracks.mGridGap;
3960 nscoord pos = 0;
3961 for (TrackSize& sz : tracks.mSizes) {
3962 sz.mPosition = pos;
3963 pos += sz.mBase + gridGap;
3967 if (aConstraint == SizingConstraint::NoConstraint &&
3968 (mFrame->HasSubgridItems() || mFrame->IsSubgrid())) {
3969 mFrame->StoreUsedTrackSizes(aAxis, tracks.mSizes);
3972 // positions and sizes are now final
3973 tracks.mCanResolveLineRangeSize = true;
3976 void nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
3977 const Grid& aGrid, const LogicalSize& aContentBox,
3978 SizingConstraint aConstraint) {
3979 CalculateTrackSizesForAxis(eLogicalAxisInline, aGrid, aContentBox.ISize(mWM),
3980 aConstraint);
3981 CalculateTrackSizesForAxis(eLogicalAxisBlock, aGrid, aContentBox.BSize(mWM),
3982 aConstraint);
3985 // Align an item's margin box in its aAxis inside aCBSize.
3986 static void AlignJustifySelf(StyleAlignFlags aAlignment, LogicalAxis aAxis,
3987 AlignJustifyFlags aFlags, nscoord aBaselineAdjust,
3988 nscoord aCBSize, const ReflowInput& aRI,
3989 const LogicalSize& aChildSize,
3990 LogicalPoint* aPos) {
3991 MOZ_ASSERT(aAlignment != StyleAlignFlags::AUTO,
3992 "unexpected 'auto' "
3993 "computed value for normal flow grid item");
3995 // NOTE: this is the resulting frame offset (border box).
3996 nscoord offset = CSSAlignUtils::AlignJustifySelf(
3997 aAlignment, aAxis, aFlags, aBaselineAdjust, aCBSize, aRI, aChildSize);
3999 // Set the position (aPos) for the requested alignment.
4000 if (offset != 0) {
4001 WritingMode wm = aRI.GetWritingMode();
4002 nscoord& pos = aAxis == eLogicalAxisBlock ? aPos->B(wm) : aPos->I(wm);
4003 pos += MOZ_LIKELY(aFlags & AlignJustifyFlags::SameSide) ? offset : -offset;
4007 static void AlignSelf(const nsGridContainerFrame::GridItemInfo& aGridItem,
4008 StyleAlignFlags aAlignSelf, nscoord aCBSize,
4009 const WritingMode aCBWM, const ReflowInput& aRI,
4010 const LogicalSize& aSize, AlignJustifyFlags aFlags,
4011 LogicalPoint* aPos) {
4012 AlignJustifyFlags flags = aFlags;
4013 if (aAlignSelf & StyleAlignFlags::SAFE) {
4014 flags |= AlignJustifyFlags::OverflowSafe;
4016 aAlignSelf &= ~StyleAlignFlags::FLAG_BITS;
4018 WritingMode childWM = aRI.GetWritingMode();
4019 if (aCBWM.ParallelAxisStartsOnSameSide(eLogicalAxisBlock, childWM)) {
4020 flags |= AlignJustifyFlags::SameSide;
4023 // Grid's 'align-self' axis is never parallel to the container's inline axis.
4024 if (aAlignSelf == StyleAlignFlags::LEFT ||
4025 aAlignSelf == StyleAlignFlags::RIGHT) {
4026 aAlignSelf = StyleAlignFlags::START;
4028 if (MOZ_LIKELY(aAlignSelf == StyleAlignFlags::NORMAL)) {
4029 aAlignSelf = StyleAlignFlags::STRETCH;
4032 nscoord baselineAdjust = 0;
4033 if (aAlignSelf == StyleAlignFlags::BASELINE ||
4034 aAlignSelf == StyleAlignFlags::LAST_BASELINE) {
4035 aAlignSelf = aGridItem.GetSelfBaseline(aAlignSelf, eLogicalAxisBlock,
4036 &baselineAdjust);
4039 bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
4040 LogicalAxis axis = isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock;
4041 AlignJustifySelf(aAlignSelf, axis, flags, baselineAdjust, aCBSize, aRI, aSize,
4042 aPos);
4045 static void JustifySelf(const nsGridContainerFrame::GridItemInfo& aGridItem,
4046 StyleAlignFlags aJustifySelf, nscoord aCBSize,
4047 const WritingMode aCBWM, const ReflowInput& aRI,
4048 const LogicalSize& aSize, AlignJustifyFlags aFlags,
4049 LogicalPoint* aPos) {
4050 AlignJustifyFlags flags = aFlags;
4051 if (aJustifySelf & StyleAlignFlags::SAFE) {
4052 flags |= AlignJustifyFlags::OverflowSafe;
4054 aJustifySelf &= ~StyleAlignFlags::FLAG_BITS;
4056 WritingMode childWM = aRI.GetWritingMode();
4057 if (aCBWM.ParallelAxisStartsOnSameSide(eLogicalAxisInline, childWM)) {
4058 flags |= AlignJustifyFlags::SameSide;
4061 if (MOZ_LIKELY(aJustifySelf == StyleAlignFlags::NORMAL)) {
4062 aJustifySelf = StyleAlignFlags::STRETCH;
4065 nscoord baselineAdjust = 0;
4066 // Grid's 'justify-self' axis is always parallel to the container's inline
4067 // axis, so justify-self:left|right always applies.
4068 if (aJustifySelf == StyleAlignFlags::LEFT) {
4069 aJustifySelf =
4070 aCBWM.IsBidiLTR() ? StyleAlignFlags::START : StyleAlignFlags::END;
4071 } else if (aJustifySelf == StyleAlignFlags::RIGHT) {
4072 aJustifySelf =
4073 aCBWM.IsBidiLTR() ? StyleAlignFlags::END : StyleAlignFlags::START;
4074 } else if (aJustifySelf == StyleAlignFlags::BASELINE ||
4075 aJustifySelf == StyleAlignFlags::LAST_BASELINE) {
4076 aJustifySelf = aGridItem.GetSelfBaseline(aJustifySelf, eLogicalAxisInline,
4077 &baselineAdjust);
4080 bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
4081 LogicalAxis axis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
4082 AlignJustifySelf(aJustifySelf, axis, flags, baselineAdjust, aCBSize, aRI,
4083 aSize, aPos);
4086 static StyleAlignFlags GetAlignJustifyValue(StyleAlignFlags aAlignment,
4087 const WritingMode aWM,
4088 const bool aIsAlign,
4089 bool* aOverflowSafe) {
4090 *aOverflowSafe = bool(aAlignment & StyleAlignFlags::SAFE);
4091 aAlignment &= ~StyleAlignFlags::FLAG_BITS;
4093 // Map some alignment values to 'start' / 'end'.
4094 if (aAlignment == StyleAlignFlags::LEFT ||
4095 aAlignment == StyleAlignFlags::RIGHT) {
4096 if (aIsAlign) {
4097 // Grid's 'align-content' axis is never parallel to the inline axis.
4098 return StyleAlignFlags::START;
4100 bool isStart = aWM.IsBidiLTR() == (aAlignment == StyleAlignFlags::LEFT);
4101 return isStart ? StyleAlignFlags::START : StyleAlignFlags::END;
4103 if (aAlignment == StyleAlignFlags::FLEX_START) {
4104 return StyleAlignFlags::START; // same as 'start' for Grid
4106 if (aAlignment == StyleAlignFlags::FLEX_END) {
4107 return StyleAlignFlags::END; // same as 'end' for Grid
4109 return aAlignment;
4112 static Maybe<StyleAlignFlags> GetAlignJustifyFallbackIfAny(
4113 const StyleContentDistribution& aDistribution, const WritingMode aWM,
4114 const bool aIsAlign, bool* aOverflowSafe) {
4115 // TODO: Eventually this should look at aDistribution's fallback alignment,
4116 // see https://github.com/w3c/csswg-drafts/issues/1002.
4117 if (aDistribution.primary == StyleAlignFlags::STRETCH ||
4118 aDistribution.primary == StyleAlignFlags::SPACE_BETWEEN) {
4119 return Some(StyleAlignFlags::START);
4121 if (aDistribution.primary == StyleAlignFlags::SPACE_AROUND ||
4122 aDistribution.primary == StyleAlignFlags::SPACE_EVENLY) {
4123 return Some(StyleAlignFlags::CENTER);
4125 return Nothing();
4128 //----------------------------------------------------------------------
4130 // Frame class boilerplate
4131 // =======================
4133 NS_QUERYFRAME_HEAD(nsGridContainerFrame)
4134 NS_QUERYFRAME_ENTRY(nsGridContainerFrame)
4135 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
4137 NS_IMPL_FRAMEARENA_HELPERS(nsGridContainerFrame)
4139 nsContainerFrame* NS_NewGridContainerFrame(PresShell* aPresShell,
4140 ComputedStyle* aStyle) {
4141 return new (aPresShell)
4142 nsGridContainerFrame(aStyle, aPresShell->GetPresContext());
4145 //----------------------------------------------------------------------
4147 // nsGridContainerFrame Method Implementations
4148 // ===========================================
4150 /*static*/ const nsRect& nsGridContainerFrame::GridItemCB(nsIFrame* aChild) {
4151 MOZ_ASSERT(aChild->IsAbsolutelyPositioned());
4152 nsRect* cb = aChild->GetProperty(GridItemContainingBlockRect());
4153 MOZ_ASSERT(cb,
4154 "this method must only be called on grid items, and the grid "
4155 "container should've reflowed this item by now and set up cb");
4156 return *cb;
4159 void nsGridContainerFrame::AddImplicitNamedAreasInternal(
4160 LineNameList& aNameList,
4161 nsGridContainerFrame::ImplicitNamedAreas*& aAreas) {
4162 for (const auto& nameIdent : aNameList.AsSpan()) {
4163 nsAtom* name = nameIdent.AsAtom();
4164 uint32_t indexOfSuffix;
4165 if (Grid::IsNameWithStartSuffix(name, &indexOfSuffix) ||
4166 Grid::IsNameWithEndSuffix(name, &indexOfSuffix)) {
4167 // Extract the name that was found earlier.
4168 nsDependentSubstring areaName(nsDependentAtomString(name), 0,
4169 indexOfSuffix);
4171 // Lazily create the ImplicitNamedAreas.
4172 if (!aAreas) {
4173 aAreas = new nsGridContainerFrame::ImplicitNamedAreas;
4174 SetProperty(nsGridContainerFrame::ImplicitNamedAreasProperty(), aAreas);
4177 RefPtr<nsAtom> name = NS_Atomize(areaName);
4178 auto addPtr = aAreas->lookupForAdd(name);
4179 if (!addPtr) {
4180 if (!aAreas->add(addPtr, name,
4181 nsGridContainerFrame::NamedArea{
4182 StyleAtom(do_AddRef(name)), {0, 0}, {0, 0}})) {
4183 MOZ_CRASH("OOM while adding grid name lists");
4190 void nsGridContainerFrame::AddImplicitNamedAreas(
4191 Span<LineNameList> aLineNameLists) {
4192 // http://dev.w3.org/csswg/css-grid/#implicit-named-areas
4193 // Note: recording these names for fast lookup later is just an optimization.
4194 ImplicitNamedAreas* areas = GetImplicitNamedAreas();
4195 const uint32_t len = std::min(aLineNameLists.Length(), size_t(kMaxLine));
4196 for (uint32_t i = 0; i < len; ++i) {
4197 AddImplicitNamedAreasInternal(aLineNameLists[i], areas);
4201 void nsGridContainerFrame::AddImplicitNamedAreas(
4202 Span<StyleLineNameListValue> aLineNameList) {
4203 // http://dev.w3.org/csswg/css-grid/#implicit-named-areas
4204 // Note: recording these names for fast lookup later is just an optimization.
4205 uint32_t count = 0;
4206 ImplicitNamedAreas* areas = GetImplicitNamedAreas();
4207 for (const auto& nameList : aLineNameList) {
4208 if (nameList.IsRepeat()) {
4209 for (const auto& repeatNameList :
4210 nameList.AsRepeat().line_names.AsSpan()) {
4211 AddImplicitNamedAreasInternal(repeatNameList, areas);
4212 ++count;
4214 } else {
4215 MOZ_ASSERT(nameList.IsLineNames());
4216 AddImplicitNamedAreasInternal(nameList.AsLineNames(), areas);
4217 ++count;
4220 if (count >= size_t(kMaxLine)) {
4221 break;
4226 void nsGridContainerFrame::InitImplicitNamedAreas(
4227 const nsStylePosition* aStyle) {
4228 ImplicitNamedAreas* areas = GetImplicitNamedAreas();
4229 if (areas) {
4230 // Clear it, but reuse the hashtable itself for now. We'll remove it
4231 // below if it isn't needed anymore.
4232 areas->clear();
4234 auto Add = [&](const GridTemplate& aTemplate, bool aIsSubgrid) {
4235 AddImplicitNamedAreas(aTemplate.LineNameLists(aIsSubgrid));
4236 for (auto& value : aTemplate.TrackListValues()) {
4237 if (value.IsTrackRepeat()) {
4238 AddImplicitNamedAreas(value.AsTrackRepeat().line_names.AsSpan());
4242 if (aIsSubgrid && aTemplate.IsSubgrid()) {
4243 // For subgrid, |aTemplate.LineNameLists(aIsSubgrid)| returns an empty
4244 // list so we have to manually add each item.
4245 AddImplicitNamedAreas(aTemplate.AsSubgrid()->line_names.AsSpan());
4248 Add(aStyle->mGridTemplateColumns, IsSubgrid(eLogicalAxisInline));
4249 Add(aStyle->mGridTemplateRows, IsSubgrid(eLogicalAxisBlock));
4250 if (areas && areas->count() == 0) {
4251 RemoveProperty(ImplicitNamedAreasProperty());
4255 int32_t nsGridContainerFrame::Grid::ResolveLine(
4256 const StyleGridLine& aLine, int32_t aNth, uint32_t aFromIndex,
4257 const LineNameMap& aNameMap, LogicalSide aSide, uint32_t aExplicitGridEnd,
4258 const nsStylePosition* aStyle) {
4259 MOZ_ASSERT(!aLine.IsAuto());
4260 int32_t line = 0;
4261 if (aLine.LineName()->IsEmpty()) {
4262 MOZ_ASSERT(aNth != 0, "css-grid 9.2: <integer> must not be zero.");
4263 line = int32_t(aFromIndex) + aNth;
4264 } else {
4265 if (aNth == 0) {
4266 // <integer> was omitted; treat it as 1.
4267 aNth = 1;
4269 bool isNameOnly = !aLine.is_span && aLine.line_num == 0;
4270 if (isNameOnly) {
4271 AutoTArray<uint32_t, 16> implicitLines;
4272 aNameMap.FindNamedAreas(aLine.ident.AsAtom(), aSide, implicitLines);
4273 if (!implicitLines.IsEmpty() ||
4274 aNameMap.HasImplicitNamedArea(aLine.LineName())) {
4275 // aName is a named area - look for explicit lines named
4276 // <name>-start/-end depending on which side we're resolving.
4277 // http://dev.w3.org/csswg/css-grid/#grid-placement-slot
4278 nsAutoString lineName(nsDependentAtomString(aLine.LineName()));
4279 if (IsStart(aSide)) {
4280 lineName.AppendLiteral("-start");
4281 } else {
4282 lineName.AppendLiteral("-end");
4284 RefPtr<nsAtom> name = NS_Atomize(lineName);
4285 line = aNameMap.FindNamedLine(name, &aNth, aFromIndex, implicitLines);
4289 if (line == 0) {
4290 // If LineName() ends in -start/-end, try the prefix as a named area.
4291 AutoTArray<uint32_t, 16> implicitLines;
4292 uint32_t index;
4293 bool useStart = IsNameWithStartSuffix(aLine.LineName(), &index);
4294 if (useStart || IsNameWithEndSuffix(aLine.LineName(), &index)) {
4295 auto side = MakeLogicalSide(
4296 GetAxis(aSide), useStart ? eLogicalEdgeStart : eLogicalEdgeEnd);
4297 RefPtr<nsAtom> name = NS_Atomize(nsDependentSubstring(
4298 nsDependentAtomString(aLine.LineName()), 0, index));
4299 aNameMap.FindNamedAreas(name, side, implicitLines);
4301 line = aNameMap.FindNamedLine(aLine.LineName(), &aNth, aFromIndex,
4302 implicitLines);
4305 if (line == 0) {
4306 MOZ_ASSERT(aNth != 0, "we found all N named lines but 'line' is zero!");
4307 int32_t edgeLine;
4308 if (aLine.is_span) {
4309 // http://dev.w3.org/csswg/css-grid/#grid-placement-span-int
4310 // 'span <custom-ident> N'
4311 edgeLine = IsStart(aSide) ? 1 : aExplicitGridEnd;
4312 } else {
4313 // http://dev.w3.org/csswg/css-grid/#grid-placement-int
4314 // '<custom-ident> N'
4315 edgeLine = aNth < 0 ? 1 : aExplicitGridEnd;
4317 // "If not enough lines with that name exist, all lines in the implicit
4318 // grid are assumed to have that name..."
4319 line = edgeLine + aNth;
4322 // Note: at this point, 'line' might be outside of aNameMap's allowed range,
4323 // [mClampMinLin, mClampMaxLine]. This is fine; we'll clamp once we've
4324 // resolved *both* the start and end line -- in particular, we clamp in
4325 // ResolveLineRange(). If we clamped here, it'd be premature -- if one line
4326 // is definite and the other is specified as a span to some named line
4327 // (i.e. we need to perform a name-search that starts from the definite
4328 // line), then it matters whether we clamp the definite line before or after
4329 // that search. See https://bugzilla.mozilla.org/show_bug.cgi?id=1800566#c6
4330 // for more.
4331 return line;
4334 nsGridContainerFrame::Grid::LinePair
4335 nsGridContainerFrame::Grid::ResolveLineRangeHelper(
4336 const StyleGridLine& aStart, const StyleGridLine& aEnd,
4337 const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
4338 const nsStylePosition* aStyle) {
4339 MOZ_ASSERT(int32_t(kAutoLine) > kMaxLine);
4341 if (aStart.is_span) {
4342 if (aEnd.is_span || aEnd.IsAuto()) {
4343 // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
4344 if (aStart.LineName()->IsEmpty()) {
4345 // span <integer> / span *
4346 // span <integer> / auto
4347 return LinePair(kAutoLine, aStart.line_num);
4349 // span <custom-ident> / span *
4350 // span <custom-ident> / auto
4351 return LinePair(kAutoLine, 1); // XXX subgrid explicit size instead of 1?
4354 uint32_t from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0;
4355 auto end = ResolveLine(aEnd, aEnd.line_num, from, aNameMap,
4356 MakeLogicalSide(aAxis, eLogicalEdgeEnd),
4357 aExplicitGridEnd, aStyle);
4358 int32_t span = aStart.line_num == 0 ? 1 : aStart.line_num;
4359 if (end <= 1) {
4360 // The end is at or before the first explicit line, thus all lines before
4361 // it match <custom-ident> since they're implicit.
4362 int32_t start = std::max(end - span, aNameMap.mClampMinLine);
4363 return LinePair(start, end);
4365 auto start = ResolveLine(aStart, -span, end, aNameMap,
4366 MakeLogicalSide(aAxis, eLogicalEdgeStart),
4367 aExplicitGridEnd, aStyle);
4368 return LinePair(start, end);
4371 int32_t start = kAutoLine;
4372 if (aStart.IsAuto()) {
4373 if (aEnd.IsAuto()) {
4374 // auto / auto
4375 return LinePair(start, 1); // XXX subgrid explicit size instead of 1?
4377 if (aEnd.is_span) {
4378 if (aEnd.LineName()->IsEmpty()) {
4379 // auto / span <integer>
4380 MOZ_ASSERT(aEnd.line_num != 0);
4381 return LinePair(start, aEnd.line_num);
4383 // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
4384 // auto / span <custom-ident>
4385 return LinePair(start, 1); // XXX subgrid explicit size instead of 1?
4387 } else {
4388 uint32_t from = aStart.line_num < 0 ? aExplicitGridEnd + 1 : 0;
4389 start = ResolveLine(aStart, aStart.line_num, from, aNameMap,
4390 MakeLogicalSide(aAxis, eLogicalEdgeStart),
4391 aExplicitGridEnd, aStyle);
4392 if (aEnd.IsAuto()) {
4393 // A "definite line / auto" should resolve the auto to 'span 1'.
4394 // The error handling in ResolveLineRange will make that happen and also
4395 // clamp the end line correctly if we return "start / start".
4396 return LinePair(start, start);
4400 uint32_t from;
4401 int32_t nth = aEnd.line_num == 0 ? 1 : aEnd.line_num;
4402 if (aEnd.is_span) {
4403 if (MOZ_UNLIKELY(start < 0)) {
4404 if (aEnd.LineName()->IsEmpty()) {
4405 return LinePair(start, start + nth);
4407 from = 0;
4408 } else {
4409 if (start >= int32_t(aExplicitGridEnd)) {
4410 // The start is at or after the last explicit line, thus all lines
4411 // after it match <custom-ident> since they're implicit.
4412 return LinePair(start, std::min(start + nth, aNameMap.mClampMaxLine));
4414 from = start;
4416 } else {
4417 from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0;
4419 auto end = ResolveLine(aEnd, nth, from, aNameMap,
4420 MakeLogicalSide(aAxis, eLogicalEdgeEnd),
4421 aExplicitGridEnd, aStyle);
4422 if (start == int32_t(kAutoLine)) {
4423 // auto / definite line
4424 start = std::max(aNameMap.mClampMinLine, end - 1);
4426 return LinePair(start, end);
4429 nsGridContainerFrame::LineRange nsGridContainerFrame::Grid::ResolveLineRange(
4430 const StyleGridLine& aStart, const StyleGridLine& aEnd,
4431 const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
4432 const nsStylePosition* aStyle) {
4433 LinePair r = ResolveLineRangeHelper(aStart, aEnd, aNameMap, aAxis,
4434 aExplicitGridEnd, aStyle);
4435 MOZ_ASSERT(r.second != int32_t(kAutoLine));
4437 if (r.first == int32_t(kAutoLine)) {
4438 // r.second is a span, clamp it to aNameMap.mClampMaxLine - 1 so that
4439 // the returned range has a HypotheticalEnd <= aNameMap.mClampMaxLine.
4440 // http://dev.w3.org/csswg/css-grid/#overlarge-grids
4441 r.second = std::min(r.second, aNameMap.mClampMaxLine - 1);
4442 } else {
4443 // Clamp the lines to be within our limits, per
4444 // https://www.w3.org/TR/css-grid-2/#overlarge-grids
4445 // Note that our limits here might come from the [kMinLine, kMaxLine]
4446 // extremes; or, they might just be the bounds of a subgrid's explicit
4447 // grid. We use the same clamping approach either way, per
4448 // https://www.w3.org/TR/css-grid-2/#subgrid-implicit ("using the same
4449 // procedure as for clamping placement in an overly-large grid").
4451 // Note that these two clamped() assignments might collapse our range to
4452 // have both edges pointing at the same line (spanning 0 tracks); this
4453 // might happen here if e.g. r.first were mClampMaxLine, and r.second gets
4454 // clamped from some higher number down to mClampMaxLine. We'll handle this
4455 // by shifting the inner line (r.first in this hypothetical) inwards by 1,
4456 // in the #grid-placement-errors section; that achieves the outcome of
4457 // the #overlarge-grids clamping spec text that says "its span must be
4458 // truncated to 1" when clamping an item that was completely outside the
4459 // limits.
4460 r.first = clamped(r.first, aNameMap.mClampMinLine, aNameMap.mClampMaxLine);
4461 r.second =
4462 clamped(r.second, aNameMap.mClampMinLine, aNameMap.mClampMaxLine);
4464 // Handle grid placement errors.
4465 // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
4466 if (r.first > r.second) {
4467 std::swap(r.first, r.second);
4468 } else if (r.first == r.second) {
4469 // (This is #grid-placement-errors fixup, but it's also where we ensure
4470 // that any #overlarge-grids fixup that we did above will end up
4471 // truncating the range to a span of 1 rather than 0 -- i.e. sliding
4472 // inwards if needed.)
4473 if (MOZ_UNLIKELY(r.first == aNameMap.mClampMaxLine)) {
4474 r.first = aNameMap.mClampMaxLine - 1;
4476 r.second = r.first + 1;
4479 return LineRange(r.first, r.second);
4482 nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceDefinite(
4483 nsIFrame* aChild, const LineNameMap& aColLineNameMap,
4484 const LineNameMap& aRowLineNameMap, const nsStylePosition* aStyle) {
4485 const nsStylePosition* itemStyle = aChild->StylePosition();
4486 return GridArea(
4487 ResolveLineRange(itemStyle->mGridColumnStart, itemStyle->mGridColumnEnd,
4488 aColLineNameMap, eLogicalAxisInline, mExplicitGridColEnd,
4489 aStyle),
4490 ResolveLineRange(itemStyle->mGridRowStart, itemStyle->mGridRowEnd,
4491 aRowLineNameMap, eLogicalAxisBlock, mExplicitGridRowEnd,
4492 aStyle));
4495 nsGridContainerFrame::LineRange
4496 nsGridContainerFrame::Grid::ResolveAbsPosLineRange(
4497 const StyleGridLine& aStart, const StyleGridLine& aEnd,
4498 const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
4499 int32_t aGridStart, int32_t aGridEnd, const nsStylePosition* aStyle) {
4500 if (aStart.IsAuto()) {
4501 if (aEnd.IsAuto()) {
4502 return LineRange(kAutoLine, kAutoLine);
4504 uint32_t from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0;
4505 int32_t end = ResolveLine(aEnd, aEnd.line_num, from, aNameMap,
4506 MakeLogicalSide(aAxis, eLogicalEdgeEnd),
4507 aExplicitGridEnd, aStyle);
4508 if (aEnd.is_span) {
4509 ++end;
4511 // A line outside the existing grid is treated as 'auto' for abs.pos (10.1).
4512 end = AutoIfOutside(end, aGridStart, aGridEnd);
4513 return LineRange(kAutoLine, end);
4516 if (aEnd.IsAuto()) {
4517 uint32_t from = aStart.line_num < 0 ? aExplicitGridEnd + 1 : 0;
4518 int32_t start = ResolveLine(aStart, aStart.line_num, from, aNameMap,
4519 MakeLogicalSide(aAxis, eLogicalEdgeStart),
4520 aExplicitGridEnd, aStyle);
4521 if (aStart.is_span) {
4522 start = std::max(aGridEnd - start, aGridStart);
4524 start = AutoIfOutside(start, aGridStart, aGridEnd);
4525 return LineRange(start, kAutoLine);
4528 LineRange r =
4529 ResolveLineRange(aStart, aEnd, aNameMap, aAxis, aExplicitGridEnd, aStyle);
4530 if (r.IsAuto()) {
4531 MOZ_ASSERT(aStart.is_span && aEnd.is_span,
4532 "span / span is the only case "
4533 "leading to IsAuto here -- we dealt with the other cases above");
4534 // The second span was ignored per 9.2.1. For abs.pos., 10.1 says that this
4535 // case should result in "auto / auto" unlike normal flow grid items.
4536 return LineRange(kAutoLine, kAutoLine);
4539 return LineRange(AutoIfOutside(r.mUntranslatedStart, aGridStart, aGridEnd),
4540 AutoIfOutside(r.mUntranslatedEnd, aGridStart, aGridEnd));
4543 nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceAbsPos(
4544 nsIFrame* aChild, const LineNameMap& aColLineNameMap,
4545 const LineNameMap& aRowLineNameMap, const nsStylePosition* aStyle) {
4546 const nsStylePosition* itemStyle = aChild->StylePosition();
4547 int32_t gridColStart = 1 - mExplicitGridOffsetCol;
4548 int32_t gridRowStart = 1 - mExplicitGridOffsetRow;
4549 return GridArea(ResolveAbsPosLineRange(
4550 itemStyle->mGridColumnStart, itemStyle->mGridColumnEnd,
4551 aColLineNameMap, eLogicalAxisInline, mExplicitGridColEnd,
4552 gridColStart, mGridColEnd, aStyle),
4553 ResolveAbsPosLineRange(
4554 itemStyle->mGridRowStart, itemStyle->mGridRowEnd,
4555 aRowLineNameMap, eLogicalAxisBlock, mExplicitGridRowEnd,
4556 gridRowStart, mGridRowEnd, aStyle));
4559 uint32_t nsGridContainerFrame::Grid::FindAutoCol(uint32_t aStartCol,
4560 uint32_t aLockedRow,
4561 const GridArea* aArea) const {
4562 const uint32_t extent = aArea->mCols.Extent();
4563 const uint32_t iStart = aLockedRow;
4564 const uint32_t iEnd = iStart + aArea->mRows.Extent();
4565 uint32_t candidate = aStartCol;
4566 for (uint32_t i = iStart; i < iEnd;) {
4567 if (i >= mCellMap.mCells.Length()) {
4568 break;
4570 const nsTArray<CellMap::Cell>& cellsInRow = mCellMap.mCells[i];
4571 const uint32_t len = cellsInRow.Length();
4572 const uint32_t lastCandidate = candidate;
4573 // Find the first gap in the current row that's at least 'extent' wide.
4574 // ('gap' tracks how wide the current column gap is.)
4575 for (uint32_t j = candidate, gap = 0; j < len && gap < extent; ++j) {
4576 if (!cellsInRow[j].mIsOccupied) {
4577 ++gap;
4578 continue;
4580 candidate = j + 1;
4581 gap = 0;
4583 if (lastCandidate < candidate && i != iStart) {
4584 // Couldn't fit 'extent' tracks at 'lastCandidate' here so we must
4585 // restart from the beginning with the new 'candidate'.
4586 i = iStart;
4587 } else {
4588 ++i;
4591 return candidate;
4594 void nsGridContainerFrame::Grid::PlaceAutoCol(uint32_t aStartCol,
4595 GridArea* aArea,
4596 uint32_t aClampMaxColLine) const {
4597 MOZ_ASSERT(aArea->mRows.IsDefinite() && aArea->mCols.IsAuto());
4598 uint32_t col = FindAutoCol(aStartCol, aArea->mRows.mStart, aArea);
4599 aArea->mCols.ResolveAutoPosition(col, aClampMaxColLine);
4600 MOZ_ASSERT(aArea->IsDefinite());
4603 uint32_t nsGridContainerFrame::Grid::FindAutoRow(uint32_t aLockedCol,
4604 uint32_t aStartRow,
4605 const GridArea* aArea) const {
4606 const uint32_t extent = aArea->mRows.Extent();
4607 const uint32_t jStart = aLockedCol;
4608 const uint32_t jEnd = jStart + aArea->mCols.Extent();
4609 const uint32_t iEnd = mCellMap.mCells.Length();
4610 uint32_t candidate = aStartRow;
4611 // Find the first gap in the rows that's at least 'extent' tall.
4612 // ('gap' tracks how tall the current row gap is.)
4613 for (uint32_t i = candidate, gap = 0; i < iEnd && gap < extent; ++i) {
4614 ++gap; // tentative, but we may reset it below if a column is occupied
4615 const nsTArray<CellMap::Cell>& cellsInRow = mCellMap.mCells[i];
4616 const uint32_t clampedJEnd = std::min<uint32_t>(jEnd, cellsInRow.Length());
4617 // Check if the current row is unoccupied from jStart to jEnd.
4618 for (uint32_t j = jStart; j < clampedJEnd; ++j) {
4619 if (cellsInRow[j].mIsOccupied) {
4620 // Couldn't fit 'extent' rows at 'candidate' here; we hit something
4621 // at row 'i'. So, try the row after 'i' as our next candidate.
4622 candidate = i + 1;
4623 gap = 0;
4624 break;
4628 return candidate;
4631 void nsGridContainerFrame::Grid::PlaceAutoRow(uint32_t aStartRow,
4632 GridArea* aArea,
4633 uint32_t aClampMaxRowLine) const {
4634 MOZ_ASSERT(aArea->mCols.IsDefinite() && aArea->mRows.IsAuto());
4635 uint32_t row = FindAutoRow(aArea->mCols.mStart, aStartRow, aArea);
4636 aArea->mRows.ResolveAutoPosition(row, aClampMaxRowLine);
4637 MOZ_ASSERT(aArea->IsDefinite());
4640 void nsGridContainerFrame::Grid::PlaceAutoAutoInRowOrder(
4641 uint32_t aStartCol, uint32_t aStartRow, GridArea* aArea,
4642 uint32_t aClampMaxColLine, uint32_t aClampMaxRowLine) const {
4643 MOZ_ASSERT(aArea->mCols.IsAuto() && aArea->mRows.IsAuto());
4644 const uint32_t colExtent = aArea->mCols.Extent();
4645 const uint32_t gridRowEnd = mGridRowEnd;
4646 const uint32_t gridColEnd = mGridColEnd;
4647 uint32_t col = aStartCol;
4648 uint32_t row = aStartRow;
4649 for (; row < gridRowEnd; ++row) {
4650 col = FindAutoCol(col, row, aArea);
4651 if (col + colExtent <= gridColEnd) {
4652 break;
4654 col = 0;
4656 MOZ_ASSERT(row < gridRowEnd || col == 0,
4657 "expected column 0 for placing in a new row");
4658 aArea->mCols.ResolveAutoPosition(col, aClampMaxColLine);
4659 aArea->mRows.ResolveAutoPosition(row, aClampMaxRowLine);
4660 MOZ_ASSERT(aArea->IsDefinite());
4663 void nsGridContainerFrame::Grid::PlaceAutoAutoInColOrder(
4664 uint32_t aStartCol, uint32_t aStartRow, GridArea* aArea,
4665 uint32_t aClampMaxColLine, uint32_t aClampMaxRowLine) const {
4666 MOZ_ASSERT(aArea->mCols.IsAuto() && aArea->mRows.IsAuto());
4667 const uint32_t rowExtent = aArea->mRows.Extent();
4668 const uint32_t gridRowEnd = mGridRowEnd;
4669 const uint32_t gridColEnd = mGridColEnd;
4670 uint32_t col = aStartCol;
4671 uint32_t row = aStartRow;
4672 for (; col < gridColEnd; ++col) {
4673 row = FindAutoRow(col, row, aArea);
4674 if (row + rowExtent <= gridRowEnd) {
4675 break;
4677 row = 0;
4679 MOZ_ASSERT(col < gridColEnd || row == 0,
4680 "expected row 0 for placing in a new column");
4681 aArea->mCols.ResolveAutoPosition(col, aClampMaxColLine);
4682 aArea->mRows.ResolveAutoPosition(row, aClampMaxRowLine);
4683 MOZ_ASSERT(aArea->IsDefinite());
4686 template <typename IsEmptyFuncT>
4687 Maybe<nsTArray<uint32_t>>
4688 nsGridContainerFrame::Grid::CalculateAdjustForAutoFitElements(
4689 uint32_t* const aOutNumEmptyLines, TrackSizingFunctions& aSizingFunctions,
4690 uint32_t aNumGridLines, IsEmptyFuncT aIsEmptyFunc) {
4691 Maybe<nsTArray<uint32_t>> trackAdjust;
4692 uint32_t& numEmptyLines = *aOutNumEmptyLines;
4693 numEmptyLines = 0;
4694 if (aSizingFunctions.NumRepeatTracks() > 0) {
4695 MOZ_ASSERT(aSizingFunctions.mHasRepeatAuto);
4696 // Since this loop is concerned with just the repeat tracks, we
4697 // iterate from 0..NumRepeatTracks() which is the natural range of
4698 // mRemoveRepeatTracks. This means we have to add
4699 // (mExplicitGridOffset + mRepeatAutoStart) to get a zero-based
4700 // index for arrays like mCellMap/aIsEmptyFunc and trackAdjust. We'll then
4701 // fill out the trackAdjust array for all the remaining lines.
4702 const uint32_t repeatStart = (aSizingFunctions.mExplicitGridOffset +
4703 aSizingFunctions.mRepeatAutoStart);
4704 const uint32_t numRepeats = aSizingFunctions.NumRepeatTracks();
4705 for (uint32_t i = 0; i < numRepeats; ++i) {
4706 if (numEmptyLines) {
4707 MOZ_ASSERT(trackAdjust.isSome());
4708 (*trackAdjust)[repeatStart + i] = numEmptyLines;
4710 if (aIsEmptyFunc(repeatStart + i)) {
4711 ++numEmptyLines;
4712 if (trackAdjust.isNothing()) {
4713 trackAdjust.emplace(aNumGridLines);
4714 trackAdjust->SetLength(aNumGridLines);
4715 PodZero(trackAdjust->Elements(), trackAdjust->Length());
4718 aSizingFunctions.mRemovedRepeatTracks[i] = true;
4721 // Fill out the trackAdjust array for all the tracks after the repeats.
4722 if (numEmptyLines) {
4723 for (uint32_t line = repeatStart + numRepeats; line < aNumGridLines;
4724 ++line) {
4725 (*trackAdjust)[line] = numEmptyLines;
4730 return trackAdjust;
4733 void nsGridContainerFrame::Grid::SubgridPlaceGridItems(
4734 GridReflowInput& aParentState, Grid* aParentGrid,
4735 const GridItemInfo& aGridItem) {
4736 MOZ_ASSERT(aGridItem.mArea.IsDefinite() ||
4737 aGridItem.mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
4738 "the subgrid's lines should be resolved by now");
4739 if (aGridItem.IsSubgrid(eLogicalAxisInline)) {
4740 aParentState.mFrame->AddStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM);
4742 if (aGridItem.IsSubgrid(eLogicalAxisBlock)) {
4743 aParentState.mFrame->AddStateBits(NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
4745 auto* childGrid = aGridItem.SubgridFrame();
4746 const auto* pos = childGrid->StylePosition();
4747 childGrid->NormalizeChildLists();
4748 GridReflowInput state(childGrid, aParentState.mRenderingContext);
4749 childGrid->InitImplicitNamedAreas(pos);
4751 const bool isOrthogonal = aParentState.mWM.IsOrthogonalTo(state.mWM);
4752 // Record the subgrid's GridArea in a frame property.
4753 auto* subgrid = childGrid->GetProperty(Subgrid::Prop());
4754 if (!subgrid) {
4755 subgrid = new Subgrid(aGridItem.mArea, isOrthogonal, aParentState.mWM);
4756 childGrid->SetProperty(Subgrid::Prop(), subgrid);
4757 } else {
4758 subgrid->mArea = aGridItem.mArea;
4759 subgrid->mIsOrthogonal = isOrthogonal;
4760 subgrid->mGridItems.Clear();
4761 subgrid->mAbsPosItems.Clear();
4764 // Abs.pos. subgrids may have kAutoLine in their area. Map those to the edge
4765 // line in the parent's grid (zero-based line numbers).
4766 if (MOZ_UNLIKELY(subgrid->mArea.mCols.mStart == kAutoLine)) {
4767 subgrid->mArea.mCols.mStart = 0;
4769 if (MOZ_UNLIKELY(subgrid->mArea.mCols.mEnd == kAutoLine)) {
4770 subgrid->mArea.mCols.mEnd = aParentGrid->mGridColEnd - 1;
4772 if (MOZ_UNLIKELY(subgrid->mArea.mRows.mStart == kAutoLine)) {
4773 subgrid->mArea.mRows.mStart = 0;
4775 if (MOZ_UNLIKELY(subgrid->mArea.mRows.mEnd == kAutoLine)) {
4776 subgrid->mArea.mRows.mEnd = aParentGrid->mGridRowEnd - 1;
4779 MOZ_ASSERT((subgrid->mArea.mCols.Extent() > 0 &&
4780 subgrid->mArea.mRows.Extent() > 0) ||
4781 state.mGridItems.IsEmpty(),
4782 "subgrid needs at least one track for its items");
4784 // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
4785 // https://drafts.csswg.org/css-grid/#auto-repeat
4786 // They're only used for auto-repeat in a non-subgridded axis so we skip
4787 // computing them otherwise.
4788 RepeatTrackSizingInput repeatSizing(state.mWM);
4789 if (!childGrid->IsColSubgrid() && state.mColFunctions.mHasRepeatAuto) {
4790 repeatSizing.InitFromStyle(eLogicalAxisInline, state.mWM,
4791 state.mFrame->Style());
4793 if (!childGrid->IsRowSubgrid() && state.mRowFunctions.mHasRepeatAuto) {
4794 repeatSizing.InitFromStyle(eLogicalAxisBlock, state.mWM,
4795 state.mFrame->Style());
4798 PlaceGridItems(state, repeatSizing);
4800 subgrid->mGridItems = std::move(state.mGridItems);
4801 subgrid->mAbsPosItems = std::move(state.mAbsPosItems);
4802 subgrid->mGridColEnd = mGridColEnd;
4803 subgrid->mGridRowEnd = mGridRowEnd;
4806 void nsGridContainerFrame::Grid::PlaceGridItems(
4807 GridReflowInput& aState, const RepeatTrackSizingInput& aSizes) {
4808 MOZ_ASSERT(mCellMap.mCells.IsEmpty(), "unexpected entries in cell map");
4810 mAreas = aState.mFrame->GetImplicitNamedAreas();
4812 if (aState.mFrame->HasSubgridItems() || aState.mFrame->IsSubgrid()) {
4813 if (auto* uts = aState.mFrame->GetUsedTrackSizes()) {
4814 uts->mCanResolveLineRangeSize = {false, false};
4815 uts->mSizes[eLogicalAxisInline].ClearAndRetainStorage();
4816 uts->mSizes[eLogicalAxisBlock].ClearAndRetainStorage();
4820 // SubgridPlaceGridItems will set these if we find any subgrid items.
4821 aState.mFrame->RemoveStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM |
4822 NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
4824 // http://dev.w3.org/csswg/css-grid/#grid-definition
4825 // Initialize the end lines of the Explicit Grid (mExplicitGridCol[Row]End).
4826 // This is determined by the larger of the number of rows/columns defined
4827 // by 'grid-template-areas' and the 'grid-template-rows'/'-columns', plus one.
4828 // Also initialize the Implicit Grid (mGridCol[Row]End) to the same values.
4829 // Note that this is for a grid with a 1,1 origin. We'll change that
4830 // to a 0,0 based grid after placing definite lines.
4831 const nsStylePosition* const gridStyle = aState.mGridStyle;
4832 const auto* areas = gridStyle->mGridTemplateAreas.IsNone()
4833 ? nullptr
4834 : &*gridStyle->mGridTemplateAreas.AsAreas();
4835 const LineNameMap* parentLineNameMap = nullptr;
4836 const LineRange* subgridRange = nullptr;
4837 bool subgridAxisIsSameDirection = true;
4838 if (!aState.mFrame->IsColSubgrid()) {
4839 aState.mColFunctions.InitRepeatTracks(
4840 gridStyle->mColumnGap, aSizes.mMin.ISize(aState.mWM),
4841 aSizes.mSize.ISize(aState.mWM), aSizes.mMax.ISize(aState.mWM));
4842 uint32_t areaCols = areas ? areas->width + 1 : 1;
4843 mExplicitGridColEnd = aState.mColFunctions.ComputeExplicitGridEnd(areaCols);
4844 } else {
4845 const auto* subgrid = aState.mFrame->GetProperty(Subgrid::Prop());
4846 subgridRange = &subgrid->SubgridCols();
4847 uint32_t extent = subgridRange->Extent();
4848 mExplicitGridColEnd = extent + 1; // the grid is 1-based at this point
4849 parentLineNameMap =
4850 ParentLineMapForAxis(subgrid->mIsOrthogonal, eLogicalAxisInline);
4851 auto parentWM =
4852 aState.mFrame->ParentGridContainerForSubgrid()->GetWritingMode();
4853 subgridAxisIsSameDirection =
4854 aState.mWM.ParallelAxisStartsOnSameSide(eLogicalAxisInline, parentWM);
4856 mGridColEnd = mExplicitGridColEnd;
4857 LineNameMap colLineNameMap(gridStyle, mAreas, aState.mColFunctions,
4858 parentLineNameMap, subgridRange,
4859 subgridAxisIsSameDirection);
4861 if (!aState.mFrame->IsRowSubgrid()) {
4862 const Maybe<nscoord> containBSize = aState.mFrame->ContainIntrinsicBSize();
4863 const nscoord repeatTrackSizingBSize = [&] {
4864 // This clamping only applies to auto sizes.
4865 if (containBSize &&
4866 aSizes.mSize.BSize(aState.mWM) == NS_UNCONSTRAINEDSIZE) {
4867 return NS_CSS_MINMAX(*containBSize, aSizes.mMin.BSize(aState.mWM),
4868 aSizes.mMax.BSize(aState.mWM));
4870 return aSizes.mSize.BSize(aState.mWM);
4871 }();
4872 aState.mRowFunctions.InitRepeatTracks(
4873 gridStyle->mRowGap, aSizes.mMin.BSize(aState.mWM),
4874 repeatTrackSizingBSize, aSizes.mMax.BSize(aState.mWM));
4875 uint32_t areaRows = areas ? areas->strings.Length() + 1 : 1;
4876 mExplicitGridRowEnd = aState.mRowFunctions.ComputeExplicitGridEnd(areaRows);
4877 parentLineNameMap = nullptr;
4878 subgridRange = nullptr;
4879 } else {
4880 const auto* subgrid = aState.mFrame->GetProperty(Subgrid::Prop());
4881 subgridRange = &subgrid->SubgridRows();
4882 uint32_t extent = subgridRange->Extent();
4883 mExplicitGridRowEnd = extent + 1; // the grid is 1-based at this point
4884 parentLineNameMap =
4885 ParentLineMapForAxis(subgrid->mIsOrthogonal, eLogicalAxisBlock);
4886 auto parentWM =
4887 aState.mFrame->ParentGridContainerForSubgrid()->GetWritingMode();
4888 subgridAxisIsSameDirection =
4889 aState.mWM.ParallelAxisStartsOnSameSide(eLogicalAxisBlock, parentWM);
4891 mGridRowEnd = mExplicitGridRowEnd;
4892 LineNameMap rowLineNameMap(gridStyle, mAreas, aState.mRowFunctions,
4893 parentLineNameMap, subgridRange,
4894 subgridAxisIsSameDirection);
4896 const bool isSubgridOrItemInSubgrid =
4897 aState.mFrame->IsSubgrid() || !!mParentGrid;
4898 auto SetSubgridChildEdgeBits =
4899 [this, isSubgridOrItemInSubgrid](GridItemInfo& aItem) -> void {
4900 if (isSubgridOrItemInSubgrid) {
4901 const auto& area = aItem.mArea;
4902 if (area.mCols.mStart == 0) {
4903 aItem.mState[eLogicalAxisInline] |= ItemState::eStartEdge;
4905 if (area.mCols.mEnd == mGridColEnd) {
4906 aItem.mState[eLogicalAxisInline] |= ItemState::eEndEdge;
4908 if (area.mRows.mStart == 0) {
4909 aItem.mState[eLogicalAxisBlock] |= ItemState::eStartEdge;
4911 if (area.mRows.mEnd == mGridRowEnd) {
4912 aItem.mState[eLogicalAxisBlock] |= ItemState::eEndEdge;
4917 SetLineMaps(&colLineNameMap, &rowLineNameMap);
4919 // http://dev.w3.org/csswg/css-grid/#line-placement
4920 // Resolve definite positions per spec chap 9.2.
4921 int32_t minCol = 1;
4922 int32_t minRow = 1;
4923 aState.mGridItems.ClearAndRetainStorage();
4924 aState.mIter.Reset();
4925 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
4926 nsIFrame* child = *aState.mIter;
4927 GridItemInfo* info = aState.mGridItems.AppendElement(GridItemInfo(
4928 child,
4929 PlaceDefinite(child, colLineNameMap, rowLineNameMap, gridStyle)));
4930 MOZ_ASSERT(aState.mIter.ItemIndex() == aState.mGridItems.Length() - 1,
4931 "ItemIndex() is broken");
4932 GridArea& area = info->mArea;
4933 if (area.mCols.IsDefinite()) {
4934 minCol = std::min(minCol, area.mCols.mUntranslatedStart);
4936 if (area.mRows.IsDefinite()) {
4937 minRow = std::min(minRow, area.mRows.mUntranslatedStart);
4941 // Translate the whole grid so that the top-/left-most area is at 0,0.
4942 mExplicitGridOffsetCol = 1 - minCol; // minCol/Row is always <= 1, see above
4943 mExplicitGridOffsetRow = 1 - minRow;
4944 aState.mColFunctions.mExplicitGridOffset = mExplicitGridOffsetCol;
4945 aState.mRowFunctions.mExplicitGridOffset = mExplicitGridOffsetRow;
4946 const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
4947 const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
4948 const bool isRowMasonry = aState.mFrame->IsMasonry(eLogicalAxisBlock);
4949 const bool isColMasonry = aState.mFrame->IsMasonry(eLogicalAxisInline);
4950 const bool isMasonry = isColMasonry || isRowMasonry;
4951 mGridColEnd += offsetToColZero;
4952 mGridRowEnd += offsetToRowZero;
4953 const uint32_t gridAxisTrackCount = isRowMasonry ? mGridColEnd : mGridRowEnd;
4954 aState.mIter.Reset();
4955 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
4956 auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
4957 GridArea& area = item.mArea;
4958 if (area.mCols.IsDefinite()) {
4959 area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
4960 area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
4962 if (area.mRows.IsDefinite()) {
4963 area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
4964 area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
4966 if (area.IsDefinite()) {
4967 if (isMasonry) {
4968 item.MaybeInhibitSubgridInMasonry(aState.mFrame, gridAxisTrackCount);
4970 if (item.IsSubgrid()) {
4971 Grid grid(this);
4972 grid.SubgridPlaceGridItems(aState, this, item);
4974 mCellMap.Fill(area);
4975 InflateGridFor(area);
4976 SetSubgridChildEdgeBits(item);
4980 // http://dev.w3.org/csswg/css-grid/#auto-placement-algo
4981 // Step 1, place 'auto' items that have one definite position -
4982 // definite row (column) for grid-auto-flow:row (column).
4983 auto flowStyle = gridStyle->mGridAutoFlow;
4984 const bool isRowOrder =
4985 isMasonry ? isRowMasonry : !!(flowStyle & StyleGridAutoFlow::ROW);
4986 const bool isSparse = !(flowStyle & StyleGridAutoFlow::DENSE);
4987 uint32_t clampMaxColLine = colLineNameMap.mClampMaxLine + offsetToColZero;
4988 uint32_t clampMaxRowLine = rowLineNameMap.mClampMaxLine + offsetToRowZero;
4989 // We need 1 cursor per row (or column) if placement is sparse.
4991 Maybe<nsTHashMap<nsUint32HashKey, uint32_t>> cursors;
4992 if (isSparse) {
4993 cursors.emplace();
4995 auto placeAutoMinorFunc =
4996 isRowOrder ? &Grid::PlaceAutoCol : &Grid::PlaceAutoRow;
4997 uint32_t clampMaxLine = isRowOrder ? clampMaxColLine : clampMaxRowLine;
4998 aState.mIter.Reset();
4999 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
5000 auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
5001 GridArea& area = item.mArea;
5002 LineRange& major = isRowOrder ? area.mRows : area.mCols;
5003 LineRange& minor = isRowOrder ? area.mCols : area.mRows;
5004 if (major.IsDefinite() && minor.IsAuto()) {
5005 // Items with 'auto' in the minor dimension only.
5006 const uint32_t cursor = isSparse ? cursors->Get(major.mStart) : 0;
5007 (this->*placeAutoMinorFunc)(cursor, &area, clampMaxLine);
5008 if (isMasonry) {
5009 item.MaybeInhibitSubgridInMasonry(aState.mFrame, gridAxisTrackCount);
5011 if (item.IsSubgrid()) {
5012 Grid grid(this);
5013 grid.SubgridPlaceGridItems(aState, this, item);
5015 mCellMap.Fill(area);
5016 SetSubgridChildEdgeBits(item);
5017 if (isSparse) {
5018 cursors->InsertOrUpdate(major.mStart, minor.mEnd);
5021 InflateGridFor(area); // Step 2, inflating for auto items too
5025 // XXX NOTE possible spec issue.
5026 // XXX It's unclear if the remaining major-dimension auto and
5027 // XXX auto in both dimensions should use the same cursor or not,
5028 // XXX https://www.w3.org/Bugs/Public/show_bug.cgi?id=16044
5029 // XXX seems to indicate it shouldn't.
5030 // XXX http://dev.w3.org/csswg/css-grid/#auto-placement-cursor
5031 // XXX now says it should (but didn't in earlier versions)
5033 // Step 3, place the remaining grid items
5034 uint32_t cursorMajor = 0; // for 'dense' these two cursors will stay at 0,0
5035 uint32_t cursorMinor = 0;
5036 auto placeAutoMajorFunc =
5037 isRowOrder ? &Grid::PlaceAutoRow : &Grid::PlaceAutoCol;
5038 uint32_t clampMaxMajorLine = isRowOrder ? clampMaxRowLine : clampMaxColLine;
5039 aState.mIter.Reset();
5040 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
5041 auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
5042 GridArea& area = item.mArea;
5043 MOZ_ASSERT(*aState.mIter == item.mFrame,
5044 "iterator out of sync with aState.mGridItems");
5045 LineRange& major = isRowOrder ? area.mRows : area.mCols;
5046 LineRange& minor = isRowOrder ? area.mCols : area.mRows;
5047 if (major.IsAuto()) {
5048 if (minor.IsDefinite()) {
5049 // Items with 'auto' in the major dimension only.
5050 if (isSparse) {
5051 if (minor.mStart < cursorMinor) {
5052 ++cursorMajor;
5054 cursorMinor = minor.mStart;
5056 (this->*placeAutoMajorFunc)(cursorMajor, &area, clampMaxMajorLine);
5057 if (isSparse) {
5058 cursorMajor = major.mStart;
5060 } else {
5061 // Items with 'auto' in both dimensions.
5062 if (isRowOrder) {
5063 PlaceAutoAutoInRowOrder(cursorMinor, cursorMajor, &area,
5064 clampMaxColLine, clampMaxRowLine);
5065 } else {
5066 PlaceAutoAutoInColOrder(cursorMajor, cursorMinor, &area,
5067 clampMaxColLine, clampMaxRowLine);
5069 if (isSparse) {
5070 cursorMajor = major.mStart;
5071 cursorMinor = minor.mEnd;
5072 #ifdef DEBUG
5073 uint32_t gridMajorEnd = isRowOrder ? mGridRowEnd : mGridColEnd;
5074 uint32_t gridMinorEnd = isRowOrder ? mGridColEnd : mGridRowEnd;
5075 MOZ_ASSERT(cursorMajor <= gridMajorEnd,
5076 "we shouldn't need to place items further than 1 track "
5077 "past the current end of the grid, in major dimension");
5078 MOZ_ASSERT(cursorMinor <= gridMinorEnd,
5079 "we shouldn't add implicit minor tracks for auto/auto");
5080 #endif
5083 if (isMasonry) {
5084 item.MaybeInhibitSubgridInMasonry(aState.mFrame, gridAxisTrackCount);
5086 if (item.IsSubgrid()) {
5087 Grid grid(this);
5088 grid.SubgridPlaceGridItems(aState, this, item);
5090 mCellMap.Fill(area);
5091 InflateGridFor(area);
5092 SetSubgridChildEdgeBits(item);
5093 // XXXmats it might be possible to optimize this a bit for masonry layout
5094 // if this item was placed in the 2nd row && !isSparse, or the 1st row
5095 // is full. Still gotta inflate the grid for all items though to make
5096 // the grid large enough...
5100 // Force all items into the 1st/2nd track and have span 1 in the masonry axis.
5101 // (See comment on nsGridContainerFrame::MasonryLayout().)
5102 if (isMasonry) {
5103 auto masonryAxis = isRowMasonry ? eLogicalAxisBlock : eLogicalAxisInline;
5104 aState.mIter.Reset();
5105 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
5106 auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
5107 auto& masonryRange = item.mArea.LineRangeForAxis(masonryAxis);
5108 masonryRange.mStart = std::min(masonryRange.mStart, 1U);
5109 masonryRange.mEnd = masonryRange.mStart + 1U;
5113 if (aState.mFrame->IsAbsoluteContainer()) {
5114 // 9.4 Absolutely-positioned Grid Items
5115 // http://dev.w3.org/csswg/css-grid/#abspos-items
5116 // We only resolve definite lines here; we'll align auto positions to the
5117 // grid container later during reflow.
5118 const nsFrameList& children =
5119 aState.mFrame->GetChildList(aState.mFrame->GetAbsoluteListID());
5120 const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
5121 const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
5122 // Untranslate the grid again temporarily while resolving abs.pos. lines.
5123 AutoRestore<uint32_t> zeroOffsetGridColEnd(mGridColEnd);
5124 AutoRestore<uint32_t> zeroOffsetGridRowEnd(mGridRowEnd);
5125 mGridColEnd -= offsetToColZero;
5126 mGridRowEnd -= offsetToRowZero;
5127 aState.mAbsPosItems.ClearAndRetainStorage();
5128 for (nsIFrame* child : children) {
5129 GridItemInfo* info = aState.mAbsPosItems.AppendElement(GridItemInfo(
5130 child,
5131 PlaceAbsPos(child, colLineNameMap, rowLineNameMap, gridStyle)));
5132 GridArea& area = info->mArea;
5133 if (area.mCols.mUntranslatedStart != int32_t(kAutoLine)) {
5134 area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
5135 if (isColMasonry) {
5136 // XXXmats clamp any non-auto line to 0 or 1. This is intended to
5137 // allow authors to address the start/end of the masonry box.
5138 // This is experimental at this point though and needs author feedback
5139 // and spec work to sort out what is desired and how it should work.
5140 // See https://github.com/w3c/csswg-drafts/issues/4650
5141 area.mCols.mStart = std::min(area.mCols.mStart, 1U);
5144 if (area.mCols.mUntranslatedEnd != int32_t(kAutoLine)) {
5145 area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
5146 if (isColMasonry) {
5147 // ditto
5148 area.mCols.mEnd = std::min(area.mCols.mEnd, 1U);
5151 if (area.mRows.mUntranslatedStart != int32_t(kAutoLine)) {
5152 area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
5153 if (isRowMasonry) {
5154 // ditto
5155 area.mRows.mStart = std::min(area.mRows.mStart, 1U);
5158 if (area.mRows.mUntranslatedEnd != int32_t(kAutoLine)) {
5159 area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
5160 if (isRowMasonry) {
5161 // ditto
5162 area.mRows.mEnd = std::min(area.mRows.mEnd, 1U);
5165 if (isMasonry) {
5166 info->MaybeInhibitSubgridInMasonry(aState.mFrame, gridAxisTrackCount);
5169 // An abs.pos. subgrid with placement auto/1 or -1/auto technically
5170 // doesn't span any parent tracks. Inhibit subgridding in this case.
5171 if (info->IsSubgrid(eLogicalAxisInline)) {
5172 if (info->mArea.mCols.mStart == zeroOffsetGridColEnd.SavedValue() ||
5173 info->mArea.mCols.mEnd == 0) {
5174 info->InhibitSubgrid(aState.mFrame, eLogicalAxisInline);
5177 if (info->IsSubgrid(eLogicalAxisBlock)) {
5178 if (info->mArea.mRows.mStart == zeroOffsetGridRowEnd.SavedValue() ||
5179 info->mArea.mRows.mEnd == 0) {
5180 info->InhibitSubgrid(aState.mFrame, eLogicalAxisBlock);
5184 if (info->IsSubgrid()) {
5185 Grid grid(this);
5186 grid.SubgridPlaceGridItems(aState, this, *info);
5191 // Count empty 'auto-fit' tracks in the repeat() range.
5192 // |colAdjust| will have a count for each line in the grid of how many
5193 // tracks were empty between the start of the grid and that line.
5195 Maybe<nsTArray<uint32_t>> colAdjust;
5196 uint32_t numEmptyCols = 0;
5197 if (aState.mColFunctions.mHasRepeatAuto &&
5198 gridStyle->mGridTemplateColumns.GetRepeatAutoValue()->count.IsAutoFit()) {
5199 const auto& cellMap = mCellMap;
5200 colAdjust = CalculateAdjustForAutoFitElements(
5201 &numEmptyCols, aState.mColFunctions, mGridColEnd + 1,
5202 [&cellMap](uint32_t i) -> bool { return cellMap.IsEmptyCol(i); });
5205 // Do similar work for the row tracks, with the same logic.
5206 Maybe<nsTArray<uint32_t>> rowAdjust;
5207 uint32_t numEmptyRows = 0;
5208 if (aState.mRowFunctions.mHasRepeatAuto &&
5209 gridStyle->mGridTemplateRows.GetRepeatAutoValue()->count.IsAutoFit()) {
5210 const auto& cellMap = mCellMap;
5211 rowAdjust = CalculateAdjustForAutoFitElements(
5212 &numEmptyRows, aState.mRowFunctions, mGridRowEnd + 1,
5213 [&cellMap](uint32_t i) -> bool { return cellMap.IsEmptyRow(i); });
5215 MOZ_ASSERT((numEmptyCols > 0) == colAdjust.isSome());
5216 MOZ_ASSERT((numEmptyRows > 0) == rowAdjust.isSome());
5217 // Remove the empty 'auto-fit' tracks we found above, if any.
5218 if (numEmptyCols || numEmptyRows) {
5219 // Adjust the line numbers in the grid areas.
5220 for (auto& item : aState.mGridItems) {
5221 if (numEmptyCols) {
5222 item.AdjustForRemovedTracks(eLogicalAxisInline, *colAdjust);
5224 if (numEmptyRows) {
5225 item.AdjustForRemovedTracks(eLogicalAxisBlock, *rowAdjust);
5228 for (auto& item : aState.mAbsPosItems) {
5229 if (numEmptyCols) {
5230 item.AdjustForRemovedTracks(eLogicalAxisInline, *colAdjust);
5232 if (numEmptyRows) {
5233 item.AdjustForRemovedTracks(eLogicalAxisBlock, *rowAdjust);
5236 // Adjust the grid size.
5237 mGridColEnd -= numEmptyCols;
5238 mExplicitGridColEnd -= numEmptyCols;
5239 mGridRowEnd -= numEmptyRows;
5240 mExplicitGridRowEnd -= numEmptyRows;
5241 // Adjust the track mapping to unmap the removed tracks.
5242 auto colRepeatCount = aState.mColFunctions.NumRepeatTracks();
5243 aState.mColFunctions.SetNumRepeatTracks(colRepeatCount - numEmptyCols);
5244 auto rowRepeatCount = aState.mRowFunctions.NumRepeatTracks();
5245 aState.mRowFunctions.SetNumRepeatTracks(rowRepeatCount - numEmptyRows);
5248 // Update the line boundaries of the implicit grid areas, if needed.
5249 if (mAreas && aState.mFrame->HasAnyStateBits(NS_STATE_GRID_COMPUTED_INFO)) {
5250 for (auto iter = mAreas->iter(); !iter.done(); iter.next()) {
5251 auto& areaInfo = iter.get().value();
5253 // Resolve the lines for the area. We use the name of the area as the
5254 // name of the lines, knowing that the line placement algorithm will
5255 // add the -start and -end suffixes as appropriate for layout.
5256 StyleGridLine lineStartAndEnd;
5257 lineStartAndEnd.ident._0 = areaInfo.name;
5259 LineRange columnLines =
5260 ResolveLineRange(lineStartAndEnd, lineStartAndEnd, colLineNameMap,
5261 eLogicalAxisInline, mExplicitGridColEnd, gridStyle);
5263 LineRange rowLines =
5264 ResolveLineRange(lineStartAndEnd, lineStartAndEnd, rowLineNameMap,
5265 eLogicalAxisBlock, mExplicitGridRowEnd, gridStyle);
5267 // Put the resolved line indices back into the area structure.
5268 areaInfo.columns.start = columnLines.mStart + mExplicitGridOffsetCol;
5269 areaInfo.columns.end = columnLines.mEnd + mExplicitGridOffsetCol;
5270 areaInfo.rows.start = rowLines.mStart + mExplicitGridOffsetRow;
5271 areaInfo.rows.end = rowLines.mEnd + mExplicitGridOffsetRow;
5276 void nsGridContainerFrame::Tracks::Initialize(
5277 const TrackSizingFunctions& aFunctions,
5278 const NonNegativeLengthPercentageOrNormal& aGridGap, uint32_t aNumTracks,
5279 nscoord aContentBoxSize) {
5280 mSizes.SetLength(aNumTracks);
5281 PodZero(mSizes.Elements(), mSizes.Length());
5282 for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
5283 auto& sz = mSizes[i];
5284 mStateUnion |= sz.Initialize(aContentBoxSize, aFunctions.SizingFor(i));
5285 if (mIsMasonry) {
5286 sz.mBase = aContentBoxSize;
5287 sz.mLimit = aContentBoxSize;
5290 mGridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aContentBoxSize);
5291 mContentBoxSize = aContentBoxSize;
5295 * Reflow aChild in the given aAvailableSize.
5297 static nscoord MeasuringReflow(nsIFrame* aChild,
5298 const ReflowInput* aReflowInput, gfxContext* aRC,
5299 const LogicalSize& aAvailableSize,
5300 const LogicalSize& aCBSize,
5301 nscoord aIMinSizeClamp = NS_MAXSIZE,
5302 nscoord aBMinSizeClamp = NS_MAXSIZE) {
5303 MOZ_ASSERT(aChild->IsGridItem(), "aChild should be a grid item!");
5304 auto* parent = static_cast<nsGridContainerFrame*>(aChild->GetParent());
5305 nsPresContext* pc = aChild->PresContext();
5306 Maybe<ReflowInput> dummyParentState;
5307 const ReflowInput* rs = aReflowInput;
5308 if (!aReflowInput) {
5309 MOZ_ASSERT(!parent->HasAnyStateBits(NS_FRAME_IN_REFLOW));
5310 dummyParentState.emplace(
5311 pc, parent, aRC,
5312 LogicalSize(parent->GetWritingMode(), 0, NS_UNCONSTRAINEDSIZE),
5313 ReflowInput::InitFlag::DummyParentReflowInput);
5314 rs = dummyParentState.ptr();
5316 #ifdef DEBUG
5317 // This will suppress various ABSURD_SIZE warnings for this reflow.
5318 parent->SetProperty(nsContainerFrame::DebugReflowingWithInfiniteISize(),
5319 true);
5320 #endif
5321 auto wm = aChild->GetWritingMode();
5322 ComputeSizeFlags csFlags = ComputeSizeFlag::IsGridMeasuringReflow;
5323 // Shrink-wrap grid items that will be aligned (rather than stretched) in
5324 // their own inline axis.
5325 if (!parent->GridItemShouldStretch(aChild, eLogicalAxisInline)) {
5326 csFlags += ComputeSizeFlag::ShrinkWrap;
5328 if (aAvailableSize.ISize(wm) == INFINITE_ISIZE_COORD) {
5329 csFlags += ComputeSizeFlag::ShrinkWrap;
5331 if (aIMinSizeClamp != NS_MAXSIZE) {
5332 csFlags += ComputeSizeFlag::IClampMarginBoxMinSize;
5334 if (aBMinSizeClamp != NS_MAXSIZE) {
5335 csFlags += ComputeSizeFlag::BClampMarginBoxMinSize;
5336 aChild->SetProperty(nsIFrame::BClampMarginBoxMinSizeProperty(),
5337 aBMinSizeClamp);
5338 } else {
5339 aChild->RemoveProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
5341 ReflowInput childRI(pc, *rs, aChild, aAvailableSize, Some(aCBSize), {}, {},
5342 csFlags);
5344 // FIXME (perf): It would be faster to do this only if the previous reflow of
5345 // the child was not a measuring reflow, and only if the child does some of
5346 // the things that are affected by ComputeSizeFlag::IsGridMeasuringReflow.
5347 childRI.SetBResize(true);
5348 // Not 100% sure this is needed, but be conservative for now:
5349 childRI.mFlags.mIsBResizeForPercentages = true;
5351 ReflowOutput childSize(childRI);
5352 nsReflowStatus childStatus;
5353 const nsIFrame::ReflowChildFlags flags =
5354 nsIFrame::ReflowChildFlags::NoMoveFrame |
5355 nsIFrame::ReflowChildFlags::NoSizeView |
5356 nsIFrame::ReflowChildFlags::NoDeleteNextInFlowChild;
5358 bool found;
5359 GridItemCachedBAxisMeasurement cachedMeasurement =
5360 aChild->GetProperty(GridItemCachedBAxisMeasurement::Prop(), &found);
5361 if (found && cachedMeasurement.IsValidFor(aChild, aCBSize)) {
5362 childSize.BSize(wm) = cachedMeasurement.BSize();
5363 childSize.ISize(wm) = aChild->ISize(wm);
5364 nsContainerFrame::FinishReflowChild(aChild, pc, childSize, &childRI, wm,
5365 LogicalPoint(wm), nsSize(), flags);
5366 GRID_LOG(
5367 "[perf] MeasuringReflow accepted cached value=%d, child=%p, "
5368 "aCBSize.ISize=%d",
5369 cachedMeasurement.BSize(), aChild,
5370 aCBSize.ISize(aChild->GetWritingMode()));
5371 return cachedMeasurement.BSize();
5374 parent->ReflowChild(aChild, pc, childSize, childRI, wm, LogicalPoint(wm),
5375 nsSize(), flags, childStatus);
5376 nsContainerFrame::FinishReflowChild(aChild, pc, childSize, &childRI, wm,
5377 LogicalPoint(wm), nsSize(), flags);
5378 #ifdef DEBUG
5379 parent->RemoveProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
5380 #endif
5381 if (!found &&
5382 GridItemCachedBAxisMeasurement::CanCacheMeasurement(aChild, aCBSize)) {
5383 GridItemCachedBAxisMeasurement cachedMeasurement(aChild, aCBSize,
5384 childSize.BSize(wm));
5385 aChild->SetProperty(GridItemCachedBAxisMeasurement::Prop(),
5386 cachedMeasurement);
5387 GRID_LOG(
5388 "[perf] MeasuringReflow created new cached value=%d, child=%p, "
5389 "aCBSize.ISize=%d",
5390 cachedMeasurement.BSize(), aChild,
5391 aCBSize.ISize(aChild->GetWritingMode()));
5392 } else if (found) {
5393 if (GridItemCachedBAxisMeasurement::CanCacheMeasurement(aChild, aCBSize)) {
5394 cachedMeasurement.Update(aChild, aCBSize, childSize.BSize(wm));
5395 GRID_LOG(
5396 "[perf] MeasuringReflow rejected but updated cached value=%d, "
5397 "child=%p, aCBSize.ISize=%d",
5398 cachedMeasurement.BSize(), aChild,
5399 aCBSize.ISize(aChild->GetWritingMode()));
5400 aChild->SetProperty(GridItemCachedBAxisMeasurement::Prop(),
5401 cachedMeasurement);
5402 } else {
5403 aChild->RemoveProperty(GridItemCachedBAxisMeasurement::Prop());
5404 GRID_LOG(
5405 "[perf] MeasuringReflow rejected and removed cached value, "
5406 "child=%p",
5407 aChild);
5411 return childSize.BSize(wm);
5415 * Reflow aChild in the given aAvailableSize, using aNewContentBoxSize as its
5416 * computed size in aChildAxis.
5418 static void PostReflowStretchChild(
5419 nsIFrame* aChild, const ReflowInput& aReflowInput,
5420 const LogicalSize& aAvailableSize, const LogicalSize& aCBSize,
5421 LogicalAxis aChildAxis, const nscoord aNewContentBoxSize,
5422 nscoord aIMinSizeClamp = NS_MAXSIZE, nscoord aBMinSizeClamp = NS_MAXSIZE) {
5423 nsPresContext* pc = aChild->PresContext();
5424 ComputeSizeFlags csFlags;
5425 if (aIMinSizeClamp != NS_MAXSIZE) {
5426 csFlags += ComputeSizeFlag::IClampMarginBoxMinSize;
5428 if (aBMinSizeClamp != NS_MAXSIZE) {
5429 csFlags += ComputeSizeFlag::BClampMarginBoxMinSize;
5430 aChild->SetProperty(nsIFrame::BClampMarginBoxMinSizeProperty(),
5431 aBMinSizeClamp);
5432 } else {
5433 aChild->RemoveProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
5435 ReflowInput ri(pc, aReflowInput, aChild, aAvailableSize, Some(aCBSize), {},
5436 {}, csFlags);
5437 if (aChildAxis == eLogicalAxisBlock) {
5438 ri.SetComputedBSize(ri.ApplyMinMaxBSize(aNewContentBoxSize));
5439 } else {
5440 ri.SetComputedISize(ri.ApplyMinMaxISize(aNewContentBoxSize));
5442 ReflowOutput childSize(ri);
5443 nsReflowStatus childStatus;
5444 const nsIFrame::ReflowChildFlags flags =
5445 nsIFrame::ReflowChildFlags::NoMoveFrame |
5446 nsIFrame::ReflowChildFlags::NoDeleteNextInFlowChild;
5447 auto wm = aChild->GetWritingMode();
5448 nsContainerFrame* parent = aChild->GetParent();
5449 parent->ReflowChild(aChild, pc, childSize, ri, wm, LogicalPoint(wm), nsSize(),
5450 flags, childStatus);
5451 nsContainerFrame::FinishReflowChild(aChild, pc, childSize, &ri, wm,
5452 LogicalPoint(wm), nsSize(), flags);
5456 * Return the accumulated margin+border+padding in aAxis for aFrame (a subgrid)
5457 * and its ancestor subgrids.
5459 static LogicalMargin SubgridAccumulatedMarginBorderPadding(
5460 nsIFrame* aFrame, const Subgrid* aSubgrid, WritingMode aResultWM,
5461 LogicalAxis aAxis) {
5462 MOZ_ASSERT(aFrame->IsGridContainerFrame());
5463 auto* subgridFrame = static_cast<nsGridContainerFrame*>(aFrame);
5464 LogicalMargin result(aSubgrid->mMarginBorderPadding);
5465 auto* parent = subgridFrame->ParentGridContainerForSubgrid();
5466 auto subgridCBWM = parent->GetWritingMode();
5467 auto childRange = aSubgrid->mArea.LineRangeForAxis(aAxis);
5468 bool skipStartSide = false;
5469 bool skipEndSide = false;
5470 auto axis = aSubgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
5471 // If aFrame's parent is also a subgrid, then add its MBP on the edges that
5472 // are adjacent (i.e. start or end in the same track), recursively.
5473 // ("parent" refers to the grid-frame we're currently adding MBP for,
5474 // and "grandParent" its parent, as we walk up the chain.)
5475 while (parent->IsSubgrid(axis)) {
5476 auto* parentSubgrid = parent->GetProperty(Subgrid::Prop());
5477 auto* grandParent = parent->ParentGridContainerForSubgrid();
5478 auto parentCBWM = grandParent->GetWritingMode();
5479 if (parentCBWM.IsOrthogonalTo(subgridCBWM)) {
5480 axis = GetOrthogonalAxis(axis);
5482 const auto& parentRange = parentSubgrid->mArea.LineRangeForAxis(axis);
5483 bool sameDir = parentCBWM.ParallelAxisStartsOnSameSide(axis, subgridCBWM);
5484 if (sameDir) {
5485 skipStartSide |= childRange.mStart != 0;
5486 skipEndSide |= childRange.mEnd != parentRange.Extent();
5487 } else {
5488 skipEndSide |= childRange.mStart != 0;
5489 skipStartSide |= childRange.mEnd != parentRange.Extent();
5491 if (skipStartSide && skipEndSide) {
5492 break;
5494 auto mbp =
5495 parentSubgrid->mMarginBorderPadding.ConvertTo(subgridCBWM, parentCBWM);
5496 if (skipStartSide) {
5497 mbp.Start(aAxis, subgridCBWM) = nscoord(0);
5499 if (skipEndSide) {
5500 mbp.End(aAxis, subgridCBWM) = nscoord(0);
5502 result += mbp;
5503 parent = grandParent;
5504 childRange = parentRange;
5506 return result.ConvertTo(aResultWM, subgridCBWM);
5510 * Return the [min|max]-content contribution of aChild to its parent (i.e.
5511 * the child's margin-box) in aAxis.
5513 static nscoord ContentContribution(
5514 const GridItemInfo& aGridItem, const GridReflowInput& aState,
5515 gfxContext* aRC, WritingMode aCBWM, LogicalAxis aAxis,
5516 const Maybe<LogicalSize>& aPercentageBasis, IntrinsicISizeType aConstraint,
5517 nscoord aMinSizeClamp = NS_MAXSIZE, uint32_t aFlags = 0) {
5518 nsIFrame* child = aGridItem.mFrame;
5520 nscoord extraMargin = 0;
5521 nsGridContainerFrame::Subgrid* subgrid = nullptr;
5522 if (child->GetParent() != aState.mFrame) {
5523 // |child| is a subgrid descendant, so it contributes its subgrids'
5524 // margin+border+padding for any edge tracks that it spans.
5525 auto* subgridFrame = child->GetParent();
5526 subgrid = subgridFrame->GetProperty(Subgrid::Prop());
5527 const auto itemEdgeBits = aGridItem.mState[aAxis] & ItemState::eEdgeBits;
5528 if (itemEdgeBits) {
5529 LogicalMargin mbp = SubgridAccumulatedMarginBorderPadding(
5530 subgridFrame, subgrid, aCBWM, aAxis);
5531 if (itemEdgeBits & ItemState::eStartEdge) {
5532 extraMargin += mbp.Start(aAxis, aCBWM);
5534 if (itemEdgeBits & ItemState::eEndEdge) {
5535 extraMargin += mbp.End(aAxis, aCBWM);
5538 // It also contributes (half of) the subgrid's gap on its edges (if any)
5539 // subtracted by the non-subgrid ancestor grid container's gap.
5540 // Note that this can also be negative since it's considered a margin.
5541 if (itemEdgeBits != ItemState::eEdgeBits) {
5542 auto subgridAxis = aCBWM.IsOrthogonalTo(subgridFrame->GetWritingMode())
5543 ? GetOrthogonalAxis(aAxis)
5544 : aAxis;
5545 auto& gapStyle = subgridAxis == eLogicalAxisBlock
5546 ? subgridFrame->StylePosition()->mRowGap
5547 : subgridFrame->StylePosition()->mColumnGap;
5548 if (!gapStyle.IsNormal()) {
5549 auto subgridExtent = subgridAxis == eLogicalAxisBlock
5550 ? subgrid->mGridRowEnd
5551 : subgrid->mGridColEnd;
5552 if (subgridExtent > 1) {
5553 nscoord subgridGap =
5554 nsLayoutUtils::ResolveGapToLength(gapStyle, NS_UNCONSTRAINEDSIZE);
5555 auto& tracks =
5556 aAxis == eLogicalAxisBlock ? aState.mRows : aState.mCols;
5557 auto gapDelta = subgridGap - tracks.mGridGap;
5558 if (!itemEdgeBits) {
5559 extraMargin += gapDelta;
5560 } else {
5561 extraMargin += gapDelta / 2;
5568 PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
5569 nscoord size = nsLayoutUtils::IntrinsicForAxis(
5570 axis, aRC, child, aConstraint, aPercentageBasis,
5571 aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED, aMinSizeClamp);
5572 auto childWM = child->GetWritingMode();
5573 const bool isOrthogonal = childWM.IsOrthogonalTo(aCBWM);
5574 auto childAxis = isOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
5575 if (size == NS_INTRINSIC_ISIZE_UNKNOWN && childAxis == eLogicalAxisBlock) {
5576 // We need to reflow the child to find its BSize contribution.
5577 // XXX this will give mostly correct results for now (until bug 1174569).
5578 nscoord availISize = INFINITE_ISIZE_COORD;
5579 nscoord availBSize = NS_UNCONSTRAINEDSIZE;
5580 // The next two variables are MinSizeClamp values in the child's axes.
5581 nscoord iMinSizeClamp = NS_MAXSIZE;
5582 nscoord bMinSizeClamp = NS_MAXSIZE;
5583 LogicalSize cbSize(childWM, 0, NS_UNCONSTRAINEDSIZE);
5584 // Below, we try to resolve the child's grid-area size in its inline-axis
5585 // to use as the CB/Available size in the MeasuringReflow that follows.
5586 if (child->GetParent() != aState.mFrame) {
5587 // This item is a child of a subgrid descendant.
5588 auto* subgridFrame =
5589 static_cast<nsGridContainerFrame*>(child->GetParent());
5590 MOZ_ASSERT(subgridFrame->IsGridContainerFrame());
5591 auto* uts = subgridFrame->GetProperty(UsedTrackSizes::Prop());
5592 if (!uts) {
5593 uts = new UsedTrackSizes();
5594 subgridFrame->SetProperty(UsedTrackSizes::Prop(), uts);
5596 // The grid-item's inline-axis as expressed in the subgrid's WM.
5597 auto subgridAxis = childWM.IsOrthogonalTo(subgridFrame->GetWritingMode())
5598 ? eLogicalAxisBlock
5599 : eLogicalAxisInline;
5600 uts->ResolveTrackSizesForAxis(subgridFrame, subgridAxis, *aRC);
5601 if (uts->mCanResolveLineRangeSize[subgridAxis]) {
5602 auto* subgrid =
5603 subgridFrame->GetProperty(nsGridContainerFrame::Subgrid::Prop());
5604 const GridItemInfo* originalItem = nullptr;
5605 for (const auto& item : subgrid->mGridItems) {
5606 if (item.mFrame == child) {
5607 originalItem = &item;
5608 break;
5611 MOZ_ASSERT(originalItem, "huh?");
5612 const auto& range = originalItem->mArea.LineRangeForAxis(subgridAxis);
5613 nscoord pos, sz;
5614 range.ToPositionAndLength(uts->mSizes[subgridAxis], &pos, &sz);
5615 if (childWM.IsOrthogonalTo(subgridFrame->GetWritingMode())) {
5616 availBSize = sz;
5617 cbSize.BSize(childWM) = sz;
5618 if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
5619 bMinSizeClamp = sz;
5621 } else {
5622 availISize = sz;
5623 cbSize.ISize(childWM) = sz;
5624 if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
5625 iMinSizeClamp = sz;
5629 } else if (aState.mCols.mCanResolveLineRangeSize) {
5630 nscoord sz = aState.mCols.ResolveSize(aGridItem.mArea.mCols);
5631 if (isOrthogonal) {
5632 availBSize = sz;
5633 cbSize.BSize(childWM) = sz;
5634 if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
5635 bMinSizeClamp = sz;
5637 } else {
5638 availISize = sz;
5639 cbSize.ISize(childWM) = sz;
5640 if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
5641 iMinSizeClamp = sz;
5645 if (isOrthogonal == (aAxis == eLogicalAxisInline)) {
5646 bMinSizeClamp = aMinSizeClamp;
5647 } else {
5648 iMinSizeClamp = aMinSizeClamp;
5650 LogicalSize availableSize(childWM, availISize, availBSize);
5651 size = ::MeasuringReflow(child, aState.mReflowInput, aRC, availableSize,
5652 cbSize, iMinSizeClamp, bMinSizeClamp);
5653 size += child->GetLogicalUsedMargin(childWM).BStartEnd(childWM);
5654 nscoord overflow = size - aMinSizeClamp;
5655 if (MOZ_UNLIKELY(overflow > 0)) {
5656 nscoord contentSize = child->ContentBSize(childWM);
5657 nscoord newContentSize = std::max(nscoord(0), contentSize - overflow);
5658 // XXXmats deal with percentages better, see bug 1300369 comment 27.
5659 size -= contentSize - newContentSize;
5662 MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
5663 "baseline offset should be non-negative at this point");
5664 MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
5665 aGridItem.mBaselineOffset[aAxis] == nscoord(0),
5666 "baseline offset should be zero when not baseline-aligned");
5667 size += aGridItem.mBaselineOffset[aAxis];
5668 size += extraMargin;
5669 return std::max(size, 0);
5672 struct CachedIntrinsicSizes {
5673 Maybe<nscoord> mMinSize;
5674 Maybe<nscoord> mMinContentContribution;
5675 Maybe<nscoord> mMaxContentContribution;
5677 // The item's percentage basis for intrinsic sizing purposes.
5678 Maybe<LogicalSize> mPercentageBasis;
5680 // "if the grid item spans only grid tracks that have a fixed max track
5681 // sizing function, its automatic minimum size in that dimension is
5682 // further clamped to less than or equal to the size necessary to fit its
5683 // margin box within the resulting grid area (flooring at zero)"
5684 // https://drafts.csswg.org/css-grid/#min-size-auto
5685 // This is the clamp value to use for that:
5686 nscoord mMinSizeClamp = NS_MAXSIZE;
5689 static nscoord MinContentContribution(const GridItemInfo& aGridItem,
5690 const GridReflowInput& aState,
5691 gfxContext* aRC, WritingMode aCBWM,
5692 LogicalAxis aAxis,
5693 CachedIntrinsicSizes* aCache) {
5694 if (aCache->mMinContentContribution.isSome()) {
5695 return aCache->mMinContentContribution.value();
5697 if (aCache->mPercentageBasis.isNothing()) {
5698 aCache->mPercentageBasis.emplace(
5699 aState.PercentageBasisFor(aAxis, aGridItem));
5701 nscoord s = ContentContribution(
5702 aGridItem, aState, aRC, aCBWM, aAxis, aCache->mPercentageBasis,
5703 IntrinsicISizeType::MinISize, aCache->mMinSizeClamp);
5704 aCache->mMinContentContribution.emplace(s);
5705 return s;
5708 static nscoord MaxContentContribution(const GridItemInfo& aGridItem,
5709 const GridReflowInput& aState,
5710 gfxContext* aRC, WritingMode aCBWM,
5711 LogicalAxis aAxis,
5712 CachedIntrinsicSizes* aCache) {
5713 if (aCache->mMaxContentContribution.isSome()) {
5714 return aCache->mMaxContentContribution.value();
5716 if (aCache->mPercentageBasis.isNothing()) {
5717 aCache->mPercentageBasis.emplace(
5718 aState.PercentageBasisFor(aAxis, aGridItem));
5720 nscoord s = ContentContribution(
5721 aGridItem, aState, aRC, aCBWM, aAxis, aCache->mPercentageBasis,
5722 IntrinsicISizeType::PrefISize, aCache->mMinSizeClamp);
5723 aCache->mMaxContentContribution.emplace(s);
5724 return s;
5727 // Computes the min-size contribution for a grid item, as defined at
5728 // https://drafts.csswg.org/css-grid/#min-size-contribution
5729 static nscoord MinSize(const GridItemInfo& aGridItem,
5730 const GridReflowInput& aState, gfxContext* aRC,
5731 WritingMode aCBWM, LogicalAxis aAxis,
5732 CachedIntrinsicSizes* aCache) {
5733 if (aCache->mMinSize.isSome()) {
5734 return aCache->mMinSize.value();
5736 nsIFrame* child = aGridItem.mFrame;
5737 PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
5738 const nsStylePosition* stylePos = child->StylePosition();
5739 StyleSize sizeStyle =
5740 axis == eAxisHorizontal ? stylePos->mWidth : stylePos->mHeight;
5742 auto ourInlineAxis = child->GetWritingMode().PhysicalAxis(eLogicalAxisInline);
5743 // max-content and min-content should behave as initial value in block axis.
5744 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
5745 // for block size dimension on sizing properties (e.g. height), so we
5746 // treat it as `auto`.
5747 if (axis != ourInlineAxis && sizeStyle.BehavesLikeInitialValueOnBlockAxis()) {
5748 sizeStyle = StyleSize::Auto();
5751 if (!sizeStyle.IsAuto() && !sizeStyle.HasPercent()) {
5752 nscoord s =
5753 MinContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, aCache);
5754 aCache->mMinSize.emplace(s);
5755 return s;
5758 if (aCache->mPercentageBasis.isNothing()) {
5759 aCache->mPercentageBasis.emplace(
5760 aState.PercentageBasisFor(aAxis, aGridItem));
5763 // https://drafts.csswg.org/css-grid/#min-size-auto
5764 // This calculates the min-content contribution from either a definite
5765 // min-width (or min-height depending on aAxis), or the "specified /
5766 // transferred size" for min-width:auto if overflow == visible (as min-width:0
5767 // otherwise), or NS_UNCONSTRAINEDSIZE for other min-width intrinsic values
5768 // (which results in always taking the "content size" part below).
5769 MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
5770 "baseline offset should be non-negative at this point");
5771 MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
5772 aGridItem.mBaselineOffset[aAxis] == nscoord(0),
5773 "baseline offset should be zero when not baseline-aligned");
5774 nscoord sz = aGridItem.mBaselineOffset[aAxis] +
5775 nsLayoutUtils::MinSizeContributionForAxis(
5776 axis, aRC, child, IntrinsicISizeType::MinISize,
5777 *aCache->mPercentageBasis);
5778 const StyleSize& style =
5779 axis == eAxisHorizontal ? stylePos->mMinWidth : stylePos->mMinHeight;
5780 // max-content and min-content should behave as initial value in block axis.
5781 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
5782 // for block size dimension on sizing properties (e.g. height), so we
5783 // treat it as `auto`.
5784 const bool inInlineAxis = axis == ourInlineAxis;
5785 const bool isAuto =
5786 style.IsAuto() ||
5787 (!inInlineAxis && style.BehavesLikeInitialValueOnBlockAxis());
5788 if ((inInlineAxis && nsIFrame::ToExtremumLength(style)) ||
5789 (isAuto && !child->StyleDisplay()->IsScrollableOverflow())) {
5790 // Now calculate the "content size" part and return whichever is smaller.
5791 MOZ_ASSERT(isAuto || sz == NS_UNCONSTRAINEDSIZE);
5792 sz = std::min(sz, ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
5793 aCache->mPercentageBasis,
5794 IntrinsicISizeType::MinISize,
5795 aCache->mMinSizeClamp,
5796 nsLayoutUtils::MIN_INTRINSIC_ISIZE));
5798 aCache->mMinSize.emplace(sz);
5799 return sz;
5802 void nsGridContainerFrame::Tracks::CalculateSizes(
5803 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
5804 const TrackSizingFunctions& aFunctions, nscoord aContentBoxSize,
5805 LineRange GridArea::*aRange, SizingConstraint aConstraint) {
5806 nscoord percentageBasis = aContentBoxSize;
5807 if (percentageBasis == NS_UNCONSTRAINEDSIZE) {
5808 percentageBasis = 0;
5810 InitializeItemBaselines(aState, aGridItems);
5811 ResolveIntrinsicSize(aState, aGridItems, aFunctions, aRange, percentageBasis,
5812 aConstraint);
5813 if (aConstraint != SizingConstraint::MinContent) {
5814 nscoord freeSpace = aContentBoxSize;
5815 if (freeSpace != NS_UNCONSTRAINEDSIZE) {
5816 freeSpace -= SumOfGridGaps();
5818 DistributeFreeSpace(freeSpace);
5819 StretchFlexibleTracks(aState, aGridItems, aFunctions, freeSpace);
5823 TrackSize::StateBits nsGridContainerFrame::Tracks::StateBitsForRange(
5824 const LineRange& aRange) const {
5825 MOZ_ASSERT(!aRange.IsAuto(), "must have a definite range");
5826 TrackSize::StateBits state = TrackSize::StateBits{0};
5827 for (auto i : aRange.Range()) {
5828 state |= mSizes[i].mState;
5830 return state;
5833 static void AddSubgridContribution(TrackSize& aSize,
5834 nscoord aMarginBorderPadding) {
5835 if (aSize.mState & TrackSize::eIntrinsicMinSizing) {
5836 aSize.mBase = std::max(aSize.mBase, aMarginBorderPadding);
5837 aSize.mLimit = std::max(aSize.mLimit, aSize.mBase);
5839 // XXX maybe eFlexMaxSizing too?
5840 // (once we implement https://github.com/w3c/csswg-drafts/issues/2177)
5841 if (aSize.mState &
5842 (TrackSize::eIntrinsicMaxSizing | TrackSize::eFitContent)) {
5843 aSize.mLimit = std::max(aSize.mLimit, aMarginBorderPadding);
5847 bool nsGridContainerFrame::Tracks::ResolveIntrinsicSizeForNonSpanningItems(
5848 GridReflowInput& aState, const TrackSizingFunctions& aFunctions,
5849 nscoord aPercentageBasis, SizingConstraint aConstraint,
5850 const LineRange& aRange, const GridItemInfo& aGridItem) {
5851 gfxContext* rc = &aState.mRenderingContext;
5852 WritingMode wm = aState.mWM;
5853 CachedIntrinsicSizes cache;
5854 TrackSize& sz = mSizes[aRange.mStart];
5856 // min sizing
5857 if (sz.mState & TrackSize::eAutoMinSizing) {
5858 nscoord s;
5859 // Check if we need to apply "Automatic Minimum Size" and cache it.
5860 if (aGridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) {
5861 aGridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize;
5862 // Clamp it if it's spanning a definite track max-sizing function.
5863 if (TrackSize::IsDefiniteMaxSizing(sz.mState)) {
5864 cache.mMinSizeClamp = aFunctions.MaxSizingFor(aRange.mStart)
5865 .AsBreadth()
5866 .Resolve(aPercentageBasis);
5867 aGridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
5869 if (aConstraint != SizingConstraint::MaxContent) {
5870 s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5871 } else {
5872 s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5874 } else {
5875 s = MinSize(aGridItem, aState, rc, wm, mAxis, &cache);
5877 sz.mBase = std::max(sz.mBase, s);
5878 } else if (sz.mState & TrackSize::eMinContentMinSizing) {
5879 auto s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5880 sz.mBase = std::max(sz.mBase, s);
5881 } else if (sz.mState & TrackSize::eMaxContentMinSizing) {
5882 auto s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5883 sz.mBase = std::max(sz.mBase, s);
5886 // max sizing
5887 if (sz.mState & TrackSize::eMinContentMaxSizing) {
5888 auto s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5889 if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
5890 sz.mLimit = s;
5891 } else {
5892 sz.mLimit = std::max(sz.mLimit, s);
5894 } else if (sz.mState &
5895 (TrackSize::eAutoMaxSizing | TrackSize::eMaxContentMaxSizing)) {
5896 auto s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
5897 if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
5898 sz.mLimit = s;
5899 } else {
5900 sz.mLimit = std::max(sz.mLimit, s);
5902 if (MOZ_UNLIKELY(sz.mState & TrackSize::eFitContent)) {
5903 // Clamp mLimit to the fit-content() size, for §12.5.1.
5904 nscoord fitContentClamp = aFunctions.SizingFor(aRange.mStart)
5905 .AsFitContent()
5906 .AsBreadth()
5907 .Resolve(aPercentageBasis);
5908 sz.mLimit = std::min(sz.mLimit, fitContentClamp);
5912 if (sz.mLimit < sz.mBase) {
5913 sz.mLimit = sz.mBase;
5916 return sz.mState & TrackSize::eFlexMaxSizing;
5919 void nsGridContainerFrame::Tracks::CalculateItemBaselines(
5920 nsTArray<ItemBaselineData>& aBaselineItems,
5921 BaselineSharingGroup aBaselineGroup) {
5922 if (aBaselineItems.IsEmpty()) {
5923 return;
5926 // Sort the collected items on their baseline track.
5927 std::sort(aBaselineItems.begin(), aBaselineItems.end(),
5928 ItemBaselineData::IsBaselineTrackLessThan);
5930 MOZ_ASSERT(mSizes.Length() > 0, "having an item implies at least one track");
5931 const uint32_t lastTrack = mSizes.Length() - 1;
5932 nscoord maxBaseline = 0;
5933 nscoord maxDescent = 0;
5934 uint32_t currentTrack = kAutoLine; // guaranteed to not match any item
5935 uint32_t trackStartIndex = 0;
5936 for (uint32_t i = 0, len = aBaselineItems.Length(); true; ++i) {
5937 // Find the maximum baseline and descent in the current track.
5938 if (i != len) {
5939 const ItemBaselineData& item = aBaselineItems[i];
5940 if (currentTrack == item.mBaselineTrack) {
5941 maxBaseline = std::max(maxBaseline, item.mBaseline);
5942 maxDescent = std::max(maxDescent, item.mSize - item.mBaseline);
5943 continue;
5946 // Iterate the current track again and update the baseline offsets making
5947 // all items baseline-aligned within this group in this track.
5948 for (uint32_t j = trackStartIndex; j < i; ++j) {
5949 const ItemBaselineData& item = aBaselineItems[j];
5950 item.mGridItem->mBaselineOffset[mAxis] = maxBaseline - item.mBaseline;
5951 MOZ_ASSERT(item.mGridItem->mBaselineOffset[mAxis] >= 0);
5953 if (i != 0) {
5954 // Store the size of this baseline-aligned subtree.
5955 mSizes[currentTrack].mBaselineSubtreeSize[aBaselineGroup] =
5956 maxBaseline + maxDescent;
5957 // Record the first(last) baseline for the first(last) track.
5958 if (currentTrack == 0 && aBaselineGroup == BaselineSharingGroup::First) {
5959 mBaseline[aBaselineGroup] = maxBaseline;
5961 if (currentTrack == lastTrack &&
5962 aBaselineGroup == BaselineSharingGroup::Last) {
5963 mBaseline[aBaselineGroup] = maxBaseline;
5966 if (i == len) {
5967 break;
5969 // Initialize data for the next track with baseline-aligned items.
5970 const ItemBaselineData& item = aBaselineItems[i];
5971 currentTrack = item.mBaselineTrack;
5972 trackStartIndex = i;
5973 maxBaseline = item.mBaseline;
5974 maxDescent = item.mSize - item.mBaseline;
5978 void nsGridContainerFrame::Tracks::InitializeItemBaselines(
5979 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems) {
5980 MOZ_ASSERT(!mIsMasonry);
5981 if (aState.mFrame->IsSubgrid(mAxis)) {
5982 // A grid container's subgridded axis doesn't have a baseline.
5983 return;
5986 nsTArray<ItemBaselineData> firstBaselineItems;
5987 nsTArray<ItemBaselineData> lastBaselineItems;
5988 const WritingMode containerWM = aState.mWM;
5989 ComputedStyle* containerStyle = aState.mFrame->Style();
5991 // The physical side of the container's block start side. We use it to match
5992 // against the physical block start side of the child to determine its
5993 // baseline sharing group.
5994 auto containerBlockStartSide =
5995 containerWM.PhysicalSide(MakeLogicalSide(mAxis, eLogicalEdgeStart));
5997 for (GridItemInfo& gridItem : aGridItems) {
5998 if (gridItem.IsSubgrid(mAxis)) {
5999 // A subgrid itself is never baseline-aligned.
6000 continue;
6003 nsIFrame* child = gridItem.mFrame;
6004 uint32_t baselineTrack = kAutoLine;
6005 auto state = ItemState(0);
6006 const auto childWM = child->GetWritingMode();
6008 const bool isOrthogonal = containerWM.IsOrthogonalTo(childWM);
6009 const bool isInlineAxis = mAxis == eLogicalAxisInline; // i.e. columns
6011 // XXX update the line below to include orthogonal grid/table boxes
6012 // XXX since they have baselines in both dimensions. And flexbox with
6013 // XXX reversed main/cross axis?
6014 const bool itemHasBaselineParallelToTrack = isInlineAxis == isOrthogonal;
6015 if (itemHasBaselineParallelToTrack) {
6016 // [align|justify]-self:[last ]baseline.
6017 auto selfAlignment =
6018 isOrthogonal
6019 ? child->StylePosition()->UsedJustifySelf(containerStyle)._0
6020 : child->StylePosition()->UsedAlignSelf(containerStyle)._0;
6021 selfAlignment &= ~StyleAlignFlags::FLAG_BITS;
6022 if (selfAlignment == StyleAlignFlags::BASELINE) {
6023 state |= ItemState::eFirstBaseline | ItemState::eSelfBaseline;
6024 const GridArea& area = gridItem.mArea;
6025 baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
6026 } else if (selfAlignment == StyleAlignFlags::LAST_BASELINE) {
6027 state |= ItemState::eLastBaseline | ItemState::eSelfBaseline;
6028 const GridArea& area = gridItem.mArea;
6029 baselineTrack = (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
6032 // [align|justify]-content:[last ]baseline.
6033 // https://drafts.csswg.org/css-align-3/#baseline-align-content
6034 // "[...] and its computed 'align-self' or 'justify-self' (whichever
6035 // affects its block axis) is 'stretch' or 'self-start' ('self-end').
6036 // For this purpose, the 'start', 'end', 'flex-start', and 'flex-end'
6037 // values of 'align-self' are treated as either 'self-start' or
6038 // 'self-end', whichever they end up equivalent to.
6039 auto alignContent = child->StylePosition()->mAlignContent.primary;
6040 alignContent &= ~StyleAlignFlags::FLAG_BITS;
6041 if (alignContent == StyleAlignFlags::BASELINE ||
6042 alignContent == StyleAlignFlags::LAST_BASELINE) {
6043 const auto selfAlignEdge = alignContent == StyleAlignFlags::BASELINE
6044 ? StyleAlignFlags::SELF_START
6045 : StyleAlignFlags::SELF_END;
6046 bool validCombo = selfAlignment == StyleAlignFlags::NORMAL ||
6047 selfAlignment == StyleAlignFlags::STRETCH ||
6048 selfAlignment == selfAlignEdge;
6049 if (!validCombo) {
6050 // We're doing alignment in the axis that's orthogonal to mAxis here.
6051 LogicalAxis alignAxis = GetOrthogonalAxis(mAxis);
6052 // |sameSide| is true if the container's start side in this axis is
6053 // the same as the child's start side, in the child's parallel axis.
6054 bool sameSide =
6055 containerWM.ParallelAxisStartsOnSameSide(alignAxis, childWM);
6056 if (selfAlignment == StyleAlignFlags::LEFT) {
6057 selfAlignment = !isInlineAxis || containerWM.IsBidiLTR()
6058 ? StyleAlignFlags::START
6059 : StyleAlignFlags::END;
6060 } else if (selfAlignment == StyleAlignFlags::RIGHT) {
6061 selfAlignment = isInlineAxis && containerWM.IsBidiLTR()
6062 ? StyleAlignFlags::END
6063 : StyleAlignFlags::START;
6066 if (selfAlignment == StyleAlignFlags::START ||
6067 selfAlignment == StyleAlignFlags::FLEX_START) {
6068 validCombo =
6069 sameSide == (alignContent == StyleAlignFlags::BASELINE);
6070 } else if (selfAlignment == StyleAlignFlags::END ||
6071 selfAlignment == StyleAlignFlags::FLEX_END) {
6072 validCombo =
6073 sameSide == (alignContent == StyleAlignFlags::LAST_BASELINE);
6076 if (validCombo) {
6077 const GridArea& area = gridItem.mArea;
6078 if (alignContent == StyleAlignFlags::BASELINE) {
6079 state |= ItemState::eFirstBaseline | ItemState::eContentBaseline;
6080 baselineTrack =
6081 isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
6082 } else if (alignContent == StyleAlignFlags::LAST_BASELINE) {
6083 state |= ItemState::eLastBaseline | ItemState::eContentBaseline;
6084 baselineTrack =
6085 (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
6091 if (state & ItemState::eIsBaselineAligned) {
6092 // The item is baseline aligned, so calculate the baseline sharing group.
6093 // <https://drafts.csswg.org/css-align-3/#baseline-terms>
6094 BaselineSharingGroup baselineAlignment =
6095 (state & ItemState::eFirstBaseline) ? BaselineSharingGroup::First
6096 : BaselineSharingGroup::Last;
6098 BaselineSharingGroup baselineSharingGroup = [&]() {
6100 auto childAxis = isOrthogonal ? GetOrthogonalAxis(mAxis) : mAxis;
6101 auto childBlockStartSide = childWM.PhysicalSide(
6102 MakeLogicalSide(childAxis, eLogicalEdgeStart));
6103 bool isFirstBaseline = (state & ItemState::eFirstBaseline) != 0;
6104 const bool containerAndChildHasEqualBaselineSide =
6105 containerBlockStartSide == childBlockStartSide;
6107 return isFirstBaseline == containerAndChildHasEqualBaselineSide
6108 ? BaselineSharingGroup::First
6109 : BaselineSharingGroup::Last;
6111 }();
6113 // XXXmats if |child| is a descendant of a subgrid then the metrics
6114 // below needs to account for the accumulated MPB somehow...
6116 // XXX available size issue
6117 LogicalSize avail(childWM, INFINITE_ISIZE_COORD, NS_UNCONSTRAINEDSIZE);
6118 auto* rc = &aState.mRenderingContext;
6119 // XXX figure out if we can avoid/merge this reflow with the main reflow.
6120 // XXX (after bug 1174569 is sorted out)
6122 // XXX How should we handle percentage padding here? (bug 1330866)
6123 // XXX (see ::ContentContribution and how it deals with percentages)
6124 // XXX What if the true baseline after line-breaking differs from this
6125 // XXX hypothetical baseline based on an infinite inline size?
6126 // XXX Maybe we should just call ::ContentContribution here instead?
6127 // XXX For now we just pass an unconstrined-bsize CB:
6128 LogicalSize cbSize(childWM, 0, NS_UNCONSTRAINEDSIZE);
6129 ::MeasuringReflow(child, aState.mReflowInput, rc, avail, cbSize);
6131 nsGridContainerFrame* grid = do_QueryFrame(child);
6132 auto frameSize =
6133 isInlineAxis ? child->ISize(containerWM) : child->BSize(containerWM);
6134 auto margin = child->GetLogicalUsedMargin(containerWM);
6135 auto alignSize =
6136 frameSize + (isInlineAxis ? margin.IStartEnd(containerWM)
6137 : margin.BStartEnd(containerWM));
6139 Maybe<nscoord> baseline;
6140 if (grid) {
6141 baseline.emplace((isOrthogonal == isInlineAxis)
6142 ? grid->GetBBaseline(baselineAlignment)
6143 : grid->GetIBaseline(baselineAlignment));
6144 } else {
6145 baseline = child->GetNaturalBaselineBOffset(
6146 childWM, baselineAlignment, BaselineExportContext::Other);
6148 if (!baseline) {
6149 // If baseline alignment is specified on a grid item whose size in
6150 // that axis depends on the size of an intrinsically-sized track, that
6151 // item does not participate in baseline alignment, and instead uses
6152 // its fallback alignment as if that were originally specified.
6153 // https://drafts.csswg.org/css-grid-1/#row-align
6155 // Check if the item crosses any tracks that are intrinsically sized.
6156 auto range = gridItem.mArea.LineRangeForAxis(mAxis).Range();
6157 auto isTrackAutoSize =
6158 std::find_if(range.begin(), range.end(), [&](auto track) {
6159 constexpr auto intrinsicSizeFlags =
6160 TrackSize::eIntrinsicMinSizing |
6161 TrackSize::eIntrinsicMaxSizing | TrackSize::eFitContent |
6162 TrackSize::eFlexMaxSizing;
6163 return (mSizes[track].mState & intrinsicSizeFlags) != 0;
6164 }) != range.end();
6166 // If either the track or the item is not auto sized, then the item
6167 // participates in baseline alignment.
6168 if (!isTrackAutoSize ||
6169 !gridItem.IsBSizeDependentOnContainerSize(containerWM)) {
6170 baseline.emplace(Baseline::SynthesizeBOffsetFromBorderBox(
6171 child, containerWM, baselineAlignment));
6176 if (baseline) {
6177 nscoord finalBaseline = *baseline;
6178 NS_ASSERTION(finalBaseline != NS_INTRINSIC_ISIZE_UNKNOWN,
6179 "about to use an unknown baseline");
6181 nscoord marginAdjust = 0;
6182 if (baselineSharingGroup == BaselineSharingGroup::First) {
6183 marginAdjust = isInlineAxis ? margin.IStart(containerWM)
6184 : margin.BStart(containerWM);
6185 } else {
6186 marginAdjust = isInlineAxis ? margin.IEnd(containerWM)
6187 : margin.BEnd(containerWM);
6189 // This flag is used in ::AlignSelf(...) to check whether the item is
6190 // last baseline aligned, but this flag should go away.
6191 state |= GridItemInfo::eEndSideBaseline;
6193 finalBaseline += marginAdjust;
6195 auto& baselineItems =
6196 (baselineSharingGroup == BaselineSharingGroup::First)
6197 ? firstBaselineItems
6198 : lastBaselineItems;
6199 baselineItems.AppendElement(ItemBaselineData{
6200 baselineTrack, finalBaseline, alignSize, &gridItem});
6201 } else {
6202 state &= ~ItemState::eAllBaselineBits;
6206 MOZ_ASSERT(
6207 (state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) !=
6208 (ItemState::eFirstBaseline | ItemState::eLastBaseline),
6209 "first/last baseline bits are mutually exclusive");
6210 MOZ_ASSERT(
6211 (state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)) !=
6212 (ItemState::eSelfBaseline | ItemState::eContentBaseline),
6213 "*-self and *-content baseline bits are mutually exclusive");
6214 MOZ_ASSERT(
6215 !(state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) ==
6216 !(state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)),
6217 "first/last bit requires self/content bit and vice versa");
6219 gridItem.mState[mAxis] |= state;
6220 gridItem.mBaselineOffset[mAxis] = nscoord(0);
6223 if (firstBaselineItems.IsEmpty() && lastBaselineItems.IsEmpty()) {
6224 return;
6227 // TODO: CSS Align spec issue - how to align a baseline subtree in a track?
6228 // https://lists.w3.org/Archives/Public/www-style/2016May/0141.html
6229 mBaselineSubtreeAlign[BaselineSharingGroup::First] = StyleAlignFlags::START;
6230 mBaselineSubtreeAlign[BaselineSharingGroup::Last] = StyleAlignFlags::END;
6232 CalculateItemBaselines(firstBaselineItems, BaselineSharingGroup::First);
6233 CalculateItemBaselines(lastBaselineItems, BaselineSharingGroup::Last);
6236 // TODO: we store the wrong baseline group offset in some cases (bug 1632200)
6237 void nsGridContainerFrame::Tracks::InitializeItemBaselinesInMasonryAxis(
6238 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
6239 BaselineAlignmentSet aSet, const nsSize& aContainerSize,
6240 nsTArray<nscoord>& aTrackSizes,
6241 nsTArray<ItemBaselineData>& aFirstBaselineItems,
6242 nsTArray<ItemBaselineData>& aLastBaselineItems) {
6243 MOZ_ASSERT(mIsMasonry);
6244 WritingMode wm = aState.mWM;
6245 ComputedStyle* containerSC = aState.mFrame->Style();
6246 for (GridItemInfo& gridItem : aGridItems) {
6247 if (gridItem.IsSubgrid(mAxis)) {
6248 // A subgrid itself is never baseline-aligned.
6249 continue;
6251 const auto& area = gridItem.mArea;
6252 if (aSet.mItemSet == BaselineAlignmentSet::LastItems) {
6253 // NOTE: eIsLastItemInMasonryTrack is set also if the item is the ONLY
6254 // item in its track; the eIsBaselineAligned check excludes it though
6255 // since it participates in the start baseline groups in that case.
6257 // XXX what if it's the only item in THAT baseline group?
6258 // XXX should it participate in the last-item group instead then
6259 // if there are more baseline-aligned items there?
6260 if (!(gridItem.mState[mAxis] & ItemState::eIsLastItemInMasonryTrack) ||
6261 (gridItem.mState[mAxis] & ItemState::eIsBaselineAligned)) {
6262 continue;
6264 } else {
6265 if (area.LineRangeForAxis(mAxis).mStart > 0 ||
6266 (gridItem.mState[mAxis] & ItemState::eIsBaselineAligned)) {
6267 continue;
6270 auto trackAlign =
6271 aState.mGridStyle
6272 ->UsedTracksAlignment(
6273 mAxis, area.LineRangeForAxis(GetOrthogonalAxis(mAxis)).mStart)
6274 .primary;
6275 if (!aSet.MatchTrackAlignment(trackAlign)) {
6276 continue;
6279 nsIFrame* child = gridItem.mFrame;
6280 uint32_t baselineTrack = kAutoLine;
6281 auto state = ItemState(0);
6282 auto childWM = child->GetWritingMode();
6283 const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
6284 const bool isInlineAxis = mAxis == eLogicalAxisInline; // i.e. columns
6285 // XXX update the line below to include orthogonal grid/table boxes
6286 // XXX since they have baselines in both dimensions. And flexbox with
6287 // XXX reversed main/cross axis?
6288 const bool itemHasBaselineParallelToTrack = isInlineAxis == isOrthogonal;
6289 if (itemHasBaselineParallelToTrack) {
6290 const auto* pos = child->StylePosition();
6291 // [align|justify]-self:[last ]baseline.
6292 auto selfAlignment = pos->UsedSelfAlignment(mAxis, containerSC);
6293 selfAlignment &= ~StyleAlignFlags::FLAG_BITS;
6294 if (selfAlignment == StyleAlignFlags::BASELINE) {
6295 state |= ItemState::eFirstBaseline | ItemState::eSelfBaseline;
6296 baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
6297 } else if (selfAlignment == StyleAlignFlags::LAST_BASELINE) {
6298 state |= ItemState::eLastBaseline | ItemState::eSelfBaseline;
6299 baselineTrack = (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
6300 } else {
6301 // [align|justify]-content:[last ]baseline.
6302 auto childAxis = isOrthogonal ? GetOrthogonalAxis(mAxis) : mAxis;
6303 auto alignContent = pos->UsedContentAlignment(childAxis).primary;
6304 alignContent &= ~StyleAlignFlags::FLAG_BITS;
6305 if (alignContent == StyleAlignFlags::BASELINE) {
6306 state |= ItemState::eFirstBaseline | ItemState::eContentBaseline;
6307 baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
6308 } else if (alignContent == StyleAlignFlags::LAST_BASELINE) {
6309 state |= ItemState::eLastBaseline | ItemState::eContentBaseline;
6310 baselineTrack =
6311 (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
6316 if (state & ItemState::eIsBaselineAligned) {
6317 // XXXmats if |child| is a descendant of a subgrid then the metrics
6318 // below needs to account for the accumulated MPB somehow...
6320 nscoord baseline;
6321 nsGridContainerFrame* grid = do_QueryFrame(child);
6322 if (state & ItemState::eFirstBaseline) {
6323 if (grid) {
6324 if (isOrthogonal == isInlineAxis) {
6325 baseline = grid->GetBBaseline(BaselineSharingGroup::First);
6326 } else {
6327 baseline = grid->GetIBaseline(BaselineSharingGroup::First);
6330 if (grid || nsLayoutUtils::GetFirstLineBaseline(wm, child, &baseline)) {
6331 NS_ASSERTION(baseline != NS_INTRINSIC_ISIZE_UNKNOWN,
6332 "about to use an unknown baseline");
6333 auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
6334 nscoord alignSize;
6335 LogicalPoint pos =
6336 child->GetLogicalNormalPosition(wm, aContainerSize);
6337 baseline += pos.Pos(mAxis, wm);
6338 if (aSet.mTrackAlignmentSet == BaselineAlignmentSet::EndStretch) {
6339 state |= ItemState::eEndSideBaseline;
6340 // Convert to distance from the track end.
6341 baseline =
6342 aTrackSizes[gridItem.mArea
6343 .LineRangeForAxis(GetOrthogonalAxis(mAxis))
6344 .mStart] -
6345 baseline;
6347 alignSize = frameSize;
6348 aFirstBaselineItems.AppendElement(ItemBaselineData(
6349 {baselineTrack, baseline, alignSize, &gridItem}));
6350 } else {
6351 state &= ~ItemState::eAllBaselineBits;
6353 } else {
6354 if (grid) {
6355 if (isOrthogonal == isInlineAxis) {
6356 baseline = grid->GetBBaseline(BaselineSharingGroup::Last);
6357 } else {
6358 baseline = grid->GetIBaseline(BaselineSharingGroup::Last);
6361 if (grid || nsLayoutUtils::GetLastLineBaseline(wm, child, &baseline)) {
6362 NS_ASSERTION(baseline != NS_INTRINSIC_ISIZE_UNKNOWN,
6363 "about to use an unknown baseline");
6364 auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
6365 auto m = child->GetLogicalUsedMargin(wm);
6366 if (!grid &&
6367 aSet.mTrackAlignmentSet == BaselineAlignmentSet::EndStretch) {
6368 // Convert to distance from border-box end.
6369 state |= ItemState::eEndSideBaseline;
6370 LogicalPoint pos =
6371 child->GetLogicalNormalPosition(wm, aContainerSize);
6372 baseline += pos.Pos(mAxis, wm);
6373 baseline =
6374 aTrackSizes[gridItem.mArea
6375 .LineRangeForAxis(GetOrthogonalAxis(mAxis))
6376 .mStart] -
6377 baseline;
6378 } else if (grid && aSet.mTrackAlignmentSet ==
6379 BaselineAlignmentSet::StartStretch) {
6380 // Convert to distance from border-box start.
6381 baseline = frameSize - baseline;
6383 if (aSet.mItemSet == BaselineAlignmentSet::LastItems &&
6384 aSet.mTrackAlignmentSet == BaselineAlignmentSet::StartStretch) {
6385 LogicalPoint pos =
6386 child->GetLogicalNormalPosition(wm, aContainerSize);
6387 baseline += pos.B(wm);
6389 if (aSet.mTrackAlignmentSet == BaselineAlignmentSet::EndStretch) {
6390 state |= ItemState::eEndSideBaseline;
6392 auto descent =
6393 baseline + ((state & ItemState::eEndSideBaseline)
6394 ? (isInlineAxis ? m.IEnd(wm) : m.BEnd(wm))
6395 : (isInlineAxis ? m.IStart(wm) : m.BStart(wm)));
6396 auto alignSize =
6397 frameSize + (isInlineAxis ? m.IStartEnd(wm) : m.BStartEnd(wm));
6398 aLastBaselineItems.AppendElement(
6399 ItemBaselineData({baselineTrack, descent, alignSize, &gridItem}));
6400 } else {
6401 state &= ~ItemState::eAllBaselineBits;
6405 MOZ_ASSERT(
6406 (state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) !=
6407 (ItemState::eFirstBaseline | ItemState::eLastBaseline),
6408 "first/last baseline bits are mutually exclusive");
6409 MOZ_ASSERT(
6410 (state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)) !=
6411 (ItemState::eSelfBaseline | ItemState::eContentBaseline),
6412 "*-self and *-content baseline bits are mutually exclusive");
6413 MOZ_ASSERT(
6414 !(state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) ==
6415 !(state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)),
6416 "first/last bit requires self/content bit and vice versa");
6417 gridItem.mState[mAxis] |= state;
6418 gridItem.mBaselineOffset[mAxis] = nscoord(0);
6421 CalculateItemBaselines(aFirstBaselineItems, BaselineSharingGroup::First);
6422 CalculateItemBaselines(aLastBaselineItems, BaselineSharingGroup::Last);
6424 // TODO: make sure the mBaselines (i.e. the baselines we export from
6425 // the grid container) are offset from the correct container edge.
6426 // Also, which of the baselines do we pick to export exactly?
6428 MOZ_ASSERT(aFirstBaselineItems.Length() != 1 ||
6429 aFirstBaselineItems[0].mGridItem->mBaselineOffset[mAxis] == 0,
6430 "a baseline group that contains only one item should not "
6431 "produce a non-zero item baseline offset");
6432 MOZ_ASSERT(aLastBaselineItems.Length() != 1 ||
6433 aLastBaselineItems[0].mGridItem->mBaselineOffset[mAxis] == 0,
6434 "a baseline group that contains only one item should not "
6435 "produce a non-zero item baseline offset");
6438 void nsGridContainerFrame::Tracks::AlignBaselineSubtree(
6439 const GridItemInfo& aGridItem) const {
6440 if (mIsMasonry) {
6441 return;
6443 auto state = aGridItem.mState[mAxis];
6444 if (!(state & ItemState::eIsBaselineAligned)) {
6445 return;
6447 const GridArea& area = aGridItem.mArea;
6448 int32_t baselineTrack;
6449 const bool isFirstBaseline = state & ItemState::eFirstBaseline;
6450 if (isFirstBaseline) {
6451 baselineTrack =
6452 mAxis == eLogicalAxisBlock ? area.mRows.mStart : area.mCols.mStart;
6453 } else {
6454 baselineTrack =
6455 (mAxis == eLogicalAxisBlock ? area.mRows.mEnd : area.mCols.mEnd) - 1;
6457 const TrackSize& sz = mSizes[baselineTrack];
6458 auto baselineGroup = isFirstBaseline ? BaselineSharingGroup::First
6459 : BaselineSharingGroup::Last;
6460 nscoord delta = sz.mBase - sz.mBaselineSubtreeSize[baselineGroup];
6461 const auto subtreeAlign = mBaselineSubtreeAlign[baselineGroup];
6462 if (subtreeAlign == StyleAlignFlags::START) {
6463 if (state & ItemState::eLastBaseline) {
6464 aGridItem.mBaselineOffset[mAxis] += delta;
6466 } else if (subtreeAlign == StyleAlignFlags::END) {
6467 if (isFirstBaseline) {
6468 aGridItem.mBaselineOffset[mAxis] += delta;
6470 } else if (subtreeAlign == StyleAlignFlags::CENTER) {
6471 aGridItem.mBaselineOffset[mAxis] += delta / 2;
6472 } else {
6473 MOZ_ASSERT_UNREACHABLE("unexpected baseline subtree alignment");
6477 template <nsGridContainerFrame::Tracks::TrackSizingPhase phase>
6478 bool nsGridContainerFrame::Tracks::GrowSizeForSpanningItems(
6479 nsTArray<SpanningItemData>::iterator aIter,
6480 nsTArray<SpanningItemData>::iterator aIterEnd, nsTArray<uint32_t>& aTracks,
6481 nsTArray<TrackSize>& aPlan, nsTArray<TrackSize>& aItemPlan,
6482 TrackSize::StateBits aSelector, const FitContentClamper& aFitContentClamper,
6483 bool aNeedInfinitelyGrowableFlag) {
6484 constexpr bool isMaxSizingPhase =
6485 phase == TrackSizingPhase::IntrinsicMaximums ||
6486 phase == TrackSizingPhase::MaxContentMaximums;
6487 bool needToUpdateSizes = false;
6488 InitializePlan<phase>(aPlan);
6489 for (; aIter != aIterEnd; ++aIter) {
6490 const SpanningItemData& item = *aIter;
6491 if (!(item.mState & aSelector)) {
6492 continue;
6494 if (isMaxSizingPhase) {
6495 for (auto i : item.mLineRange.Range()) {
6496 aPlan[i].mState |= TrackSize::eModified;
6499 nscoord space = item.SizeContributionForPhase<phase>();
6500 if (space <= 0) {
6501 continue;
6503 aTracks.ClearAndRetainStorage();
6504 space = CollectGrowable<phase>(space, item.mLineRange, aSelector, aTracks);
6505 if (space > 0) {
6506 DistributeToTrackSizes<phase>(space, aPlan, aItemPlan, aTracks, aSelector,
6507 aFitContentClamper);
6508 needToUpdateSizes = true;
6511 if (isMaxSizingPhase) {
6512 needToUpdateSizes = true;
6514 if (needToUpdateSizes) {
6515 CopyPlanToSize<phase>(aPlan, aNeedInfinitelyGrowableFlag);
6517 return needToUpdateSizes;
6520 void nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
6521 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
6522 const TrackSizingFunctions& aFunctions, LineRange GridArea::*aRange,
6523 nscoord aPercentageBasis, SizingConstraint aConstraint) {
6524 // Resolve Intrinsic Track Sizes
6525 // https://w3c.github.io/csswg-drafts/css-grid-1/#algo-content
6526 // We're also setting eIsFlexing on the item state here to speed up
6527 // FindUsedFlexFraction later.
6529 gfxContext* rc = &aState.mRenderingContext;
6530 WritingMode wm = aState.mWM;
6532 // Data we accumulate when grouping similar sized spans together.
6533 struct PerSpanData {
6534 uint32_t mItemCountWithSameSpan = 0;
6535 TrackSize::StateBits mStateBits = TrackSize::StateBits{0};
6537 AutoTArray<PerSpanData, 16> perSpanData;
6539 nsTArray<SpanningItemData> spanningItems;
6540 uint32_t maxSpan = 0; // max span of items in `spanningItems`.
6542 // Setup track selector for step 3.2:
6543 const auto contentBasedMinSelector =
6544 aConstraint == SizingConstraint::MinContent
6545 ? TrackSize::eIntrinsicMinSizing
6546 : TrackSize::eMinOrMaxContentMinSizing;
6548 // Setup track selector for step 3.3:
6549 const auto maxContentMinSelector =
6550 aConstraint == SizingConstraint::MaxContent
6551 ? (TrackSize::eMaxContentMinSizing | TrackSize::eAutoMinSizing)
6552 : TrackSize::eMaxContentMinSizing;
6554 const auto orthogonalAxis = GetOrthogonalAxis(mAxis);
6555 const bool isMasonryInOtherAxis = aState.mFrame->IsMasonry(orthogonalAxis);
6557 for (auto& gridItem : aGridItems) {
6558 MOZ_ASSERT(!(gridItem.mState[mAxis] &
6559 (ItemState::eApplyAutoMinSize | ItemState::eIsFlexing |
6560 ItemState::eClampMarginBoxMinSize)),
6561 "Why are any of these bits set already?");
6563 const GridArea& area = gridItem.mArea;
6564 const LineRange& lineRange = area.*aRange;
6566 // If we have masonry layout in the other axis then skip this item unless
6567 // it's in the first masonry track, or has definite placement in this axis,
6568 // or spans all tracks in this axis (since that implies it will be placed
6569 // at line 1 regardless of layout results of other items).
6570 if (isMasonryInOtherAxis &&
6571 gridItem.mArea.LineRangeForAxis(orthogonalAxis).mStart != 0 &&
6572 (gridItem.mState[mAxis] & ItemState::eAutoPlacement) &&
6573 gridItem.mArea.LineRangeForAxis(mAxis).Extent() != mSizes.Length()) {
6574 continue;
6577 uint32_t span = lineRange.Extent();
6578 if (MOZ_UNLIKELY(gridItem.mState[mAxis] & ItemState::eIsSubgrid)) {
6579 auto itemWM = gridItem.mFrame->GetWritingMode();
6580 auto percentageBasis = aState.PercentageBasisFor(mAxis, gridItem);
6582 if (percentageBasis.ISize(itemWM) == NS_UNCONSTRAINEDSIZE) {
6583 percentageBasis.ISize(itemWM) = nscoord(0);
6586 if (percentageBasis.BSize(itemWM) == NS_UNCONSTRAINEDSIZE) {
6587 percentageBasis.BSize(itemWM) = nscoord(0);
6590 auto* subgrid =
6591 SubgridComputeMarginBorderPadding(gridItem, percentageBasis);
6592 LogicalMargin mbp = SubgridAccumulatedMarginBorderPadding(
6593 gridItem.SubgridFrame(), subgrid, wm, mAxis);
6595 if (span == 1) {
6596 AddSubgridContribution(mSizes[lineRange.mStart],
6597 mbp.StartEnd(mAxis, wm));
6598 } else {
6599 AddSubgridContribution(mSizes[lineRange.mStart], mbp.Start(mAxis, wm));
6600 AddSubgridContribution(mSizes[lineRange.mEnd - 1], mbp.End(mAxis, wm));
6602 continue;
6605 if (span == 1) {
6606 // Step 2. Size tracks to fit non-spanning items.
6607 if (ResolveIntrinsicSizeForNonSpanningItems(aState, aFunctions,
6608 aPercentageBasis, aConstraint,
6609 lineRange, gridItem)) {
6610 gridItem.mState[mAxis] |= ItemState::eIsFlexing;
6612 } else {
6613 TrackSize::StateBits state = StateBitsForRange(lineRange);
6615 // Check if we need to apply "Automatic Minimum Size" and cache it.
6616 if ((state & TrackSize::eAutoMinSizing) &&
6617 !(state & TrackSize::eFlexMaxSizing) &&
6618 gridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) {
6619 gridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize;
6622 if (state & TrackSize::eFlexMaxSizing) {
6623 gridItem.mState[mAxis] |= ItemState::eIsFlexing;
6624 } else if (state & (TrackSize::eIntrinsicMinSizing |
6625 TrackSize::eIntrinsicMaxSizing)) {
6626 // Collect data for Step 3.
6627 maxSpan = std::max(maxSpan, span);
6628 if (span >= perSpanData.Length()) {
6629 perSpanData.SetLength(2 * span);
6632 perSpanData[span].mItemCountWithSameSpan++;
6633 perSpanData[span].mStateBits |= state;
6635 CachedIntrinsicSizes cache;
6637 // Calculate data for "Automatic Minimum Size" clamping, if needed.
6638 if (TrackSize::IsDefiniteMaxSizing(state) &&
6639 (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize)) {
6640 nscoord minSizeClamp = 0;
6641 for (auto i : lineRange.Range()) {
6642 minSizeClamp += aFunctions.MaxSizingFor(i).AsBreadth().Resolve(
6643 aPercentageBasis);
6645 minSizeClamp += mGridGap * (span - 1);
6646 cache.mMinSizeClamp = minSizeClamp;
6647 gridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
6650 // Collect the various grid item size contributions we need.
6651 nscoord minSize = 0;
6652 if (state & TrackSize::eIntrinsicMinSizing) { // for 3.1
6653 minSize = MinSize(gridItem, aState, rc, wm, mAxis, &cache);
6655 nscoord minContent = 0;
6656 if (state & (contentBasedMinSelector | // for 3.2
6657 TrackSize::eIntrinsicMaxSizing)) { // for 3.5
6658 minContent =
6659 MinContentContribution(gridItem, aState, rc, wm, mAxis, &cache);
6661 nscoord maxContent = 0;
6662 if (state & (maxContentMinSelector | // for 3.3
6663 TrackSize::eAutoOrMaxContentMaxSizing)) { // for 3.6
6664 maxContent =
6665 MaxContentContribution(gridItem, aState, rc, wm, mAxis, &cache);
6668 spanningItems.AppendElement(
6669 SpanningItemData({span, state, lineRange, minSize, minContent,
6670 maxContent, gridItem.mFrame}));
6674 MOZ_ASSERT(!(gridItem.mState[mAxis] & ItemState::eClampMarginBoxMinSize) ||
6675 (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize),
6676 "clamping only applies to Automatic Minimum Size");
6679 // Step 3 - Increase sizes to accommodate spanning items crossing
6680 // content-sized tracks.
6681 if (maxSpan) {
6682 auto fitContentClamper = [&aFunctions, aPercentageBasis](uint32_t aTrack,
6683 nscoord aMinSize,
6684 nscoord* aSize) {
6685 nscoord fitContentLimit = ::ResolveToDefiniteSize(
6686 aFunctions.MaxSizingFor(aTrack), aPercentageBasis);
6687 if (*aSize > fitContentLimit) {
6688 *aSize = std::max(aMinSize, fitContentLimit);
6689 return true;
6691 return false;
6694 // Sort the collected items on span length, shortest first. There's no need
6695 // for a stable sort here since the sizing isn't order dependent within
6696 // a group of items with the same span length.
6697 std::sort(spanningItems.begin(), spanningItems.end(),
6698 SpanningItemData::IsSpanLessThan);
6700 nsTArray<uint32_t> tracks(maxSpan);
6701 nsTArray<TrackSize> plan(mSizes.Length());
6702 plan.SetLength(mSizes.Length());
6703 nsTArray<TrackSize> itemPlan(mSizes.Length());
6704 itemPlan.SetLength(mSizes.Length());
6705 // Start / end iterator for items of the same span length:
6706 auto spanGroupStart = spanningItems.begin();
6707 auto spanGroupEnd = spanGroupStart;
6708 const auto end = spanningItems.end();
6709 for (; spanGroupStart != end; spanGroupStart = spanGroupEnd) {
6710 const uint32_t span = spanGroupStart->mSpan;
6711 spanGroupEnd = spanGroupStart + perSpanData[span].mItemCountWithSameSpan;
6712 TrackSize::StateBits stateBitsForSpan = perSpanData[span].mStateBits;
6713 bool updatedBase = false; // Did we update any mBase in step 3.1..3.3?
6714 TrackSize::StateBits selector(TrackSize::eIntrinsicMinSizing);
6715 if (stateBitsForSpan & selector) {
6716 // Step 3.1 MinSize to intrinsic min-sizing.
6717 updatedBase =
6718 GrowSizeForSpanningItems<TrackSizingPhase::IntrinsicMinimums>(
6719 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
6722 selector = contentBasedMinSelector;
6723 if (stateBitsForSpan & selector) {
6724 // Step 3.2 MinContentContribution to min-/max-content (and 'auto' when
6725 // sizing under a min-content constraint) min-sizing.
6726 updatedBase |=
6727 GrowSizeForSpanningItems<TrackSizingPhase::ContentBasedMinimums>(
6728 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
6731 selector = maxContentMinSelector;
6732 if (stateBitsForSpan & selector) {
6733 // Step 3.3 MaxContentContribution to max-content (and 'auto' when
6734 // sizing under a max-content constraint) min-sizing.
6735 updatedBase |=
6736 GrowSizeForSpanningItems<TrackSizingPhase::MaxContentMinimums>(
6737 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
6740 if (updatedBase) {
6741 // Step 3.4
6742 for (TrackSize& sz : mSizes) {
6743 if (sz.mBase > sz.mLimit) {
6744 sz.mLimit = sz.mBase;
6749 selector = TrackSize::eIntrinsicMaxSizing;
6750 if (stateBitsForSpan & selector) {
6751 const bool willRunStep3_6 =
6752 stateBitsForSpan & TrackSize::eAutoOrMaxContentMaxSizing;
6753 // Step 3.5 MinContentContribution to intrinsic max-sizing.
6754 GrowSizeForSpanningItems<TrackSizingPhase::IntrinsicMaximums>(
6755 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector,
6756 fitContentClamper, willRunStep3_6);
6758 if (willRunStep3_6) {
6759 // Step 2.6 MaxContentContribution to max-content max-sizing.
6760 selector = TrackSize::eAutoOrMaxContentMaxSizing;
6761 GrowSizeForSpanningItems<TrackSizingPhase::MaxContentMaximums>(
6762 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector,
6763 fitContentClamper);
6769 // Step 5 - If any track still has an infinite growth limit, set its growth
6770 // limit to its base size.
6771 for (TrackSize& sz : mSizes) {
6772 if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
6773 sz.mLimit = sz.mBase;
6778 float nsGridContainerFrame::Tracks::FindFrUnitSize(
6779 const LineRange& aRange, const nsTArray<uint32_t>& aFlexTracks,
6780 const TrackSizingFunctions& aFunctions, nscoord aSpaceToFill) const {
6781 MOZ_ASSERT(aSpaceToFill > 0 && !aFlexTracks.IsEmpty());
6782 float flexFactorSum = 0.0f;
6783 nscoord leftOverSpace = aSpaceToFill;
6784 for (auto i : aRange.Range()) {
6785 const TrackSize& sz = mSizes[i];
6786 if (sz.mState & TrackSize::eFlexMaxSizing) {
6787 flexFactorSum += aFunctions.MaxSizingFor(i).AsFr();
6788 } else {
6789 leftOverSpace -= sz.mBase;
6790 if (leftOverSpace <= 0) {
6791 return 0.0f;
6795 bool restart;
6796 float hypotheticalFrSize;
6797 nsTArray<uint32_t> flexTracks(aFlexTracks.Clone());
6798 uint32_t numFlexTracks = flexTracks.Length();
6799 do {
6800 restart = false;
6801 hypotheticalFrSize = leftOverSpace / std::max(flexFactorSum, 1.0f);
6802 for (uint32_t i = 0, len = flexTracks.Length(); i < len; ++i) {
6803 uint32_t track = flexTracks[i];
6804 if (track == kAutoLine) {
6805 continue; // Track marked as inflexible in a prev. iter of this loop.
6807 float flexFactor = aFunctions.MaxSizingFor(track).AsFr();
6808 const nscoord base = mSizes[track].mBase;
6809 if (flexFactor * hypotheticalFrSize < base) {
6810 // 12.7.1.4: Treat this track as inflexible.
6811 flexTracks[i] = kAutoLine;
6812 flexFactorSum -= flexFactor;
6813 leftOverSpace -= base;
6814 --numFlexTracks;
6815 if (numFlexTracks == 0 || leftOverSpace <= 0) {
6816 return 0.0f;
6818 restart = true;
6819 // break; XXX (bug 1176621 comment 16) measure which is more common
6822 } while (restart);
6823 return hypotheticalFrSize;
6826 float nsGridContainerFrame::Tracks::FindUsedFlexFraction(
6827 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
6828 const nsTArray<uint32_t>& aFlexTracks,
6829 const TrackSizingFunctions& aFunctions, nscoord aAvailableSize) const {
6830 if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
6831 // Use all of the grid tracks and a 'space to fill' of the available space.
6832 const TranslatedLineRange range(0, mSizes.Length());
6833 return FindFrUnitSize(range, aFlexTracks, aFunctions, aAvailableSize);
6836 // The used flex fraction is the maximum of:
6837 // ... each flexible track's base size divided by its flex factor (which is
6838 // floored at 1).
6839 float fr = 0.0f;
6840 for (uint32_t track : aFlexTracks) {
6841 float flexFactor = aFunctions.MaxSizingFor(track).AsFr();
6842 float possiblyDividedBaseSize = (flexFactor > 1.0f)
6843 ? mSizes[track].mBase / flexFactor
6844 : mSizes[track].mBase;
6845 fr = std::max(fr, possiblyDividedBaseSize);
6847 WritingMode wm = aState.mWM;
6848 gfxContext* rc = &aState.mRenderingContext;
6849 // ... the result of 'finding the size of an fr' for each item that spans
6850 // a flex track with its max-content contribution as 'space to fill'
6851 for (const GridItemInfo& item : aGridItems) {
6852 if (item.mState[mAxis] & ItemState::eIsFlexing) {
6853 // XXX optimize: bug 1194446
6854 auto pb = Some(aState.PercentageBasisFor(mAxis, item));
6855 nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis, pb,
6856 IntrinsicISizeType::PrefISize);
6857 const LineRange& range =
6858 mAxis == eLogicalAxisInline ? item.mArea.mCols : item.mArea.mRows;
6859 MOZ_ASSERT(range.Extent() >= 1);
6860 const auto spannedGaps = range.Extent() - 1;
6861 if (spannedGaps > 0) {
6862 spaceToFill -= mGridGap * spannedGaps;
6864 if (spaceToFill <= 0) {
6865 continue;
6867 // ... and all its spanned tracks as input.
6868 nsTArray<uint32_t> itemFlexTracks;
6869 for (auto i : range.Range()) {
6870 if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
6871 itemFlexTracks.AppendElement(i);
6874 float itemFr =
6875 FindFrUnitSize(range, itemFlexTracks, aFunctions, spaceToFill);
6876 fr = std::max(fr, itemFr);
6879 return fr;
6882 void nsGridContainerFrame::Tracks::StretchFlexibleTracks(
6883 GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
6884 const TrackSizingFunctions& aFunctions, nscoord aAvailableSize) {
6885 if (aAvailableSize <= 0) {
6886 return;
6888 nsTArray<uint32_t> flexTracks(mSizes.Length());
6889 for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
6890 if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
6891 flexTracks.AppendElement(i);
6894 if (flexTracks.IsEmpty()) {
6895 return;
6897 nscoord minSize = 0;
6898 nscoord maxSize = NS_UNCONSTRAINEDSIZE;
6899 if (aState.mReflowInput) {
6900 auto* ri = aState.mReflowInput;
6901 minSize = mAxis == eLogicalAxisBlock ? ri->ComputedMinBSize()
6902 : ri->ComputedMinISize();
6903 maxSize = mAxis == eLogicalAxisBlock ? ri->ComputedMaxBSize()
6904 : ri->ComputedMaxISize();
6906 Maybe<CopyableAutoTArray<TrackSize, 32>> origSizes;
6907 bool applyMinMax = (minSize != 0 || maxSize != NS_UNCONSTRAINEDSIZE) &&
6908 aAvailableSize == NS_UNCONSTRAINEDSIZE;
6909 // We iterate twice at most. The 2nd time if the grid size changed after
6910 // applying a min/max-size (can only occur if aAvailableSize is indefinite).
6911 while (true) {
6912 float fr = FindUsedFlexFraction(aState, aGridItems, flexTracks, aFunctions,
6913 aAvailableSize);
6914 if (fr != 0.0f) {
6915 for (uint32_t i : flexTracks) {
6916 float flexFactor = aFunctions.MaxSizingFor(i).AsFr();
6917 nscoord flexLength = NSToCoordRound(flexFactor * fr);
6918 nscoord& base = mSizes[i].mBase;
6919 if (flexLength > base) {
6920 if (applyMinMax && origSizes.isNothing()) {
6921 origSizes.emplace(mSizes);
6923 base = flexLength;
6927 if (applyMinMax) {
6928 applyMinMax = false;
6929 // https://drafts.csswg.org/css-grid/#algo-flex-tracks
6930 // "If using this flex fraction would cause the grid to be smaller than
6931 // the grid container’s min-width/height (or larger than the grid
6932 // container’s max-width/height), then redo this step, treating the free
6933 // space as definite [...]"
6934 const auto sumOfGridGaps = SumOfGridGaps();
6935 nscoord newSize = SumOfGridTracks() + sumOfGridGaps;
6936 if (newSize > maxSize) {
6937 aAvailableSize = maxSize;
6938 } else if (newSize < minSize) {
6939 aAvailableSize = minSize;
6941 if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
6942 aAvailableSize = std::max(0, aAvailableSize - sumOfGridGaps);
6943 // Restart with the original track sizes and definite aAvailableSize.
6944 if (origSizes.isSome()) {
6945 mSizes = std::move(*origSizes);
6946 origSizes.reset();
6947 } // else, no mSizes[].mBase were changed above so it's still correct
6948 if (aAvailableSize == 0) {
6949 break; // zero available size wouldn't change any sizes though...
6951 continue;
6954 break;
6958 void nsGridContainerFrame::Tracks::AlignJustifyContent(
6959 const nsStylePosition* aStyle, StyleContentDistribution aAligmentStyleValue,
6960 WritingMode aWM, nscoord aContentBoxSize, bool aIsSubgriddedAxis) {
6961 const bool isAlign = mAxis == eLogicalAxisBlock;
6962 // Align-/justify-content doesn't apply in a subgridded axis.
6963 // Gap properties do apply though so we need to stretch/position the tracks
6964 // to center-align the gaps with the parent's gaps.
6965 if (MOZ_UNLIKELY(aIsSubgriddedAxis)) {
6966 auto& gap = isAlign ? aStyle->mRowGap : aStyle->mColumnGap;
6967 if (gap.IsNormal()) {
6968 return;
6970 auto len = mSizes.Length();
6971 if (len <= 1) {
6972 return;
6974 // This stores the gap deltas between the subgrid gap and the gaps in
6975 // the used track sizes (as encoded in its tracks' mPosition):
6976 nsTArray<nscoord> gapDeltas;
6977 const size_t numGaps = len - 1;
6978 gapDeltas.SetLength(numGaps);
6979 for (size_t i = 0; i < numGaps; ++i) {
6980 TrackSize& sz1 = mSizes[i];
6981 TrackSize& sz2 = mSizes[i + 1];
6982 nscoord currentGap = sz2.mPosition - (sz1.mPosition + sz1.mBase);
6983 gapDeltas[i] = mGridGap - currentGap;
6985 // Recompute the tracks' size/position so that they end up with
6986 // a subgrid-gap centered on the original track gap.
6987 nscoord currentPos = mSizes[0].mPosition;
6988 nscoord lastHalfDelta(0);
6989 for (size_t i = 0; i < numGaps; ++i) {
6990 TrackSize& sz = mSizes[i];
6991 nscoord delta = gapDeltas[i];
6992 nscoord halfDelta;
6993 nscoord roundingError = NSCoordDivRem(delta, 2, &halfDelta);
6994 auto newSize = sz.mBase - (halfDelta + roundingError) - lastHalfDelta;
6995 lastHalfDelta = halfDelta;
6996 // If the gap delta (in particular 'halfDelta + lastHalfDelta') is larger
6997 // than the current track size, newSize can be negative. Don't let the new
6998 // track size (mBase) be negative.
6999 sz.mBase = std::max(newSize, 0);
7000 sz.mPosition = currentPos;
7001 currentPos += newSize + mGridGap;
7003 auto& lastTrack = mSizes.LastElement();
7004 auto newSize = lastTrack.mBase - lastHalfDelta;
7005 lastTrack.mBase = std::max(newSize, 0);
7006 lastTrack.mPosition = currentPos;
7007 return;
7010 if (mSizes.IsEmpty()) {
7011 return;
7014 bool overflowSafe;
7015 auto alignment = ::GetAlignJustifyValue(aAligmentStyleValue.primary, aWM,
7016 isAlign, &overflowSafe);
7017 if (alignment == StyleAlignFlags::NORMAL) {
7018 alignment = StyleAlignFlags::STRETCH;
7019 // we may need a fallback for 'stretch' below
7020 aAligmentStyleValue = {alignment};
7023 // Compute the free space and count auto-sized tracks.
7024 size_t numAutoTracks = 0;
7025 nscoord space;
7026 if (alignment != StyleAlignFlags::START) {
7027 nscoord trackSizeSum = 0;
7028 if (aIsSubgriddedAxis) {
7029 numAutoTracks = mSizes.Length();
7030 } else {
7031 for (const TrackSize& sz : mSizes) {
7032 trackSizeSum += sz.mBase;
7033 if (sz.mState & TrackSize::eAutoMaxSizing) {
7034 ++numAutoTracks;
7038 space = aContentBoxSize - trackSizeSum - SumOfGridGaps();
7039 // Use the fallback value instead when applicable.
7040 if (space < 0 ||
7041 (alignment == StyleAlignFlags::SPACE_BETWEEN && mSizes.Length() == 1)) {
7042 auto fallback = ::GetAlignJustifyFallbackIfAny(aAligmentStyleValue, aWM,
7043 isAlign, &overflowSafe);
7044 if (fallback) {
7045 alignment = *fallback;
7048 if (space == 0 || (space < 0 && overflowSafe)) {
7049 // XXX check that this makes sense also for [last ]baseline (bug 1151204).
7050 alignment = StyleAlignFlags::START;
7054 // Optimize the cases where we just need to set each track's position.
7055 nscoord pos = 0;
7056 bool distribute = true;
7057 if (alignment == StyleAlignFlags::BASELINE ||
7058 alignment == StyleAlignFlags::LAST_BASELINE) {
7059 NS_WARNING("NYI: 'first/last baseline' (bug 1151204)"); // XXX
7060 alignment = StyleAlignFlags::START;
7062 if (alignment == StyleAlignFlags::START) {
7063 distribute = false;
7064 } else if (alignment == StyleAlignFlags::END) {
7065 pos = space;
7066 distribute = false;
7067 } else if (alignment == StyleAlignFlags::CENTER) {
7068 pos = space / 2;
7069 distribute = false;
7070 } else if (alignment == StyleAlignFlags::STRETCH) {
7071 distribute = numAutoTracks != 0;
7073 if (!distribute) {
7074 for (TrackSize& sz : mSizes) {
7075 sz.mPosition = pos;
7076 pos += sz.mBase + mGridGap;
7078 return;
7081 // Distribute free space to/between tracks and set their position.
7082 MOZ_ASSERT(space > 0, "should've handled that on the fallback path above");
7083 nscoord between, roundingError;
7084 if (alignment == StyleAlignFlags::STRETCH) {
7085 MOZ_ASSERT(numAutoTracks > 0, "we handled numAutoTracks == 0 above");
7086 // The outer loop typically only runs once - it repeats only in a masonry
7087 // axis when some stretchable items reach their `max-size`.
7088 // It's O(n^2) worst case; if all items are stretchable with a `max-size`
7089 // and exactly one item reaches its `max-size` each round.
7090 while (space) {
7091 pos = 0;
7092 nscoord spacePerTrack;
7093 roundingError = NSCoordDivRem(space, numAutoTracks, &spacePerTrack);
7094 space = 0;
7095 for (TrackSize& sz : mSizes) {
7096 sz.mPosition = pos;
7097 if (!(sz.mState & TrackSize::eAutoMaxSizing)) {
7098 pos += sz.mBase + mGridGap;
7099 continue;
7101 nscoord stretch = spacePerTrack;
7102 if (roundingError) {
7103 roundingError -= 1;
7104 stretch += 1;
7106 nscoord newBase = sz.mBase + stretch;
7107 if (mIsMasonry && (sz.mState & TrackSize::eClampToLimit)) {
7108 auto clampedSize = std::min(newBase, sz.mLimit);
7109 auto sizeOverLimit = newBase - clampedSize;
7110 if (sizeOverLimit > 0) {
7111 newBase = clampedSize;
7112 sz.mState &= ~(sz.mState & TrackSize::eAutoMaxSizing);
7113 // This repeats the outer loop to distribute the superfluous space:
7114 space += sizeOverLimit;
7115 if (--numAutoTracks == 0) {
7116 // ... except if we don't have any stretchable items left.
7117 space = 0;
7121 sz.mBase = newBase;
7122 pos += newBase + mGridGap;
7125 MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
7126 return;
7128 if (alignment == StyleAlignFlags::SPACE_BETWEEN) {
7129 MOZ_ASSERT(mSizes.Length() > 1, "should've used a fallback above");
7130 roundingError = NSCoordDivRem(space, mSizes.Length() - 1, &between);
7131 } else if (alignment == StyleAlignFlags::SPACE_AROUND) {
7132 roundingError = NSCoordDivRem(space, mSizes.Length(), &between);
7133 pos = between / 2;
7134 } else if (alignment == StyleAlignFlags::SPACE_EVENLY) {
7135 roundingError = NSCoordDivRem(space, mSizes.Length() + 1, &between);
7136 pos = between;
7137 } else {
7138 MOZ_ASSERT_UNREACHABLE("unknown align-/justify-content value");
7139 between = 0; // just to avoid a compiler warning
7140 roundingError = 0; // just to avoid a compiler warning
7142 between += mGridGap;
7143 for (TrackSize& sz : mSizes) {
7144 sz.mPosition = pos;
7145 nscoord spacing = between;
7146 if (roundingError) {
7147 roundingError -= 1;
7148 spacing += 1;
7150 pos += sz.mBase + spacing;
7152 MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
7155 void nsGridContainerFrame::LineRange::ToPositionAndLength(
7156 const nsTArray<TrackSize>& aTrackSizes, nscoord* aPos,
7157 nscoord* aLength) const {
7158 MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
7159 "expected a definite LineRange");
7160 MOZ_ASSERT(mStart < mEnd);
7161 nscoord startPos = aTrackSizes[mStart].mPosition;
7162 const TrackSize& sz = aTrackSizes[mEnd - 1];
7163 *aPos = startPos;
7164 *aLength = (sz.mPosition + sz.mBase) - startPos;
7167 nscoord nsGridContainerFrame::LineRange::ToLength(
7168 const nsTArray<TrackSize>& aTrackSizes) const {
7169 MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
7170 "expected a definite LineRange");
7171 MOZ_ASSERT(mStart < mEnd);
7172 nscoord startPos = aTrackSizes[mStart].mPosition;
7173 const TrackSize& sz = aTrackSizes[mEnd - 1];
7174 return (sz.mPosition + sz.mBase) - startPos;
7177 void nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos(
7178 const Tracks& aTracks, nscoord aGridOrigin, nscoord* aPos,
7179 nscoord* aLength) const {
7180 // kAutoLine for abspos children contributes the corresponding edge
7181 // of the grid container's padding-box.
7182 if (mEnd == kAutoLine) {
7183 if (mStart == kAutoLine) {
7184 // done
7185 } else {
7186 const nscoord endPos = *aPos + *aLength;
7187 auto side = mStart == aTracks.mSizes.Length()
7188 ? GridLineSide::BeforeGridGap
7189 : GridLineSide::AfterGridGap;
7190 nscoord startPos = aTracks.GridLineEdge(mStart, side);
7191 *aPos = aGridOrigin + startPos;
7192 *aLength = std::max(endPos - *aPos, 0);
7194 } else {
7195 if (mStart == kAutoLine) {
7196 auto side =
7197 mEnd == 0 ? GridLineSide::AfterGridGap : GridLineSide::BeforeGridGap;
7198 nscoord endPos = aTracks.GridLineEdge(mEnd, side);
7199 *aLength = std::max(aGridOrigin + endPos, 0);
7200 } else if (MOZ_LIKELY(mStart != mEnd)) {
7201 nscoord pos;
7202 ToPositionAndLength(aTracks.mSizes, &pos, aLength);
7203 *aPos = aGridOrigin + pos;
7204 } else {
7205 // The grid area only covers removed 'auto-fit' tracks.
7206 nscoord pos = aTracks.GridLineEdge(mStart, GridLineSide::BeforeGridGap);
7207 *aPos = aGridOrigin + pos;
7208 *aLength = nscoord(0);
7213 LogicalSize nsGridContainerFrame::GridReflowInput::PercentageBasisFor(
7214 LogicalAxis aAxis, const GridItemInfo& aGridItem) const {
7215 auto wm = aGridItem.mFrame->GetWritingMode();
7216 const auto* itemParent = aGridItem.mFrame->GetParent();
7217 if (MOZ_UNLIKELY(itemParent != mFrame)) {
7218 // The item comes from a descendant subgrid. Use the subgrid's
7219 // used track sizes to resolve the grid area size, if present.
7220 MOZ_ASSERT(itemParent->IsGridContainerFrame());
7221 auto* subgridFrame = static_cast<const nsGridContainerFrame*>(itemParent);
7222 MOZ_ASSERT(subgridFrame->IsSubgrid());
7223 if (auto* uts = subgridFrame->GetUsedTrackSizes()) {
7224 auto subgridWM = subgridFrame->GetWritingMode();
7225 LogicalSize cbSize(subgridWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
7226 if (!subgridFrame->IsSubgrid(eLogicalAxisInline) &&
7227 uts->mCanResolveLineRangeSize[eLogicalAxisInline]) {
7228 // NOTE: At this point aGridItem.mArea is in this->mFrame coordinates
7229 // and thus may have been transposed. The range values in a non-
7230 // subgridded axis still has its original values in subgridFrame's
7231 // coordinates though.
7232 auto rangeAxis = subgridWM.IsOrthogonalTo(mWM) ? eLogicalAxisBlock
7233 : eLogicalAxisInline;
7234 const auto& range = aGridItem.mArea.LineRangeForAxis(rangeAxis);
7235 cbSize.ISize(subgridWM) =
7236 range.ToLength(uts->mSizes[eLogicalAxisInline]);
7238 if (!subgridFrame->IsSubgrid(eLogicalAxisBlock) &&
7239 uts->mCanResolveLineRangeSize[eLogicalAxisBlock]) {
7240 auto rangeAxis = subgridWM.IsOrthogonalTo(mWM) ? eLogicalAxisInline
7241 : eLogicalAxisBlock;
7242 const auto& range = aGridItem.mArea.LineRangeForAxis(rangeAxis);
7243 cbSize.BSize(subgridWM) =
7244 range.ToLength(uts->mSizes[eLogicalAxisBlock]);
7246 return cbSize.ConvertTo(wm, subgridWM);
7249 return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
7252 if (aAxis == eLogicalAxisInline || !mCols.mCanResolveLineRangeSize) {
7253 return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
7255 // Note: for now, we only resolve transferred percentages to row sizing.
7256 // We may need to adjust these assertions once we implement bug 1300366.
7257 MOZ_ASSERT(!mRows.mCanResolveLineRangeSize);
7258 nscoord colSize = aGridItem.mArea.mCols.ToLength(mCols.mSizes);
7259 nscoord rowSize = NS_UNCONSTRAINEDSIZE;
7260 return !wm.IsOrthogonalTo(mWM) ? LogicalSize(wm, colSize, rowSize)
7261 : LogicalSize(wm, rowSize, colSize);
7264 LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockFor(
7265 const GridArea& aArea) const {
7266 nscoord i, b, iSize, bSize;
7267 MOZ_ASSERT(aArea.mCols.Extent() > 0, "grid items cover at least one track");
7268 MOZ_ASSERT(aArea.mRows.Extent() > 0, "grid items cover at least one track");
7269 aArea.mCols.ToPositionAndLength(mCols.mSizes, &i, &iSize);
7270 aArea.mRows.ToPositionAndLength(mRows.mSizes, &b, &bSize);
7271 return LogicalRect(mWM, i, b, iSize, bSize);
7274 LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockForAbsPos(
7275 const GridArea& aArea, const LogicalPoint& aGridOrigin,
7276 const LogicalRect& aGridCB) const {
7277 nscoord i = aGridCB.IStart(mWM);
7278 nscoord b = aGridCB.BStart(mWM);
7279 nscoord iSize = aGridCB.ISize(mWM);
7280 nscoord bSize = aGridCB.BSize(mWM);
7281 aArea.mCols.ToPositionAndLengthForAbsPos(mCols, aGridOrigin.I(mWM), &i,
7282 &iSize);
7283 aArea.mRows.ToPositionAndLengthForAbsPos(mRows, aGridOrigin.B(mWM), &b,
7284 &bSize);
7285 return LogicalRect(mWM, i, b, iSize, bSize);
7288 void nsGridContainerFrame::GridReflowInput::AlignJustifyContentInMasonryAxis(
7289 nscoord aMasonryBoxSize, nscoord aContentBoxSize) {
7290 if (aContentBoxSize == NS_UNCONSTRAINEDSIZE) {
7291 aContentBoxSize = aMasonryBoxSize;
7293 auto& masonryAxisTracks = mRows.mIsMasonry ? mRows : mCols;
7294 MOZ_ASSERT(masonryAxisTracks.mSizes.Length() == 2,
7295 "unexpected masonry axis tracks");
7296 const auto masonryAxis = masonryAxisTracks.mAxis;
7297 const auto contentAlignment = mGridStyle->UsedContentAlignment(masonryAxis);
7298 if (contentAlignment.primary == StyleAlignFlags::NORMAL ||
7299 contentAlignment.primary == StyleAlignFlags::STRETCH) {
7300 // Stretch the "masonry box" to the full content box if it's smaller.
7301 nscoord cbSize = std::max(aMasonryBoxSize, aContentBoxSize);
7302 for (auto& sz : masonryAxisTracks.mSizes) {
7303 sz.mBase = cbSize;
7305 return;
7308 // Save our current track sizes; replace them with one track sized to
7309 // the masonry box and align that within our content box.
7310 auto savedTrackSizes(std::move(masonryAxisTracks.mSizes));
7311 masonryAxisTracks.mSizes.AppendElement(savedTrackSizes[0]);
7312 masonryAxisTracks.mSizes[0].mBase = aMasonryBoxSize;
7313 masonryAxisTracks.AlignJustifyContent(mGridStyle, contentAlignment, mWM,
7314 aContentBoxSize, false);
7315 nscoord masonryBoxOffset = masonryAxisTracks.mSizes[0].mPosition;
7316 // Restore the original track sizes...
7317 masonryAxisTracks.mSizes = std::move(savedTrackSizes);
7318 // ...then reposition and resize all of them to the aligned result.
7319 for (auto& sz : masonryAxisTracks.mSizes) {
7320 sz.mPosition = masonryBoxOffset;
7321 sz.mBase = aMasonryBoxSize;
7325 // Note: this is called after all items have been positioned/reflowed.
7326 // The masonry-axis tracks have the size of the "masonry box" at this point
7327 // and are positioned according to 'align/justify-content'.
7328 void nsGridContainerFrame::GridReflowInput::AlignJustifyTracksInMasonryAxis(
7329 const LogicalSize& aContentSize, const nsSize& aContainerSize) {
7330 auto& masonryAxisTracks = mRows.mIsMasonry ? mRows : mCols;
7331 MOZ_ASSERT(masonryAxisTracks.mSizes.Length() == 2,
7332 "unexpected masonry axis tracks");
7333 const auto masonryAxis = masonryAxisTracks.mAxis;
7334 auto gridAxis = GetOrthogonalAxis(masonryAxis);
7335 auto& gridAxisTracks = TracksFor(gridAxis);
7336 AutoTArray<TrackSize, 32> savedSizes;
7337 savedSizes.AppendElements(masonryAxisTracks.mSizes);
7338 auto wm = mWM;
7339 nscoord contentAreaStart = mBorderPadding.Start(masonryAxis, wm);
7340 // The offset to the "masonry box" from our content-box start edge.
7341 nscoord masonryBoxOffset = masonryAxisTracks.mSizes[0].mPosition;
7342 nscoord alignmentContainerSize = masonryAxisTracks.mSizes[0].mBase;
7344 for (auto i : IntegerRange(gridAxisTracks.mSizes.Length())) {
7345 auto tracksAlignment = mGridStyle->UsedTracksAlignment(masonryAxis, i);
7346 if (tracksAlignment.primary != StyleAlignFlags::START) {
7347 masonryAxisTracks.mSizes.ClearAndRetainStorage();
7348 for (const auto& item : mGridItems) {
7349 if (item.mArea.LineRangeForAxis(gridAxis).mStart == i) {
7350 const auto* child = item.mFrame;
7351 LogicalRect rect = child->GetLogicalRect(wm, aContainerSize);
7352 TrackSize sz = {0, 0, 0, {0, 0}, TrackSize::StateBits{0}};
7353 const auto& margin = child->GetLogicalUsedMargin(wm);
7354 sz.mPosition = rect.Start(masonryAxis, wm) -
7355 margin.Start(masonryAxis, wm) - contentAreaStart;
7356 sz.mBase =
7357 rect.Size(masonryAxis, wm) + margin.StartEnd(masonryAxis, wm);
7358 // Account for a align-self baseline offset on the end side.
7359 // XXXmats hmm, it seems it would be a lot simpler to just store
7360 // these baseline adjustments into the UsedMarginProperty instead
7361 auto state = item.mState[masonryAxis];
7362 if ((state & ItemState::eSelfBaseline) &&
7363 (state & ItemState::eEndSideBaseline)) {
7364 sz.mBase += item.mBaselineOffset[masonryAxis];
7366 if (tracksAlignment.primary == StyleAlignFlags::STRETCH) {
7367 const auto* pos = child->StylePosition();
7368 auto itemAlignment =
7369 pos->UsedSelfAlignment(masonryAxis, mFrame->Style());
7370 if (child->StyleMargin()->HasAuto(masonryAxis, wm)) {
7371 sz.mState |= TrackSize::eAutoMaxSizing;
7372 sz.mState |= TrackSize::eItemHasAutoMargin;
7373 } else if (pos->Size(masonryAxis, wm).IsAuto() &&
7374 (itemAlignment == StyleAlignFlags::NORMAL ||
7375 itemAlignment == StyleAlignFlags::STRETCH)) {
7376 sz.mState |= TrackSize::eAutoMaxSizing;
7377 sz.mState |= TrackSize::eItemStretchSize;
7378 const auto& max = pos->MaxSize(masonryAxis, wm);
7379 if (max.ConvertsToLength()) { // XXX deal with percentages
7380 // XXX add in baselineOffset ? use actual frame size - content
7381 // size?
7382 nscoord boxSizingAdjust =
7383 child->GetLogicalUsedBorderAndPadding(wm).StartEnd(
7384 masonryAxis, wm);
7385 if (pos->mBoxSizing == StyleBoxSizing::Border) {
7386 boxSizingAdjust = 0;
7388 sz.mLimit = nsLayoutUtils::ComputeBSizeValue(
7389 aContentSize.Size(masonryAxis, wm), boxSizingAdjust,
7390 max.AsLengthPercentage());
7391 sz.mLimit += margin.StartEnd(masonryAxis, wm);
7392 sz.mState |= TrackSize::eClampToLimit;
7396 masonryAxisTracks.mSizes.AppendElement(std::move(sz));
7399 masonryAxisTracks.AlignJustifyContent(mGridStyle, tracksAlignment, wm,
7400 alignmentContainerSize, false);
7401 auto iter = mGridItems.begin();
7402 auto end = mGridItems.end();
7403 // We limit the loop to the number of items we found in the current
7404 // grid-axis axis track (in the outer loop) as an optimization.
7405 for (auto r : IntegerRange(masonryAxisTracks.mSizes.Length())) {
7406 GridItemInfo* item = nullptr;
7407 auto& sz = masonryAxisTracks.mSizes[r];
7408 // Find the next item in the current grid-axis axis track.
7409 for (; iter != end; ++iter) {
7410 if (iter->mArea.LineRangeForAxis(gridAxis).mStart == i) {
7411 item = &*iter;
7412 ++iter;
7413 break;
7416 nsIFrame* child = item->mFrame;
7417 const auto childWM = child->GetWritingMode();
7418 auto masonryChildAxis =
7419 childWM.IsOrthogonalTo(wm) ? gridAxis : masonryAxis;
7420 LogicalMargin margin = child->GetLogicalUsedMargin(childWM);
7421 bool forceReposition = false;
7422 if (sz.mState & TrackSize::eItemStretchSize) {
7423 auto size = child->GetLogicalSize().Size(masonryChildAxis, childWM);
7424 auto newSize = sz.mBase - margin.StartEnd(masonryChildAxis, childWM);
7425 if (size != newSize) {
7426 // XXX need to pass aIMinSizeClamp aBMinSizeClamp ?
7427 LogicalSize cb =
7428 ContainingBlockFor(item->mArea).Size(wm).ConvertTo(childWM, wm);
7429 LogicalSize availableSize = cb;
7430 cb.Size(masonryChildAxis, childWM) = alignmentContainerSize;
7431 availableSize.Size(eLogicalAxisBlock, childWM) =
7432 NS_UNCONSTRAINEDSIZE;
7433 const auto& bp = child->GetLogicalUsedBorderAndPadding(childWM);
7434 newSize -= bp.StartEnd(masonryChildAxis, childWM);
7435 ::PostReflowStretchChild(child, *mReflowInput, availableSize, cb,
7436 masonryChildAxis, newSize);
7437 if (childWM.IsPhysicalRTL()) {
7438 // The NormalPosition of this child is frame-size dependent so we
7439 // need to reset its stored position below.
7440 forceReposition = true;
7443 } else if (sz.mState & TrackSize::eItemHasAutoMargin) {
7444 // Re-compute the auto-margin(s) in the masonry axis.
7445 auto size = child->GetLogicalSize().Size(masonryChildAxis, childWM);
7446 auto spaceToFill = sz.mBase - size;
7447 if (spaceToFill > nscoord(0)) {
7448 const auto& marginStyle = child->StyleMargin();
7449 if (marginStyle->mMargin.Start(masonryChildAxis, childWM)
7450 .IsAuto()) {
7451 if (marginStyle->mMargin.End(masonryChildAxis, childWM)
7452 .IsAuto()) {
7453 nscoord half;
7454 nscoord roundingError = NSCoordDivRem(spaceToFill, 2, &half);
7455 margin.Start(masonryChildAxis, childWM) = half;
7456 margin.End(masonryChildAxis, childWM) = half + roundingError;
7457 } else {
7458 margin.Start(masonryChildAxis, childWM) = spaceToFill;
7460 } else {
7461 MOZ_ASSERT(
7462 marginStyle->mMargin.End(masonryChildAxis, childWM).IsAuto());
7463 margin.End(masonryChildAxis, childWM) = spaceToFill;
7465 nsMargin* propValue =
7466 child->GetProperty(nsIFrame::UsedMarginProperty());
7467 if (propValue) {
7468 *propValue = margin.GetPhysicalMargin(childWM);
7469 } else {
7470 child->AddProperty(
7471 nsIFrame::UsedMarginProperty(),
7472 new nsMargin(margin.GetPhysicalMargin(childWM)));
7476 nscoord newPos = contentAreaStart + masonryBoxOffset + sz.mPosition +
7477 margin.Start(masonryChildAxis, childWM);
7478 LogicalPoint pos = child->GetLogicalNormalPosition(wm, aContainerSize);
7479 auto delta = newPos - pos.Pos(masonryAxis, wm);
7480 if (delta != 0 || forceReposition) {
7481 LogicalPoint logicalDelta(wm);
7482 logicalDelta.Pos(masonryAxis, wm) = delta;
7483 child->MovePositionBy(wm, logicalDelta);
7486 } else if (masonryBoxOffset != nscoord(0)) {
7487 // TODO move placeholders too
7488 auto delta = masonryBoxOffset;
7489 LogicalPoint logicalDelta(wm);
7490 logicalDelta.Pos(masonryAxis, wm) = delta;
7491 for (const auto& item : mGridItems) {
7492 if (item.mArea.LineRangeForAxis(gridAxis).mStart != i) {
7493 continue;
7495 item.mFrame->MovePositionBy(wm, logicalDelta);
7499 masonryAxisTracks.mSizes = std::move(savedSizes);
7503 * Return a Fragmentainer object if we have a fragmentainer frame in our
7504 * ancestor chain of containing block (CB) reflow inputs. We'll only
7505 * continue traversing the ancestor chain as long as the CBs have
7506 * the same writing-mode and have overflow:visible.
7508 Maybe<nsGridContainerFrame::Fragmentainer>
7509 nsGridContainerFrame::GetNearestFragmentainer(
7510 const GridReflowInput& aState) const {
7511 Maybe<nsGridContainerFrame::Fragmentainer> data;
7512 const ReflowInput* gridRI = aState.mReflowInput;
7513 if (!gridRI->IsInFragmentedContext()) {
7514 return data;
7516 WritingMode wm = aState.mWM;
7517 const ReflowInput* cbRI = gridRI->mCBReflowInput;
7518 for (; cbRI; cbRI = cbRI->mCBReflowInput) {
7519 nsIScrollableFrame* sf = do_QueryFrame(cbRI->mFrame);
7520 if (sf) {
7521 break;
7523 if (wm.IsOrthogonalTo(cbRI->GetWritingMode())) {
7524 break;
7526 LayoutFrameType frameType = cbRI->mFrame->Type();
7527 if ((frameType == LayoutFrameType::Canvas &&
7528 PresContext()->IsPaginated()) ||
7529 frameType == LayoutFrameType::ColumnSet) {
7530 data.emplace();
7531 data->mIsTopOfPage = gridRI->mFlags.mIsTopOfPage;
7532 if (gridRI->AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
7533 data->mToFragmentainerEnd = aState.mFragBStart +
7534 gridRI->AvailableBSize() -
7535 aState.mBorderPadding.BStart(wm);
7536 } else {
7537 // This occurs when nsColumnSetFrame reflows its last column in
7538 // unconstrained available block-size.
7539 data->mToFragmentainerEnd = NS_UNCONSTRAINEDSIZE;
7541 const auto numRows = aState.mRows.mSizes.Length();
7542 data->mCanBreakAtStart =
7543 numRows > 0 && aState.mRows.mSizes[0].mPosition > 0;
7544 nscoord bSize = gridRI->ComputedBSize();
7545 data->mIsAutoBSize = bSize == NS_UNCONSTRAINEDSIZE;
7546 if (data->mIsAutoBSize) {
7547 bSize = gridRI->ComputedMinBSize();
7548 } else {
7549 bSize = gridRI->ApplyMinMaxBSize(bSize);
7551 nscoord gridEnd =
7552 aState.mRows.GridLineEdge(numRows, GridLineSide::BeforeGridGap);
7553 data->mCanBreakAtEnd = bSize > gridEnd && bSize > aState.mFragBStart;
7554 break;
7557 return data;
7560 void nsGridContainerFrame::ReflowInFlowChild(
7561 nsIFrame* aChild, const GridItemInfo* aGridItemInfo, nsSize aContainerSize,
7562 const Maybe<nscoord>& aStretchBSize, const Fragmentainer* aFragmentainer,
7563 const GridReflowInput& aState, const LogicalRect& aContentArea,
7564 ReflowOutput& aDesiredSize, nsReflowStatus& aStatus) {
7565 nsPresContext* pc = PresContext();
7566 ComputedStyle* containerSC = Style();
7567 WritingMode wm = aState.mReflowInput->GetWritingMode();
7568 const bool isGridItem = !!aGridItemInfo;
7569 MOZ_ASSERT(isGridItem == !aChild->IsPlaceholderFrame());
7570 LogicalRect cb(wm);
7571 WritingMode childWM = aChild->GetWritingMode();
7572 bool isConstrainedBSize = false;
7573 nscoord toFragmentainerEnd;
7574 // The part of the child's grid area that's in previous container fragments.
7575 nscoord consumedGridAreaBSize = 0;
7576 const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
7577 if (MOZ_LIKELY(isGridItem)) {
7578 MOZ_ASSERT(aGridItemInfo->mFrame == aChild);
7579 const GridArea& area = aGridItemInfo->mArea;
7580 MOZ_ASSERT(area.IsDefinite());
7581 cb = aState.ContainingBlockFor(area);
7582 if (aFragmentainer && !wm.IsOrthogonalTo(childWM)) {
7583 // |gridAreaBOffset| is the offset of the child's grid area in this
7584 // container fragment (if negative, that distance is the child CB size
7585 // consumed in previous container fragments). Note that cb.BStart
7586 // (initially) and aState.mFragBStart are in "global" grid coordinates
7587 // (like all track positions).
7588 nscoord gridAreaBOffset = cb.BStart(wm) - aState.mFragBStart;
7589 consumedGridAreaBSize = std::max(0, -gridAreaBOffset);
7590 cb.BStart(wm) = std::max(0, gridAreaBOffset);
7591 if (aFragmentainer->mToFragmentainerEnd != NS_UNCONSTRAINEDSIZE) {
7592 toFragmentainerEnd = aFragmentainer->mToFragmentainerEnd -
7593 aState.mFragBStart - cb.BStart(wm);
7594 toFragmentainerEnd = std::max(toFragmentainerEnd, 0);
7595 isConstrainedBSize = true;
7598 cb += aContentArea.Origin(wm);
7599 aState.mRows.AlignBaselineSubtree(*aGridItemInfo);
7600 aState.mCols.AlignBaselineSubtree(*aGridItemInfo);
7601 // Setup [align|justify]-content:[last ]baseline related frame properties.
7602 // These are added to the padding in SizeComputationInput::InitOffsets.
7603 // (a negative value signals the value is for 'last baseline' and should be
7604 // added to the (logical) end padding)
7605 typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop;
7606 auto SetProp = [aGridItemInfo, aChild](LogicalAxis aGridAxis, Prop aProp) {
7607 auto state = aGridItemInfo->mState[aGridAxis];
7608 auto baselineAdjust = (state & ItemState::eContentBaseline)
7609 ? aGridItemInfo->mBaselineOffset[aGridAxis]
7610 : nscoord(0);
7611 if (baselineAdjust < nscoord(0)) {
7612 // This happens when the subtree overflows its track.
7613 // XXX spec issue? it's unclear how to handle this.
7614 baselineAdjust = nscoord(0);
7615 } else if (state & ItemState::eLastBaseline) {
7616 // FIXME: We're not setting the ItemState::eEndSideBaseline flag any
7617 // more as the new baseline sharing group calculation handles most of
7618 // the cases we need. For non-masonry grids this flag was always set
7619 // for LAST_BASELINE items, so we're just mimicking that behavior here.
7620 // That said, masonry grids might not work 100% any more..
7621 baselineAdjust = -baselineAdjust;
7623 if (baselineAdjust != nscoord(0)) {
7624 aChild->SetProperty(aProp, baselineAdjust);
7625 } else {
7626 aChild->RemoveProperty(aProp);
7629 SetProp(eLogicalAxisBlock,
7630 isOrthogonal ? IBaselinePadProperty() : BBaselinePadProperty());
7631 SetProp(eLogicalAxisInline,
7632 isOrthogonal ? BBaselinePadProperty() : IBaselinePadProperty());
7633 } else {
7634 // By convention, for frames that perform CSS Box Alignment, we position
7635 // placeholder children at the start corner of their alignment container,
7636 // and in this case that's usually the grid's content-box.
7637 // ("Usually" - the exception is when the grid *also* forms the
7638 // abs.pos. containing block. In that case, the alignment container isn't
7639 // the content-box -- it's some grid area instead. But that case doesn't
7640 // require any special handling here, because we handle it later using a
7641 // special flag (ReflowInput::InitFlag::StaticPosIsCBOrigin) which will make
7642 // us ignore the placeholder's position entirely.)
7643 cb = aContentArea;
7644 aChild->AddStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN);
7647 LogicalSize reflowSize(cb.Size(wm));
7648 if (isConstrainedBSize) {
7649 reflowSize.BSize(wm) = toFragmentainerEnd;
7651 LogicalSize childCBSize = reflowSize.ConvertTo(childWM, wm);
7653 // Setup the ClampMarginBoxMinSize reflow flags and property, if needed.
7654 ComputeSizeFlags csFlags;
7655 if (aGridItemInfo) {
7656 const auto childIAxisInWM =
7657 isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
7658 // Clamp during reflow if we're stretching in that axis.
7659 if (GridItemShouldStretch(aChild, eLogicalAxisInline)) {
7660 if (aGridItemInfo->mState[childIAxisInWM] &
7661 ItemState::eClampMarginBoxMinSize) {
7662 csFlags += ComputeSizeFlag::IClampMarginBoxMinSize;
7664 } else {
7665 csFlags += ComputeSizeFlag::ShrinkWrap;
7668 const auto childBAxisInWM = GetOrthogonalAxis(childIAxisInWM);
7669 if (GridItemShouldStretch(aChild, eLogicalAxisBlock) &&
7670 aGridItemInfo->mState[childBAxisInWM] &
7671 ItemState::eClampMarginBoxMinSize) {
7672 csFlags += ComputeSizeFlag::BClampMarginBoxMinSize;
7673 aChild->SetProperty(BClampMarginBoxMinSizeProperty(),
7674 childCBSize.BSize(childWM));
7675 } else {
7676 aChild->RemoveProperty(BClampMarginBoxMinSizeProperty());
7679 if ((aGridItemInfo->mState[childIAxisInWM] &
7680 ItemState::eApplyAutoMinSize)) {
7681 csFlags += ComputeSizeFlag::IApplyAutoMinSize;
7685 if (!isConstrainedBSize) {
7686 childCBSize.BSize(childWM) = NS_UNCONSTRAINEDSIZE;
7688 LogicalSize percentBasis(cb.Size(wm).ConvertTo(childWM, wm));
7689 ReflowInput childRI(pc, *aState.mReflowInput, aChild, childCBSize,
7690 Some(percentBasis), {}, {}, csFlags);
7691 childRI.mFlags.mIsTopOfPage =
7692 aFragmentainer ? aFragmentainer->mIsTopOfPage : false;
7694 // FIXME (perf): It would be faster to do this only if the previous reflow of
7695 // the child was a measuring reflow, and only if the child does some of the
7696 // things that are affected by ComputeSizeFlag::IsGridMeasuringReflow.
7697 childRI.SetBResize(true);
7698 childRI.mFlags.mIsBResizeForPercentages = true;
7700 // If the child is stretching in its block axis, and we might be fragmenting
7701 // it in that axis, then setup a frame property to tell
7702 // nsBlockFrame::ComputeFinalSize the size.
7703 if (isConstrainedBSize && !wm.IsOrthogonalTo(childWM)) {
7704 const bool stretch = childRI.mStylePosition->BSize(childWM).IsAuto() &&
7705 GridItemShouldStretch(aChild, eLogicalAxisBlock);
7706 if (stretch) {
7707 aChild->SetProperty(FragStretchBSizeProperty(), *aStretchBSize);
7708 } else {
7709 aChild->RemoveProperty(FragStretchBSizeProperty());
7713 // We need the width of the child before we can correctly convert
7714 // the writing-mode of its origin, so we reflow at (0, 0) using a dummy
7715 // aContainerSize, and then pass the correct position to FinishReflowChild.
7716 ReflowOutput childSize(childRI);
7717 const nsSize dummyContainerSize;
7719 ReflowChild(aChild, pc, childSize, childRI, childWM, LogicalPoint(childWM),
7720 dummyContainerSize, ReflowChildFlags::Default, aStatus);
7721 LogicalPoint childPos = cb.Origin(wm).ConvertTo(
7722 childWM, wm, aContainerSize - childSize.PhysicalSize());
7723 // Apply align/justify-self and reflow again if that affects the size.
7724 if (MOZ_LIKELY(isGridItem)) {
7725 LogicalSize size = childSize.Size(childWM); // from the ReflowChild()
7726 auto applyItemSelfAlignment = [&](LogicalAxis aAxis, nscoord aCBSize) {
7727 auto align =
7728 childRI.mStylePosition->UsedSelfAlignment(aAxis, containerSC);
7729 auto state = aGridItemInfo->mState[aAxis];
7730 auto flags = AlignJustifyFlags::NoFlags;
7731 if (IsMasonry(aAxis)) {
7732 // In a masonry axis, we inhibit applying 'stretch' and auto-margins
7733 // here since AlignJustifyTracksInMasonryAxis deals with that.
7734 // The only other {align,justify}-{self,content} values that have an
7735 // effect are '[last] baseline', the rest behave as 'start'.
7736 if (MOZ_LIKELY(!(state & ItemState::eSelfBaseline))) {
7737 align = {StyleAlignFlags::START};
7738 } else {
7739 auto group = (state & ItemState::eFirstBaseline)
7740 ? BaselineSharingGroup::First
7741 : BaselineSharingGroup::Last;
7742 auto itemStart = aGridItemInfo->mArea.LineRangeForAxis(aAxis).mStart;
7743 aCBSize = aState.TracksFor(aAxis)
7744 .mSizes[itemStart]
7745 .mBaselineSubtreeSize[group];
7747 flags = AlignJustifyFlags::IgnoreAutoMargins;
7748 } else if (state & ItemState::eContentBaseline) {
7749 align = {(state & ItemState::eFirstBaseline)
7750 ? StyleAlignFlags::SELF_START
7751 : StyleAlignFlags::SELF_END};
7753 if (aAxis == eLogicalAxisBlock) {
7754 AlignSelf(*aGridItemInfo, align, aCBSize, wm, childRI, size, flags,
7755 &childPos);
7756 } else {
7757 JustifySelf(*aGridItemInfo, align, aCBSize, wm, childRI, size, flags,
7758 &childPos);
7761 if (aStatus.IsComplete()) {
7762 applyItemSelfAlignment(eLogicalAxisBlock,
7763 cb.BSize(wm) - consumedGridAreaBSize);
7765 applyItemSelfAlignment(eLogicalAxisInline, cb.ISize(wm));
7766 } // else, nsAbsoluteContainingBlock.cpp will handle align/justify-self.
7768 FinishReflowChild(aChild, pc, childSize, &childRI, childWM, childPos,
7769 aContainerSize, ReflowChildFlags::ApplyRelativePositioning);
7770 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, aChild);
7773 nscoord nsGridContainerFrame::ReflowInFragmentainer(
7774 GridReflowInput& aState, const LogicalRect& aContentArea,
7775 ReflowOutput& aDesiredSize, nsReflowStatus& aStatus,
7776 Fragmentainer& aFragmentainer, const nsSize& aContainerSize) {
7777 MOZ_ASSERT(aStatus.IsEmpty());
7778 MOZ_ASSERT(aState.mReflowInput);
7780 // Collect our grid items and sort them in row order. Collect placeholders
7781 // and put them in a separate array.
7782 nsTArray<const GridItemInfo*> sortedItems(aState.mGridItems.Length());
7783 nsTArray<nsIFrame*> placeholders(aState.mAbsPosItems.Length());
7784 aState.mIter.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll);
7785 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
7786 nsIFrame* child = *aState.mIter;
7787 if (!child->IsPlaceholderFrame()) {
7788 const GridItemInfo* info = &aState.mGridItems[aState.mIter.ItemIndex()];
7789 sortedItems.AppendElement(info);
7790 } else {
7791 placeholders.AppendElement(child);
7794 // NOTE: We don't need stable_sort here, except in Masonry layout. There are
7795 // no dependencies on having content order between items on the same row in
7796 // the code below in the non-Masonry case.
7797 if (IsMasonry()) {
7798 std::stable_sort(sortedItems.begin(), sortedItems.end(),
7799 GridItemInfo::IsStartRowLessThan);
7800 } else {
7801 std::sort(sortedItems.begin(), sortedItems.end(),
7802 GridItemInfo::IsStartRowLessThan);
7805 // Reflow our placeholder children; they must all be complete.
7806 for (auto child : placeholders) {
7807 nsReflowStatus childStatus;
7808 ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(),
7809 &aFragmentainer, aState, aContentArea, aDesiredSize,
7810 childStatus);
7811 MOZ_ASSERT(childStatus.IsComplete(),
7812 "nsPlaceholderFrame should never need to be fragmented");
7815 // The available size for children - we'll set this to the edge of the last
7816 // row in most cases below, but for now use the full size.
7817 nscoord childAvailableSize = aFragmentainer.mToFragmentainerEnd;
7818 const uint32_t startRow = aState.mStartRow;
7819 const uint32_t numRows = aState.mRows.mSizes.Length();
7820 bool isBDBClone = aState.mReflowInput->mStyleBorder->mBoxDecorationBreak ==
7821 StyleBoxDecorationBreak::Clone;
7822 nscoord bpBEnd = aState.mBorderPadding.BEnd(aState.mWM);
7824 // Set |endRow| to the first row that doesn't fit.
7825 uint32_t endRow = numRows;
7826 for (uint32_t row = startRow; row < numRows; ++row) {
7827 auto& sz = aState.mRows.mSizes[row];
7828 const nscoord bEnd = sz.mPosition + sz.mBase;
7829 nscoord remainingAvailableSize = childAvailableSize - bEnd;
7830 if (remainingAvailableSize < 0 ||
7831 (isBDBClone && remainingAvailableSize < bpBEnd)) {
7832 endRow = row;
7833 break;
7837 // Check for forced breaks on the items if available block-size for children
7838 // is constrained. That is, ignore forced breaks if available block-size for
7839 // children is unconstrained since our parent expected us to be fully
7840 // complete.
7841 bool isForcedBreak = false;
7842 const bool avoidBreakInside = ShouldAvoidBreakInside(*aState.mReflowInput);
7843 if (childAvailableSize != NS_UNCONSTRAINEDSIZE) {
7844 const bool isTopOfPage = aFragmentainer.mIsTopOfPage;
7845 for (const GridItemInfo* info : sortedItems) {
7846 uint32_t itemStartRow = info->mArea.mRows.mStart;
7847 if (itemStartRow == endRow) {
7848 break;
7850 const auto* disp = info->mFrame->StyleDisplay();
7851 if (disp->BreakBefore()) {
7852 // Propagate break-before on the first row to the container unless we're
7853 // already at top-of-page.
7854 if ((itemStartRow == 0 && !isTopOfPage) || avoidBreakInside) {
7855 aStatus.SetInlineLineBreakBeforeAndReset();
7856 return aState.mFragBStart;
7858 if ((itemStartRow > startRow ||
7859 (itemStartRow == startRow && !isTopOfPage)) &&
7860 itemStartRow < endRow) {
7861 endRow = itemStartRow;
7862 isForcedBreak = true;
7863 // reset any BREAK_AFTER we found on an earlier item
7864 aStatus.Reset();
7865 break; // we're done since the items are sorted in row order
7868 uint32_t itemEndRow = info->mArea.mRows.mEnd;
7869 if (disp->BreakAfter()) {
7870 if (itemEndRow != numRows) {
7871 if (itemEndRow > startRow && itemEndRow < endRow) {
7872 endRow = itemEndRow;
7873 isForcedBreak = true;
7874 // No "break;" here since later items with break-after may have
7875 // a shorter span.
7877 } else {
7878 // Propagate break-after on the last row to the container, we may
7879 // still find a break-before on this row though (and reset aStatus).
7880 aStatus.SetInlineLineBreakAfter(); // tentative
7885 // Consume at least one row in each fragment until we have consumed them
7886 // all. Except for the first row if there's a break opportunity before it.
7887 if (startRow == endRow && startRow != numRows &&
7888 (startRow != 0 || !aFragmentainer.mCanBreakAtStart)) {
7889 ++endRow;
7892 // Honor break-inside:avoid if we can't fit all rows.
7893 if (avoidBreakInside && endRow < numRows) {
7894 aStatus.SetInlineLineBreakBeforeAndReset();
7895 return aState.mFragBStart;
7899 // Calculate the block-size including this fragment.
7900 nscoord bEndRow =
7901 aState.mRows.GridLineEdge(endRow, GridLineSide::BeforeGridGap);
7902 nscoord bSize;
7903 if (aFragmentainer.mIsAutoBSize) {
7904 // We only apply min-bsize once all rows are complete (when bsize is auto).
7905 if (endRow < numRows) {
7906 bSize = bEndRow;
7907 auto clampedBSize = ClampToCSSMaxBSize(bSize, aState.mReflowInput);
7908 if (MOZ_UNLIKELY(clampedBSize != bSize)) {
7909 // We apply max-bsize in all fragments though.
7910 bSize = clampedBSize;
7911 } else if (!isBDBClone) {
7912 // The max-bsize won't make this fragment COMPLETE, so the block-end
7913 // border will be in a later fragment.
7914 bpBEnd = 0;
7916 } else {
7917 bSize = aState.mReflowInput->ApplyMinMaxBSize(bEndRow);
7919 } else {
7920 bSize = aState.mReflowInput->ApplyMinMaxBSize(
7921 aState.mReflowInput->ComputedBSize());
7924 // Check for overflow and set aStatus INCOMPLETE if so.
7925 bool overflow = bSize + bpBEnd > childAvailableSize;
7926 if (overflow) {
7927 if (avoidBreakInside) {
7928 aStatus.SetInlineLineBreakBeforeAndReset();
7929 return aState.mFragBStart;
7931 bool breakAfterLastRow = endRow == numRows && aFragmentainer.mCanBreakAtEnd;
7932 if (breakAfterLastRow) {
7933 MOZ_ASSERT(bEndRow < bSize, "bogus aFragmentainer.mCanBreakAtEnd");
7934 nscoord availableSize = childAvailableSize;
7935 if (isBDBClone) {
7936 availableSize -= bpBEnd;
7938 // Pretend we have at least 1px available size, otherwise we'll never make
7939 // progress in consuming our bSize.
7940 availableSize =
7941 std::max(availableSize, aState.mFragBStart + AppUnitsPerCSSPixel());
7942 // Fill the fragmentainer, but not more than our desired block-size and
7943 // at least to the size of the last row (even if that overflows).
7944 nscoord newBSize = std::min(bSize, availableSize);
7945 newBSize = std::max(newBSize, bEndRow);
7946 // If it's just the border+padding that is overflowing and we have
7947 // box-decoration-break:clone then we are technically COMPLETE. There's
7948 // no point in creating another zero-bsize fragment in this case.
7949 if (newBSize < bSize || !isBDBClone) {
7950 aStatus.SetIncomplete();
7952 bSize = newBSize;
7953 } else if (bSize <= bEndRow && startRow + 1 < endRow) {
7954 if (endRow == numRows) {
7955 // We have more than one row in this fragment, so we can break before
7956 // the last row instead.
7957 --endRow;
7958 bEndRow =
7959 aState.mRows.GridLineEdge(endRow, GridLineSide::BeforeGridGap);
7960 bSize = bEndRow;
7961 if (aFragmentainer.mIsAutoBSize) {
7962 bSize = ClampToCSSMaxBSize(bSize, aState.mReflowInput);
7965 aStatus.SetIncomplete();
7966 } else if (endRow < numRows) {
7967 bSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
7968 } // else - no break opportunities.
7969 } else {
7970 // Even though our block-size fits we need to honor forced breaks, or if
7971 // a row doesn't fit in an auto-sized container (unless it's constrained
7972 // by a max-bsize which make us overflow-incomplete).
7973 if (endRow < numRows &&
7974 (isForcedBreak || (aFragmentainer.mIsAutoBSize && bEndRow == bSize))) {
7975 bSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
7979 // If we can't fit all rows then we're at least overflow-incomplete.
7980 if (endRow < numRows) {
7981 childAvailableSize = bEndRow;
7982 if (aStatus.IsComplete()) {
7983 aStatus.SetOverflowIncomplete();
7984 aStatus.SetNextInFlowNeedsReflow();
7986 } else {
7987 // Children always have the full size of the rows in this fragment.
7988 childAvailableSize = std::max(childAvailableSize, bEndRow);
7991 return ReflowRowsInFragmentainer(aState, aContentArea, aDesiredSize, aStatus,
7992 aFragmentainer, aContainerSize, sortedItems,
7993 startRow, endRow, bSize, childAvailableSize);
7996 nscoord nsGridContainerFrame::ReflowRowsInFragmentainer(
7997 GridReflowInput& aState, const LogicalRect& aContentArea,
7998 ReflowOutput& aDesiredSize, nsReflowStatus& aStatus,
7999 Fragmentainer& aFragmentainer, const nsSize& aContainerSize,
8000 const nsTArray<const GridItemInfo*>& aSortedItems, uint32_t aStartRow,
8001 uint32_t aEndRow, nscoord aBSize, nscoord aAvailableSize) {
8002 FrameHashtable pushedItems;
8003 FrameHashtable incompleteItems;
8004 FrameHashtable overflowIncompleteItems;
8005 Maybe<nsTArray<nscoord>> masonryAxisPos;
8006 const auto rowCount = aState.mRows.mSizes.Length();
8007 nscoord masonryAxisGap;
8008 const auto wm = aState.mWM;
8009 const bool isColMasonry = IsMasonry(eLogicalAxisInline);
8010 if (isColMasonry) {
8011 for (auto& sz : aState.mCols.mSizes) {
8012 sz.mPosition = 0;
8014 masonryAxisGap = nsLayoutUtils::ResolveGapToLength(
8015 aState.mGridStyle->mColumnGap, aContentArea.ISize(wm));
8016 aState.mCols.mGridGap = masonryAxisGap;
8017 masonryAxisPos.emplace(rowCount);
8018 masonryAxisPos->SetLength(rowCount);
8019 PodZero(masonryAxisPos->Elements(), rowCount);
8021 bool isBDBClone = aState.mReflowInput->mStyleBorder->mBoxDecorationBreak ==
8022 StyleBoxDecorationBreak::Clone;
8023 bool didGrowRow = false;
8024 // As we walk across rows, we track whether the current row is at the top
8025 // of its grid-fragment, to help decide whether we can break before it. When
8026 // this function starts, our row is at the top of the current fragment if:
8027 // - we're starting with a nonzero row (i.e. we're a continuation)
8028 // OR:
8029 // - we're starting with the first row, & we're not allowed to break before
8030 // it (which makes it effectively at the top of its grid-fragment).
8031 bool isRowTopOfPage = aStartRow != 0 || !aFragmentainer.mCanBreakAtStart;
8032 const bool isStartRowTopOfPage = isRowTopOfPage;
8033 // Save our full available size for later.
8034 const nscoord gridAvailableSize = aFragmentainer.mToFragmentainerEnd;
8035 // Propagate the constrained size to our children.
8036 aFragmentainer.mToFragmentainerEnd = aAvailableSize;
8037 // Reflow the items in row order up to |aEndRow| and push items after that.
8038 uint32_t row = 0;
8039 // |i| is intentionally signed, so we can set it to -1 to restart the loop.
8040 for (int32_t i = 0, len = aSortedItems.Length(); i < len; ++i) {
8041 const GridItemInfo* const info = aSortedItems[i];
8042 nsIFrame* child = info->mFrame;
8043 row = info->mArea.mRows.mStart;
8044 MOZ_ASSERT(child->GetPrevInFlow() ? row < aStartRow : row >= aStartRow,
8045 "unexpected child start row");
8046 if (row >= aEndRow) {
8047 pushedItems.Insert(child);
8048 continue;
8051 bool rowCanGrow = false;
8052 nscoord maxRowSize = 0;
8053 if (row >= aStartRow) {
8054 if (row > aStartRow) {
8055 isRowTopOfPage = false;
8057 // Can we grow this row? Only consider span=1 items per spec...
8058 rowCanGrow = !didGrowRow && info->mArea.mRows.Extent() == 1;
8059 if (rowCanGrow) {
8060 auto& sz = aState.mRows.mSizes[row];
8061 // and only min-/max-content rows or flex rows in an auto-sized
8062 // container
8063 rowCanGrow = (sz.mState & TrackSize::eMinOrMaxContentMinSizing) ||
8064 ((sz.mState & TrackSize::eFlexMaxSizing) &&
8065 aFragmentainer.mIsAutoBSize);
8066 if (rowCanGrow) {
8067 if (isBDBClone) {
8068 maxRowSize = gridAvailableSize - aState.mBorderPadding.BEnd(wm);
8069 } else {
8070 maxRowSize = gridAvailableSize;
8072 maxRowSize -= sz.mPosition;
8073 // ...and only if there is space for it to grow.
8074 rowCanGrow = maxRowSize > sz.mBase;
8079 if (isColMasonry) {
8080 const auto& cols = info->mArea.mCols;
8081 MOZ_ASSERT((cols.mStart == 0 || cols.mStart == 1) && cols.Extent() == 1);
8082 aState.mCols.mSizes[cols.mStart].mPosition = masonryAxisPos.ref()[row];
8085 // aFragmentainer.mIsTopOfPage is propagated to the child reflow input.
8086 // When it's false the child may request InlineBreak::Before. We set it
8087 // to false when the row is growable (as determined in the CSS Grid
8088 // Fragmentation spec) and there is a non-zero space between it and the
8089 // fragmentainer end (that can be used to grow it). If the child reports
8090 // a forced break in this case, we grow this row to fill the fragment and
8091 // restart the loop. We also restart the loop with |aEndRow = row|
8092 // (but without growing any row) for a InlineBreak::Before child if it spans
8093 // beyond the last row in this fragment. This is to avoid fragmenting it.
8094 // We only restart the loop once.
8095 aFragmentainer.mIsTopOfPage = isRowTopOfPage && !rowCanGrow;
8096 nsReflowStatus childStatus;
8097 // Pass along how much to stretch this fragment, in case it's needed.
8098 nscoord bSize =
8099 aState.mRows.GridLineEdge(std::min(aEndRow, info->mArea.mRows.mEnd),
8100 GridLineSide::BeforeGridGap) -
8101 aState.mRows.GridLineEdge(std::max(aStartRow, row),
8102 GridLineSide::AfterGridGap);
8103 ReflowInFlowChild(child, info, aContainerSize, Some(bSize), &aFragmentainer,
8104 aState, aContentArea, aDesiredSize, childStatus);
8105 MOZ_ASSERT(childStatus.IsInlineBreakBefore() ||
8106 !childStatus.IsFullyComplete() || !child->GetNextInFlow(),
8107 "fully-complete reflow should destroy any NIFs");
8109 if (childStatus.IsInlineBreakBefore()) {
8110 MOZ_ASSERT(
8111 !child->GetPrevInFlow(),
8112 "continuations should never report InlineBreak::Before status");
8113 MOZ_ASSERT(!aFragmentainer.mIsTopOfPage,
8114 "got IsInlineBreakBefore() at top of page");
8115 if (!didGrowRow) {
8116 if (rowCanGrow) {
8117 // Grow this row and restart with the next row as |aEndRow|.
8118 aState.mRows.ResizeRow(row, maxRowSize);
8119 if (aState.mSharedGridData) {
8120 aState.mSharedGridData->mRows.ResizeRow(row, maxRowSize);
8122 didGrowRow = true;
8123 aEndRow = row + 1; // growing this row makes the next one not fit
8124 i = -1; // i == 0 after the next loop increment
8125 isRowTopOfPage = isStartRowTopOfPage;
8126 overflowIncompleteItems.Clear();
8127 incompleteItems.Clear();
8128 nscoord bEndRow =
8129 aState.mRows.GridLineEdge(aEndRow, GridLineSide::BeforeGridGap);
8130 aFragmentainer.mToFragmentainerEnd = bEndRow;
8131 if (aFragmentainer.mIsAutoBSize) {
8132 aBSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
8133 } else if (aStatus.IsIncomplete()) {
8134 aBSize = aState.mReflowInput->ApplyMinMaxBSize(
8135 aState.mReflowInput->ComputedBSize());
8136 aBSize = std::min(bEndRow, aBSize);
8138 continue;
8141 if (!isRowTopOfPage) {
8142 // We can break before this row - restart with it as the new end row.
8143 aEndRow = row;
8144 aBSize =
8145 aState.mRows.GridLineEdge(aEndRow, GridLineSide::BeforeGridGap);
8146 i = -1; // i == 0 after the next loop increment
8147 isRowTopOfPage = isStartRowTopOfPage;
8148 overflowIncompleteItems.Clear();
8149 incompleteItems.Clear();
8150 aStatus.SetIncomplete();
8151 continue;
8153 NS_ERROR("got InlineBreak::Before at top-of-page");
8154 childStatus.Reset();
8155 } else {
8156 // We got InlineBreak::Before again after growing the row - this can
8157 // happen if the child isn't splittable, e.g. some form controls.
8158 childStatus.Reset();
8159 if (child->GetNextInFlow()) {
8160 // The child already has a fragment, so we know it's splittable.
8161 childStatus.SetIncomplete();
8162 } // else, report that it's complete
8164 } else if (childStatus.IsInlineBreakAfter()) {
8165 MOZ_ASSERT_UNREACHABLE("unexpected child reflow status");
8168 MOZ_ASSERT(!childStatus.IsInlineBreakBefore(),
8169 "should've handled InlineBreak::Before above");
8170 if (childStatus.IsIncomplete()) {
8171 incompleteItems.Insert(child);
8172 } else if (!childStatus.IsFullyComplete()) {
8173 overflowIncompleteItems.Insert(child);
8175 if (isColMasonry) {
8176 auto childWM = child->GetWritingMode();
8177 auto childAxis =
8178 !childWM.IsOrthogonalTo(wm) ? eLogicalAxisInline : eLogicalAxisBlock;
8179 auto normalPos = child->GetLogicalNormalPosition(wm, aContainerSize);
8180 auto sz =
8181 childAxis == eLogicalAxisBlock ? child->BSize() : child->ISize();
8182 auto pos = normalPos.Pos(eLogicalAxisInline, wm) + sz +
8183 child->GetLogicalUsedMargin(childWM).End(childAxis, childWM);
8184 masonryAxisPos.ref()[row] =
8185 pos + masonryAxisGap - aContentArea.Start(eLogicalAxisInline, wm);
8189 // Record a break before |aEndRow|.
8190 aState.mNextFragmentStartRow = aEndRow;
8191 if (aEndRow < rowCount) {
8192 aState.mRows.BreakBeforeRow(aEndRow);
8193 if (aState.mSharedGridData) {
8194 aState.mSharedGridData->mRows.BreakBeforeRow(aEndRow);
8198 const bool childrenMoved = PushIncompleteChildren(
8199 pushedItems, incompleteItems, overflowIncompleteItems);
8200 if (childrenMoved && aStatus.IsComplete()) {
8201 aStatus.SetOverflowIncomplete();
8202 aStatus.SetNextInFlowNeedsReflow();
8204 if (!pushedItems.IsEmpty()) {
8205 AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
8206 // NOTE since we messed with our child list here, we intentionally
8207 // make aState.mIter invalid to avoid any use of it after this point.
8208 aState.mIter.Invalidate();
8210 if (!incompleteItems.IsEmpty()) {
8211 // NOTE since we messed with our child list here, we intentionally
8212 // make aState.mIter invalid to avoid any use of it after this point.
8213 aState.mIter.Invalidate();
8216 if (isColMasonry) {
8217 nscoord maxSize = 0;
8218 for (auto pos : masonryAxisPos.ref()) {
8219 maxSize = std::max(maxSize, pos);
8221 maxSize = std::max(nscoord(0), maxSize - masonryAxisGap);
8222 aState.AlignJustifyContentInMasonryAxis(maxSize, aContentArea.ISize(wm));
8225 return aBSize;
8228 // Here's a brief overview of how Masonry layout is implemented:
8229 // We setup two synthetic tracks in the Masonry axis so that the Reflow code
8230 // can treat it the same as for normal grid layout. The first track is
8231 // fixed (during item placement/layout) at the content box start and contains
8232 // the start items for each grid-axis track. The second track contains
8233 // all other items and is moved to the position where we want to position
8234 // the currently laid out item (like a sliding window as we place items).
8235 // Once item layout is done, the tracks are resized to be the size of
8236 // the "masonry box", which is the offset from the content box start to
8237 // the margin-box end of the item that is furthest away (this happens in
8238 // AlignJustifyContentInMasonryAxis() called at the end of this method).
8239 // This is to prepare for AlignJustifyTracksInMasonryAxis, which is called
8240 // later by our caller.
8241 // Both tracks store their first-/last-baseline group offsets as usual.
8242 // The first-baseline of the start track, and the last-baseline of the last
8243 // track (if they exist) are exported as the grid container's baselines, or
8244 // we fall back to picking an item's baseline (all this is per normal grid
8245 // layout). There's a slight difference in which items belongs to which
8246 // group though - see InitializeItemBaselinesInMasonryAxis for details.
8247 // This method returns the "masonry box" size (in the masonry axis).
8248 nscoord nsGridContainerFrame::MasonryLayout(GridReflowInput& aState,
8249 const LogicalRect& aContentArea,
8250 SizingConstraint aConstraint,
8251 ReflowOutput& aDesiredSize,
8252 nsReflowStatus& aStatus,
8253 Fragmentainer* aFragmentainer,
8254 const nsSize& aContainerSize) {
8255 using BaselineAlignmentSet = Tracks::BaselineAlignmentSet;
8257 auto recordAutoPlacement = [this, &aState](GridItemInfo* aItem,
8258 LogicalAxis aGridAxis) {
8259 // When we're auto-placing an item in a continuation we need to record
8260 // the placement in mSharedGridData.
8261 if (MOZ_UNLIKELY(aState.mSharedGridData && GetPrevInFlow()) &&
8262 (aItem->mState[aGridAxis] & ItemState::eAutoPlacement)) {
8263 auto* child = aItem->mFrame;
8264 MOZ_RELEASE_ASSERT(!child->GetPrevInFlow(),
8265 "continuations should never be auto-placed");
8266 for (auto& sharedItem : aState.mSharedGridData->mGridItems) {
8267 if (sharedItem.mFrame == child) {
8268 sharedItem.mArea.LineRangeForAxis(aGridAxis) =
8269 aItem->mArea.LineRangeForAxis(aGridAxis);
8270 MOZ_ASSERT(sharedItem.mState[aGridAxis] & ItemState::eAutoPlacement);
8271 sharedItem.mState[aGridAxis] &= ~ItemState::eAutoPlacement;
8272 break;
8276 aItem->mState[aGridAxis] &= ~ItemState::eAutoPlacement;
8279 // Collect our grid items and sort them in grid order.
8280 nsTArray<GridItemInfo*> sortedItems(aState.mGridItems.Length());
8281 aState.mIter.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll);
8282 size_t absposIndex = 0;
8283 const LogicalAxis masonryAxis =
8284 IsMasonry(eLogicalAxisBlock) ? eLogicalAxisBlock : eLogicalAxisInline;
8285 const auto wm = aState.mWM;
8286 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
8287 nsIFrame* child = *aState.mIter;
8288 if (MOZ_LIKELY(!child->IsPlaceholderFrame())) {
8289 GridItemInfo* item = &aState.mGridItems[aState.mIter.ItemIndex()];
8290 sortedItems.AppendElement(item);
8291 } else if (aConstraint == SizingConstraint::NoConstraint) {
8292 // (we only collect placeholders in the NoConstraint case since they
8293 // don't affect intrinsic sizing in any way)
8294 GridItemInfo* item = nullptr;
8295 auto* ph = static_cast<nsPlaceholderFrame*>(child);
8296 if (ph->GetOutOfFlowFrame()->GetParent() == this) {
8297 item = &aState.mAbsPosItems[absposIndex++];
8298 MOZ_RELEASE_ASSERT(item->mFrame == ph->GetOutOfFlowFrame());
8299 auto masonryStart = item->mArea.LineRangeForAxis(masonryAxis).mStart;
8300 // If the item was placed by the author at line 1 (masonryStart == 0)
8301 // then include it to be placed at the masonry-box start. If it's
8302 // auto-placed and has an `auto` inset value in the masonry axis then
8303 // we include it to be placed after the last grid item with the same
8304 // grid-axis start track.
8305 // XXXmats this is all a bit experimental at this point, pending a spec
8306 if (masonryStart == 0 ||
8307 (masonryStart == kAutoLine && item->mFrame->StylePosition()
8308 ->mOffset.Start(masonryAxis, wm)
8309 .IsAuto())) {
8310 sortedItems.AppendElement(item);
8311 } else {
8312 item = nullptr;
8315 if (!item) {
8316 // It wasn't included above - just reflow it and be done with it.
8317 nsReflowStatus childStatus;
8318 ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(), nullptr,
8319 aState, aContentArea, aDesiredSize, childStatus);
8323 const auto masonryAutoFlow = aState.mGridStyle->mMasonryAutoFlow;
8324 const bool definiteFirst =
8325 masonryAutoFlow.order == StyleMasonryItemOrder::DefiniteFirst;
8326 if (masonryAxis == eLogicalAxisBlock) {
8327 std::stable_sort(sortedItems.begin(), sortedItems.end(),
8328 definiteFirst ? GridItemInfo::RowMasonryDefiniteFirst
8329 : GridItemInfo::RowMasonryOrdered);
8330 } else {
8331 std::stable_sort(sortedItems.begin(), sortedItems.end(),
8332 definiteFirst ? GridItemInfo::ColMasonryDefiniteFirst
8333 : GridItemInfo::ColMasonryOrdered);
8336 FrameHashtable pushedItems;
8337 FrameHashtable incompleteItems;
8338 FrameHashtable overflowIncompleteItems;
8339 nscoord toFragmentainerEnd = nscoord_MAX;
8340 nscoord fragStartPos = aState.mFragBStart;
8341 const bool avoidBreakInside =
8342 aFragmentainer && ShouldAvoidBreakInside(*aState.mReflowInput);
8343 const bool isTopOfPageAtStart =
8344 aFragmentainer && aFragmentainer->mIsTopOfPage;
8345 if (aFragmentainer) {
8346 toFragmentainerEnd = std::max(0, aFragmentainer->mToFragmentainerEnd);
8348 const LogicalAxis gridAxis = GetOrthogonalAxis(masonryAxis);
8349 const auto gridAxisTrackCount = aState.TracksFor(gridAxis).mSizes.Length();
8350 auto& masonryTracks = aState.TracksFor(masonryAxis);
8351 auto& masonrySizes = masonryTracks.mSizes;
8352 MOZ_ASSERT(masonrySizes.Length() == 2);
8353 for (auto& sz : masonrySizes) {
8354 sz.mPosition = fragStartPos;
8356 // The current running position for each grid-axis track where the next item
8357 // should be positioned. When an item is placed we'll update the tracks it
8358 // spans to the end of its margin box + 'gap'.
8359 nsTArray<nscoord> currentPos(gridAxisTrackCount);
8360 currentPos.SetLength(gridAxisTrackCount);
8361 for (auto& sz : currentPos) {
8362 sz = fragStartPos;
8364 nsTArray<nscoord> lastPos(currentPos.Clone());
8365 nsTArray<GridItemInfo*> lastItems(gridAxisTrackCount);
8366 lastItems.SetLength(gridAxisTrackCount);
8367 PodZero(lastItems.Elements(), gridAxisTrackCount);
8368 const nscoord gap = nsLayoutUtils::ResolveGapToLength(
8369 masonryAxis == eLogicalAxisBlock ? aState.mGridStyle->mRowGap
8370 : aState.mGridStyle->mColumnGap,
8371 masonryTracks.mContentBoxSize);
8372 masonryTracks.mGridGap = gap;
8373 uint32_t cursor = 0;
8374 const auto containerToMasonryBoxOffset =
8375 fragStartPos - aContentArea.Start(masonryAxis, wm);
8376 const bool isPack = masonryAutoFlow.placement == StyleMasonryPlacement::Pack;
8377 bool didAlignStartAlignedFirstItems = false;
8379 // Return true if any of the lastItems in aRange are baseline-aligned in
8380 // the masonry axis.
8381 auto lastItemHasBaselineAlignment = [&](const LineRange& aRange) {
8382 for (auto i : aRange.Range()) {
8383 if (auto* child = lastItems[i] ? lastItems[i]->mFrame : nullptr) {
8384 const auto& pos = child->StylePosition();
8385 auto selfAlignment = pos->UsedSelfAlignment(masonryAxis, this->Style());
8386 if (selfAlignment == StyleAlignFlags::BASELINE ||
8387 selfAlignment == StyleAlignFlags::LAST_BASELINE) {
8388 return true;
8390 auto childAxis = masonryAxis;
8391 if (child->GetWritingMode().IsOrthogonalTo(wm)) {
8392 childAxis = gridAxis;
8394 auto contentAlignment = pos->UsedContentAlignment(childAxis).primary;
8395 if (contentAlignment == StyleAlignFlags::BASELINE ||
8396 contentAlignment == StyleAlignFlags::LAST_BASELINE) {
8397 return true;
8401 return false;
8404 // Resolve aItem's placement, unless it's definite already. Return its
8405 // masonry axis position with that placement.
8406 auto placeItem = [&](GridItemInfo* aItem) -> nscoord {
8407 auto& masonryAxisRange = aItem->mArea.LineRangeForAxis(masonryAxis);
8408 MOZ_ASSERT(masonryAxisRange.mStart != 0, "item placement is already final");
8409 auto& gridAxisRange = aItem->mArea.LineRangeForAxis(gridAxis);
8410 bool isAutoPlaced = aItem->mState[gridAxis] & ItemState::eAutoPlacement;
8411 uint32_t start = isAutoPlaced ? 0 : gridAxisRange.mStart;
8412 if (isAutoPlaced && !isPack) {
8413 start = cursor;
8414 isAutoPlaced = false;
8416 const uint32_t extent = gridAxisRange.Extent();
8417 if (start + extent > gridAxisTrackCount) {
8418 // Note that this will only happen to auto-placed items since the grid is
8419 // always wide enough to fit other items.
8420 start = 0;
8422 // This keeps track of the smallest `maxPosForRange` value that
8423 // we discover in the loop below:
8424 nscoord minPos = nscoord_MAX;
8425 MOZ_ASSERT(extent <= gridAxisTrackCount);
8426 const uint32_t iEnd = gridAxisTrackCount + 1 - extent;
8427 for (uint32_t i = start; i < iEnd; ++i) {
8428 // Find the max `currentPos` value for the tracks that we would span
8429 // if we were to use `i` as our start track:
8430 nscoord maxPosForRange = 0;
8431 for (auto j = i, jEnd = j + extent; j < jEnd; ++j) {
8432 maxPosForRange = std::max(currentPos[j], maxPosForRange);
8434 if (maxPosForRange < minPos) {
8435 minPos = maxPosForRange;
8436 start = i;
8438 if (!isAutoPlaced) {
8439 break;
8442 gridAxisRange.mStart = start;
8443 gridAxisRange.mEnd = start + extent;
8444 bool isFirstItem = true;
8445 for (uint32_t i : gridAxisRange.Range()) {
8446 if (lastItems[i]) {
8447 isFirstItem = false;
8448 break;
8451 // If this is the first item in its spanned grid tracks, then place it in
8452 // the first masonry track. Otherwise, place it in the second masonry track.
8453 masonryAxisRange.mStart = isFirstItem ? 0 : 1;
8454 masonryAxisRange.mEnd = masonryAxisRange.mStart + 1;
8455 return minPos;
8458 // Handle the resulting reflow status after reflowing aItem.
8459 // This may set aStatus to BreakBefore which the caller is expected
8460 // to handle by returning from MasonryLayout.
8461 // @return true if this item should consume all remaining space
8462 auto handleChildStatus = [&](GridItemInfo* aItem,
8463 const nsReflowStatus& aChildStatus) {
8464 bool result = false;
8465 if (MOZ_UNLIKELY(aFragmentainer)) {
8466 auto* child = aItem->mFrame;
8467 if (!aChildStatus.IsComplete() || aChildStatus.IsInlineBreakBefore() ||
8468 aChildStatus.IsInlineBreakAfter() ||
8469 child->StyleDisplay()->BreakAfter()) {
8470 if (!isTopOfPageAtStart && avoidBreakInside) {
8471 aStatus.SetInlineLineBreakBeforeAndReset();
8472 return result;
8474 result = true;
8476 if (aChildStatus.IsInlineBreakBefore()) {
8477 aStatus.SetIncomplete();
8478 pushedItems.Insert(child);
8479 } else if (aChildStatus.IsIncomplete()) {
8480 recordAutoPlacement(aItem, gridAxis);
8481 aStatus.SetIncomplete();
8482 incompleteItems.Insert(child);
8483 } else if (!aChildStatus.IsFullyComplete()) {
8484 recordAutoPlacement(aItem, gridAxis);
8485 overflowIncompleteItems.Insert(child);
8488 return result;
8491 // @return the distance from the masonry-box start to the end of the margin-
8492 // box of aChild
8493 auto offsetToMarginBoxEnd = [&](nsIFrame* aChild) {
8494 auto childWM = aChild->GetWritingMode();
8495 auto childAxis = !childWM.IsOrthogonalTo(wm) ? masonryAxis : gridAxis;
8496 auto normalPos = aChild->GetLogicalNormalPosition(wm, aContainerSize);
8497 auto sz =
8498 childAxis == eLogicalAxisBlock ? aChild->BSize() : aChild->ISize();
8499 return containerToMasonryBoxOffset + normalPos.Pos(masonryAxis, wm) + sz +
8500 aChild->GetLogicalUsedMargin(childWM).End(childAxis, childWM);
8503 // Apply baseline alignment to items belonging to the given set.
8504 nsTArray<Tracks::ItemBaselineData> firstBaselineItems;
8505 nsTArray<Tracks::ItemBaselineData> lastBaselineItems;
8506 auto applyBaselineAlignment = [&](BaselineAlignmentSet aSet) {
8507 firstBaselineItems.ClearAndRetainStorage();
8508 lastBaselineItems.ClearAndRetainStorage();
8509 masonryTracks.InitializeItemBaselinesInMasonryAxis(
8510 aState, aState.mGridItems, aSet, aContainerSize, currentPos,
8511 firstBaselineItems, lastBaselineItems);
8513 bool didBaselineAdjustment = false;
8514 nsTArray<Tracks::ItemBaselineData>* baselineItems[] = {&firstBaselineItems,
8515 &lastBaselineItems};
8516 for (const auto* items : baselineItems) {
8517 for (const auto& data : *items) {
8518 GridItemInfo* item = data.mGridItem;
8519 MOZ_ASSERT((item->mState[masonryAxis] & ItemState::eIsBaselineAligned));
8520 nscoord baselineOffset = item->mBaselineOffset[masonryAxis];
8521 if (baselineOffset == nscoord(0)) {
8522 continue; // no adjustment needed for this item
8524 didBaselineAdjustment = true;
8525 auto* child = item->mFrame;
8526 auto masonryAxisStart =
8527 item->mArea.LineRangeForAxis(masonryAxis).mStart;
8528 auto gridAxisRange = item->mArea.LineRangeForAxis(gridAxis);
8529 masonrySizes[masonryAxisStart].mPosition =
8530 aSet.mItemSet == BaselineAlignmentSet::LastItems
8531 ? lastPos[gridAxisRange.mStart]
8532 : fragStartPos;
8533 bool consumeAllSpace = false;
8534 const auto state = item->mState[masonryAxis];
8535 if ((state & ItemState::eContentBaseline) ||
8536 MOZ_UNLIKELY(aFragmentainer)) {
8537 if (MOZ_UNLIKELY(aFragmentainer)) {
8538 aFragmentainer->mIsTopOfPage =
8539 isTopOfPageAtStart &&
8540 masonrySizes[masonryAxisStart].mPosition == fragStartPos;
8542 nsReflowStatus childStatus;
8543 ReflowInFlowChild(child, item, aContainerSize, Nothing(),
8544 aFragmentainer, aState, aContentArea, aDesiredSize,
8545 childStatus);
8546 consumeAllSpace = handleChildStatus(item, childStatus);
8547 if (aStatus.IsInlineBreakBefore()) {
8548 return false;
8550 } else if (!(state & ItemState::eEndSideBaseline)) {
8551 // `align/justify-self` baselines on the start side can be handled by
8552 // just moving the frame (except in a fragmentainer in which case we
8553 // reflow it above instead since it might make it INCOMPLETE).
8554 LogicalPoint logicalDelta(wm);
8555 logicalDelta.Pos(masonryAxis, wm) = baselineOffset;
8556 child->MovePositionBy(wm, logicalDelta);
8558 if ((state & ItemState::eEndSideBaseline) && !consumeAllSpace) {
8559 // Account for an end-side baseline adjustment.
8560 for (uint32_t i : gridAxisRange.Range()) {
8561 currentPos[i] += baselineOffset;
8563 } else {
8564 nscoord pos = consumeAllSpace ? toFragmentainerEnd
8565 : offsetToMarginBoxEnd(child);
8566 pos += gap;
8567 for (uint32_t i : gridAxisRange.Range()) {
8568 currentPos[i] = pos;
8573 return didBaselineAdjustment;
8576 // Place and reflow items. We'll use two fake tracks in the masonry axis.
8577 // The first contains items that were placed there by the regular grid
8578 // placement algo (PlaceGridItems) and we may add some items here if there
8579 // are still empty slots. The second track contains all other items.
8580 // Both tracks always have the size of the content box in the masonry axis.
8581 // The position of the first track is always at the start. The position
8582 // of the second track is updated as we go to a position where we want
8583 // the current item to be positioned.
8584 for (GridItemInfo* item : sortedItems) {
8585 auto* child = item->mFrame;
8586 auto& masonryRange = item->mArea.LineRangeForAxis(masonryAxis);
8587 auto& gridRange = item->mArea.LineRangeForAxis(gridAxis);
8588 nsReflowStatus childStatus;
8589 if (MOZ_UNLIKELY(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) {
8590 auto contentArea = aContentArea;
8591 nscoord pos = nscoord_MAX;
8592 // XXXmats take mEnd into consideration...
8593 if (gridRange.mStart == kAutoLine) {
8594 for (auto p : currentPos) {
8595 pos = std::min(p, pos);
8597 } else if (gridRange.mStart < currentPos.Length()) {
8598 pos = currentPos[gridRange.mStart];
8599 } else if (currentPos.Length() > 0) {
8600 pos = currentPos.LastElement();
8602 if (pos == nscoord_MAX) {
8603 pos = nscoord(0);
8605 contentArea.Start(masonryAxis, wm) = pos;
8606 child = child->GetPlaceholderFrame();
8607 ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(), nullptr,
8608 aState, contentArea, aDesiredSize, childStatus);
8609 } else {
8610 MOZ_ASSERT(gridRange.Extent() > 0 &&
8611 gridRange.Extent() <= gridAxisTrackCount);
8612 MOZ_ASSERT((masonryRange.mStart == 0 || masonryRange.mStart == 1) &&
8613 masonryRange.Extent() == 1);
8614 if (masonryRange.mStart != 0) {
8615 masonrySizes[1].mPosition = placeItem(item);
8618 // If this is the first item NOT in the first track and if any of
8619 // the grid-axis tracks we span has a baseline-aligned item then we
8620 // need to do that baseline alignment now since it may affect
8621 // the placement of this and later items.
8622 if (!didAlignStartAlignedFirstItems &&
8623 aConstraint == SizingConstraint::NoConstraint &&
8624 masonryRange.mStart != 0 && lastItemHasBaselineAlignment(gridRange)) {
8625 didAlignStartAlignedFirstItems = true;
8626 if (applyBaselineAlignment({BaselineAlignmentSet::FirstItems,
8627 BaselineAlignmentSet::StartStretch})) {
8628 // Baseline alignment resized some items - redo our placement.
8629 masonrySizes[1].mPosition = placeItem(item);
8631 if (aStatus.IsInlineBreakBefore()) {
8632 return fragStartPos;
8636 for (uint32_t i : gridRange.Range()) {
8637 lastItems[i] = item;
8639 cursor = gridRange.mEnd;
8640 if (cursor >= gridAxisTrackCount) {
8641 cursor = 0;
8644 nscoord pos;
8645 if (aConstraint == SizingConstraint::NoConstraint) {
8646 const auto* disp = child->StyleDisplay();
8647 if (MOZ_UNLIKELY(aFragmentainer)) {
8648 aFragmentainer->mIsTopOfPage =
8649 isTopOfPageAtStart &&
8650 masonrySizes[masonryRange.mStart].mPosition == fragStartPos;
8651 if (!aFragmentainer->mIsTopOfPage &&
8652 (disp->BreakBefore() ||
8653 masonrySizes[masonryRange.mStart].mPosition >=
8654 toFragmentainerEnd)) {
8655 childStatus.SetInlineLineBreakBeforeAndReset();
8658 if (!childStatus.IsInlineBreakBefore()) {
8659 ReflowInFlowChild(child, item, aContainerSize, Nothing(),
8660 aFragmentainer, aState, aContentArea, aDesiredSize,
8661 childStatus);
8663 bool consumeAllSpace = handleChildStatus(item, childStatus);
8664 if (aStatus.IsInlineBreakBefore()) {
8665 return fragStartPos;
8667 pos =
8668 consumeAllSpace ? toFragmentainerEnd : offsetToMarginBoxEnd(child);
8669 } else {
8670 LogicalSize percentBasis(
8671 aState.PercentageBasisFor(eLogicalAxisInline, *item));
8672 IntrinsicISizeType type = aConstraint == SizingConstraint::MaxContent
8673 ? IntrinsicISizeType::PrefISize
8674 : IntrinsicISizeType::MinISize;
8675 auto sz =
8676 ::ContentContribution(*item, aState, &aState.mRenderingContext, wm,
8677 masonryAxis, Some(percentBasis), type);
8678 pos = sz + masonrySizes[masonryRange.mStart].mPosition;
8680 pos += gap;
8681 for (uint32_t i : gridRange.Range()) {
8682 lastPos[i] = currentPos[i];
8683 currentPos[i] = pos;
8688 // Do the remaining baseline alignment sets.
8689 if (aConstraint == SizingConstraint::NoConstraint) {
8690 for (auto*& item : lastItems) {
8691 if (item) {
8692 item->mState[masonryAxis] |= ItemState::eIsLastItemInMasonryTrack;
8695 BaselineAlignmentSet baselineSets[] = {
8696 {BaselineAlignmentSet::FirstItems, BaselineAlignmentSet::StartStretch},
8697 {BaselineAlignmentSet::FirstItems, BaselineAlignmentSet::EndStretch},
8698 {BaselineAlignmentSet::LastItems, BaselineAlignmentSet::StartStretch},
8699 {BaselineAlignmentSet::LastItems, BaselineAlignmentSet::EndStretch},
8701 for (uint32_t i = 0; i < ArrayLength(baselineSets); ++i) {
8702 if (i == 0 && didAlignStartAlignedFirstItems) {
8703 continue;
8705 applyBaselineAlignment(baselineSets[i]);
8709 const bool childrenMoved = PushIncompleteChildren(
8710 pushedItems, incompleteItems, overflowIncompleteItems);
8711 if (childrenMoved && aStatus.IsComplete()) {
8712 aStatus.SetOverflowIncomplete();
8713 aStatus.SetNextInFlowNeedsReflow();
8715 if (!pushedItems.IsEmpty()) {
8716 AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
8717 // NOTE since we messed with our child list here, we intentionally
8718 // make aState.mIter invalid to avoid any use of it after this point.
8719 aState.mIter.Invalidate();
8721 if (!incompleteItems.IsEmpty()) {
8722 // NOTE since we messed with our child list here, we intentionally
8723 // make aState.mIter invalid to avoid any use of it after this point.
8724 aState.mIter.Invalidate();
8727 nscoord masonryBoxSize = 0;
8728 for (auto pos : currentPos) {
8729 masonryBoxSize = std::max(masonryBoxSize, pos);
8731 masonryBoxSize = std::max(nscoord(0), masonryBoxSize - gap);
8732 if (aConstraint == SizingConstraint::NoConstraint) {
8733 aState.AlignJustifyContentInMasonryAxis(masonryBoxSize,
8734 masonryTracks.mContentBoxSize);
8736 return masonryBoxSize;
8739 nsGridContainerFrame* nsGridContainerFrame::ParentGridContainerForSubgrid()
8740 const {
8741 MOZ_ASSERT(IsSubgrid());
8742 nsIFrame* p = GetParent();
8743 while (p->GetContent() == GetContent()) {
8744 p = p->GetParent();
8746 MOZ_ASSERT(p->IsGridContainerFrame());
8747 auto* parent = static_cast<nsGridContainerFrame*>(p);
8748 MOZ_ASSERT(parent->HasSubgridItems());
8749 return parent;
8752 nscoord nsGridContainerFrame::ReflowChildren(GridReflowInput& aState,
8753 const LogicalRect& aContentArea,
8754 const nsSize& aContainerSize,
8755 ReflowOutput& aDesiredSize,
8756 nsReflowStatus& aStatus) {
8757 WritingMode wm = aState.mReflowInput->GetWritingMode();
8758 nscoord bSize = aContentArea.BSize(wm);
8759 MOZ_ASSERT(aState.mReflowInput);
8760 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
8761 if (HidesContentForLayout()) {
8762 return bSize;
8765 OverflowAreas ocBounds;
8766 nsReflowStatus ocStatus;
8767 if (GetPrevInFlow()) {
8768 ReflowOverflowContainerChildren(PresContext(), *aState.mReflowInput,
8769 ocBounds, ReflowChildFlags::Default,
8770 ocStatus, MergeSortedFrameListsFor);
8773 Maybe<Fragmentainer> fragmentainer = GetNearestFragmentainer(aState);
8774 // MasonryLayout() can only handle fragmentation in the masonry-axis,
8775 // so we let ReflowInFragmentainer() deal with grid-axis fragmentation
8776 // in the else-clause below.
8777 if (IsMasonry() &&
8778 !(IsMasonry(eLogicalAxisInline) && fragmentainer.isSome())) {
8779 aState.mInFragmentainer = fragmentainer.isSome();
8780 nscoord sz = MasonryLayout(
8781 aState, aContentArea, SizingConstraint::NoConstraint, aDesiredSize,
8782 aStatus, fragmentainer.ptrOr(nullptr), aContainerSize);
8783 if (IsMasonry(eLogicalAxisBlock)) {
8784 bSize = aState.mReflowInput->ComputedBSize();
8785 if (bSize == NS_UNCONSTRAINEDSIZE) {
8786 bSize = aState.mReflowInput->ApplyMinMaxBSize(sz);
8789 } else if (MOZ_UNLIKELY(fragmentainer.isSome())) {
8790 if (IsMasonry(eLogicalAxisInline) && !GetPrevInFlow()) {
8791 // First we do an unconstrained reflow to resolve the item placement
8792 // which is then kept as-is in the constrained reflow below.
8793 MasonryLayout(aState, aContentArea, SizingConstraint::NoConstraint,
8794 aDesiredSize, aStatus, nullptr, aContainerSize);
8796 aState.mInFragmentainer = true;
8797 bSize = ReflowInFragmentainer(aState, aContentArea, aDesiredSize, aStatus,
8798 *fragmentainer, aContainerSize);
8799 } else {
8800 aState.mIter.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll);
8801 for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
8802 nsIFrame* child = *aState.mIter;
8803 const GridItemInfo* info = nullptr;
8804 if (!child->IsPlaceholderFrame()) {
8805 info = &aState.mGridItems[aState.mIter.ItemIndex()];
8807 ReflowInFlowChild(child, info, aContainerSize, Nothing(), nullptr, aState,
8808 aContentArea, aDesiredSize, aStatus);
8809 MOZ_ASSERT(aStatus.IsComplete(),
8810 "child should be complete in unconstrained reflow");
8814 // Merge overflow container bounds and status.
8815 aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
8816 aStatus.MergeCompletionStatusFrom(ocStatus);
8818 if (IsAbsoluteContainer()) {
8819 const nsFrameList& children = GetChildList(GetAbsoluteListID());
8820 if (!children.IsEmpty()) {
8821 // 'gridOrigin' is the origin of the grid (the start of the first track),
8822 // with respect to the grid container's padding-box (CB).
8823 LogicalMargin pad(aState.mReflowInput->ComputedLogicalPadding(wm));
8824 const LogicalPoint gridOrigin(wm, pad.IStart(wm), pad.BStart(wm));
8825 const LogicalRect gridCB(wm, 0, 0,
8826 aContentArea.ISize(wm) + pad.IStartEnd(wm),
8827 bSize + pad.BStartEnd(wm));
8828 const nsSize gridCBPhysicalSize = gridCB.Size(wm).GetPhysicalSize(wm);
8829 size_t i = 0;
8830 for (nsIFrame* child : children) {
8831 MOZ_ASSERT(i < aState.mAbsPosItems.Length());
8832 MOZ_ASSERT(aState.mAbsPosItems[i].mFrame == child);
8833 GridArea& area = aState.mAbsPosItems[i].mArea;
8834 LogicalRect itemCB =
8835 aState.ContainingBlockForAbsPos(area, gridOrigin, gridCB);
8836 // nsAbsoluteContainingBlock::Reflow uses physical coordinates.
8837 nsRect* cb = child->GetProperty(GridItemContainingBlockRect());
8838 if (!cb) {
8839 cb = new nsRect;
8840 child->SetProperty(GridItemContainingBlockRect(), cb);
8842 *cb = itemCB.GetPhysicalRect(wm, gridCBPhysicalSize);
8843 ++i;
8845 // We pass a dummy rect as CB because each child has its own CB rect.
8846 // The eIsGridContainerCB flag tells nsAbsoluteContainingBlock::Reflow to
8847 // use those instead.
8848 nsRect dummyRect;
8849 AbsPosReflowFlags flags =
8850 AbsPosReflowFlags::CBWidthAndHeightChanged; // XXX could be optimized
8851 flags |= AbsPosReflowFlags::ConstrainHeight;
8852 flags |= AbsPosReflowFlags::IsGridContainerCB;
8853 GetAbsoluteContainingBlock()->Reflow(
8854 this, PresContext(), *aState.mReflowInput, aStatus, dummyRect, flags,
8855 &aDesiredSize.mOverflowAreas);
8858 return bSize;
8861 void nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
8862 ReflowOutput& aDesiredSize,
8863 const ReflowInput& aReflowInput,
8864 nsReflowStatus& aStatus) {
8865 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
8866 return;
8869 MarkInReflow();
8870 DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
8871 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
8872 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
8874 if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) {
8875 return;
8878 NormalizeChildLists();
8880 #ifdef DEBUG
8881 mDidPushItemsBitMayLie = false;
8882 SanityCheckChildListsBeforeReflow();
8883 #endif // DEBUG
8885 for (auto& perAxisBaseline : mBaseline) {
8886 for (auto& baseline : perAxisBaseline) {
8887 baseline = NS_INTRINSIC_ISIZE_UNKNOWN;
8891 const nsStylePosition* stylePos = aReflowInput.mStylePosition;
8892 auto prevInFlow = static_cast<nsGridContainerFrame*>(GetPrevInFlow());
8893 if (MOZ_LIKELY(!prevInFlow)) {
8894 InitImplicitNamedAreas(stylePos);
8895 } else {
8896 MOZ_ASSERT(prevInFlow->HasAnyStateBits(kIsSubgridBits) ==
8897 HasAnyStateBits(kIsSubgridBits),
8898 "continuations should have same kIsSubgridBits");
8900 GridReflowInput gridReflowInput(this, aReflowInput);
8901 if (gridReflowInput.mIter.ItemsAreAlreadyInOrder()) {
8902 AddStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
8903 } else {
8904 RemoveStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
8906 if (gridReflowInput.mIter.AtEnd() ||
8907 aReflowInput.mStyleDisplay->IsContainLayout()) {
8908 // We have no grid items, or we're layout-contained. So, we have no
8909 // baseline, and our parent should synthesize a baseline if needed.
8910 AddStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
8911 } else {
8912 RemoveStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
8914 const nscoord computedBSize = aReflowInput.ComputedBSize();
8915 const nscoord computedISize = aReflowInput.ComputedISize();
8916 const WritingMode& wm = gridReflowInput.mWM;
8917 const LogicalSize computedSize(wm, computedISize, computedBSize);
8919 nscoord consumedBSize = 0;
8920 nscoord bSize = 0;
8921 if (MOZ_LIKELY(!prevInFlow)) {
8922 Grid grid;
8923 if (MOZ_LIKELY(!IsSubgrid())) {
8924 RepeatTrackSizingInput repeatSizing(aReflowInput.ComputedMinSize(),
8925 computedSize,
8926 aReflowInput.ComputedMaxSize());
8927 grid.PlaceGridItems(gridReflowInput, repeatSizing);
8928 } else {
8929 auto* subgrid = GetProperty(Subgrid::Prop());
8930 MOZ_ASSERT(subgrid, "an ancestor forgot to call PlaceGridItems?");
8931 gridReflowInput.mGridItems = subgrid->mGridItems.Clone();
8932 gridReflowInput.mAbsPosItems = subgrid->mAbsPosItems.Clone();
8933 grid.mGridColEnd = subgrid->mGridColEnd;
8934 grid.mGridRowEnd = subgrid->mGridRowEnd;
8936 // XXX Technically incorrect: 'contain-intrinsic-block-size: none' is
8937 // treated as 0, ignoring our row sizes, when really we should use them but
8938 // *they* should be computed as if we had no children. To be fixed in bug
8939 // 1488878.
8940 const Maybe<nscoord> containBSize =
8941 aReflowInput.mFrame->ContainIntrinsicBSize();
8942 const nscoord trackSizingBSize = [&] {
8943 // This clamping only applies to auto sizes.
8944 if (containBSize && computedBSize == NS_UNCONSTRAINEDSIZE) {
8945 return aReflowInput.ApplyMinMaxBSize(*containBSize);
8947 return computedBSize;
8948 }();
8949 const LogicalSize containLogicalSize(wm, computedISize, trackSizingBSize);
8950 gridReflowInput.CalculateTrackSizes(grid, containLogicalSize,
8951 SizingConstraint::NoConstraint);
8952 if (containBSize) {
8953 bSize = *containBSize;
8954 } else {
8955 if (IsMasonry(eLogicalAxisBlock)) {
8956 bSize = computedBSize;
8957 } else {
8958 const auto& rowSizes = gridReflowInput.mRows.mSizes;
8959 if (MOZ_LIKELY(!IsSubgrid(eLogicalAxisBlock))) {
8960 // Note: we can't use GridLineEdge here since we haven't calculated
8961 // the rows' mPosition yet (happens in AlignJustifyContent below).
8962 for (const auto& sz : rowSizes) {
8963 bSize += sz.mBase;
8965 bSize += gridReflowInput.mRows.SumOfGridGaps();
8966 } else if (computedBSize == NS_UNCONSTRAINEDSIZE) {
8967 bSize = gridReflowInput.mRows.GridLineEdge(
8968 rowSizes.Length(), GridLineSide::BeforeGridGap);
8972 } else {
8973 consumedBSize = CalcAndCacheConsumedBSize();
8974 gridReflowInput.InitializeForContinuation(this, consumedBSize);
8975 // XXX Technically incorrect: 'contain-intrinsic-block-size: none' is
8976 // treated as 0, ignoring our row sizes, when really we should use them but
8977 // *they* should be computed as if we had no children. To be fixed in bug
8978 // 1488878.
8979 if (Maybe<nscoord> containBSize =
8980 aReflowInput.mFrame->ContainIntrinsicBSize()) {
8981 bSize = *containBSize;
8982 } else {
8983 const uint32_t numRows = gridReflowInput.mRows.mSizes.Length();
8984 bSize = gridReflowInput.mRows.GridLineEdge(numRows,
8985 GridLineSide::AfterGridGap);
8988 if (computedBSize == NS_UNCONSTRAINEDSIZE) {
8989 bSize = aReflowInput.ApplyMinMaxBSize(bSize);
8990 } else if (aReflowInput.ShouldApplyAutomaticMinimumOnBlockAxis()) {
8991 nscoord contentBSize = aReflowInput.ApplyMinMaxBSize(bSize);
8992 bSize = std::max(contentBSize, computedBSize);
8993 } else {
8994 bSize = computedBSize;
8996 if (bSize != NS_UNCONSTRAINEDSIZE) {
8997 bSize = std::max(bSize - consumedBSize, 0);
8999 auto& bp = gridReflowInput.mBorderPadding;
9000 LogicalRect contentArea(wm, bp.IStart(wm), bp.BStart(wm), computedISize,
9001 bSize);
9003 if (!prevInFlow) {
9004 const auto& rowSizes = gridReflowInput.mRows.mSizes;
9005 if (!IsRowSubgrid()) {
9006 // Apply 'align-content' to the grid.
9007 if (computedBSize == NS_UNCONSTRAINEDSIZE &&
9008 stylePos->mRowGap.IsLengthPercentage() &&
9009 stylePos->mRowGap.AsLengthPercentage().HasPercent()) {
9010 // Re-resolve the row-gap now that we know our intrinsic block-size.
9011 gridReflowInput.mRows.mGridGap =
9012 nsLayoutUtils::ResolveGapToLength(stylePos->mRowGap, bSize);
9014 if (!gridReflowInput.mRows.mIsMasonry) {
9015 auto alignment = stylePos->mAlignContent;
9016 gridReflowInput.mRows.AlignJustifyContent(stylePos, alignment, wm,
9017 bSize, false);
9019 } else {
9020 if (computedBSize == NS_UNCONSTRAINEDSIZE) {
9021 bSize = gridReflowInput.mRows.GridLineEdge(rowSizes.Length(),
9022 GridLineSide::BeforeGridGap);
9023 contentArea.BSize(wm) = std::max(bSize, nscoord(0));
9026 // Save the final row sizes for use by subgrids, if needed.
9027 if (HasSubgridItems() || IsSubgrid()) {
9028 StoreUsedTrackSizes(eLogicalAxisBlock, rowSizes);
9032 nsSize containerSize = contentArea.Size(wm).GetPhysicalSize(wm);
9033 bool repositionChildren = false;
9034 if (containerSize.width == NS_UNCONSTRAINEDSIZE && wm.IsVerticalRL()) {
9035 // Note that writing-mode:vertical-rl is the only case where the block
9036 // logical direction progresses in a negative physical direction, and
9037 // therefore block-dir coordinate conversion depends on knowing the width
9038 // of the coordinate space in order to translate between the logical and
9039 // physical origins.
9041 // A masonry axis size may be unconstrained, otherwise in a regular grid
9042 // our intrinsic size is always known by now. We'll re-position
9043 // the children below once our size is known.
9044 repositionChildren = true;
9045 containerSize.width = 0;
9047 containerSize.width += bp.LeftRight(wm);
9048 containerSize.height += bp.TopBottom(wm);
9050 bSize = ReflowChildren(gridReflowInput, contentArea, containerSize,
9051 aDesiredSize, aStatus);
9052 bSize = std::max(bSize - consumedBSize, 0);
9054 // Skip our block-end border if we're INCOMPLETE.
9055 if (!aStatus.IsComplete() && !gridReflowInput.mSkipSides.BEnd() &&
9056 StyleBorder()->mBoxDecorationBreak != StyleBoxDecorationBreak::Clone) {
9057 bp.BEnd(wm) = nscoord(0);
9060 LogicalSize desiredSize(wm, computedISize + bp.IStartEnd(wm),
9061 bSize + bp.BStartEnd(wm));
9062 aDesiredSize.SetSize(wm, desiredSize);
9063 nsRect frameRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height());
9064 aDesiredSize.mOverflowAreas.UnionAllWith(frameRect);
9066 if (repositionChildren) {
9067 nsPoint physicalDelta(aDesiredSize.Width() - bp.LeftRight(wm), 0);
9068 for (const auto& item : gridReflowInput.mGridItems) {
9069 auto* child = item.mFrame;
9070 child->MovePositionBy(physicalDelta);
9071 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child);
9075 if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
9076 // Per spec, the grid area is included in a grid container's scrollable
9077 // overflow region [1], as well as the padding on the end-edge sides that
9078 // would satisfy the requirements of 'place-content: end' alignment [2].
9080 // Note that we include the padding from all sides of the grid area, not
9081 // just the end sides; this is fine because the grid area is relative to our
9082 // content-box origin. The inflated bounds won't go beyond our padding-box
9083 // edges on the start sides.
9085 // The margin areas of grid item boxes are also included in the scrollable
9086 // overflow region [2].
9088 // [1] https://drafts.csswg.org/css-grid-1/#overflow
9089 // [2] https://drafts.csswg.org/css-overflow-3/#scrollable
9091 // Synthesize a grid area covering all columns and rows, and compute its
9092 // rect relative to our border-box.
9094 // Note: the grid columns and rows exist only if there is an explicit grid;
9095 // or when an implicit grid is needed to place any grid items. See
9096 // nsGridContainerFrame::Grid::PlaceGridItems().
9097 const auto numCols =
9098 static_cast<int32_t>(gridReflowInput.mCols.mSizes.Length());
9099 const auto numRows =
9100 static_cast<int32_t>(gridReflowInput.mRows.mSizes.Length());
9101 if (numCols > 0 && numRows > 0) {
9102 const GridArea gridArea(LineRange(0, numCols), LineRange(0, numRows));
9103 const LogicalRect gridAreaRect =
9104 gridReflowInput.ContainingBlockFor(gridArea) +
9105 LogicalPoint(wm, bp.IStart(wm), bp.BStart(wm));
9107 MOZ_ASSERT(bp == aReflowInput.ComputedLogicalPadding(wm),
9108 "A scrolled inner frame shouldn't have any border!");
9109 const LogicalMargin& padding = bp;
9110 nsRect physicalGridAreaRectWithPadding =
9111 gridAreaRect.GetPhysicalRect(wm, containerSize);
9112 physicalGridAreaRectWithPadding.Inflate(padding.GetPhysicalMargin(wm));
9113 aDesiredSize.mOverflowAreas.UnionAllWith(physicalGridAreaRectWithPadding);
9116 nsRect gridItemMarginBoxBounds;
9117 for (const auto& item : gridReflowInput.mGridItems) {
9118 gridItemMarginBoxBounds =
9119 gridItemMarginBoxBounds.Union(item.mFrame->GetMarginRect());
9121 aDesiredSize.mOverflowAreas.UnionAllWith(gridItemMarginBoxBounds);
9124 // TODO: fix align-tracks alignment in fragments
9125 if ((IsMasonry(eLogicalAxisBlock) && !prevInFlow) ||
9126 IsMasonry(eLogicalAxisInline)) {
9127 gridReflowInput.AlignJustifyTracksInMasonryAxis(
9128 contentArea.Size(wm), aDesiredSize.PhysicalSize());
9131 // Convert INCOMPLETE -> OVERFLOW_INCOMPLETE and zero bsize if we're an OC.
9132 if (HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
9133 if (!aStatus.IsComplete()) {
9134 aStatus.SetOverflowIncomplete();
9135 aStatus.SetNextInFlowNeedsReflow();
9137 bSize = 0;
9138 desiredSize.BSize(wm) = bSize + bp.BStartEnd(wm);
9139 aDesiredSize.SetSize(wm, desiredSize);
9142 if (!gridReflowInput.mInFragmentainer) {
9143 MOZ_ASSERT(gridReflowInput.mIter.IsValid());
9144 auto sz = frameRect.Size();
9145 CalculateBaselines(BaselineSet::eBoth, &gridReflowInput.mIter,
9146 &gridReflowInput.mGridItems, gridReflowInput.mCols, 0,
9147 gridReflowInput.mCols.mSizes.Length(), wm, sz,
9148 bp.IStart(wm), bp.IEnd(wm), desiredSize.ISize(wm));
9149 CalculateBaselines(BaselineSet::eBoth, &gridReflowInput.mIter,
9150 &gridReflowInput.mGridItems, gridReflowInput.mRows, 0,
9151 gridReflowInput.mRows.mSizes.Length(), wm, sz,
9152 bp.BStart(wm), bp.BEnd(wm), desiredSize.BSize(wm));
9153 } else {
9154 // Only compute 'first baseline' if this fragment contains the first track.
9155 // XXXmats maybe remove this condition? bug 1306499
9156 BaselineSet baselines = BaselineSet::eNone;
9157 if (gridReflowInput.mStartRow == 0 &&
9158 gridReflowInput.mStartRow != gridReflowInput.mNextFragmentStartRow) {
9159 baselines = BaselineSet::eFirst;
9161 // Only compute 'last baseline' if this fragment contains the last track.
9162 // XXXmats maybe remove this condition? bug 1306499
9163 uint32_t len = gridReflowInput.mRows.mSizes.Length();
9164 if (gridReflowInput.mStartRow != len &&
9165 gridReflowInput.mNextFragmentStartRow == len) {
9166 baselines = BaselineSet(baselines | BaselineSet::eLast);
9168 Maybe<CSSOrderAwareFrameIterator> iter;
9169 Maybe<nsTArray<GridItemInfo>> gridItems;
9170 if (baselines != BaselineSet::eNone) {
9171 // We need to create a new iterator and GridItemInfo array because we
9172 // might have pushed some children at this point.
9173 // Even if the gridReflowInput iterator is invalid we can reuse its
9174 // state about order to optimize initialization of the new iterator.
9175 // An ordered child list can't become unordered by pushing frames.
9176 // An unordered list can become ordered in a number of cases, but we
9177 // ignore that here and guess that the child list is still unordered.
9178 // XXX this is O(n^2) in the number of items in this fragment: bug 1306705
9179 using Filter = CSSOrderAwareFrameIterator::ChildFilter;
9180 using Order = CSSOrderAwareFrameIterator::OrderState;
9181 bool ordered = gridReflowInput.mIter.ItemsAreAlreadyInOrder();
9182 auto orderState = ordered ? Order::Ordered : Order::Unordered;
9183 iter.emplace(this, FrameChildListID::Principal, Filter::SkipPlaceholders,
9184 orderState);
9185 gridItems.emplace();
9186 for (; !iter->AtEnd(); iter->Next()) {
9187 auto child = **iter;
9188 for (const auto& info : gridReflowInput.mGridItems) {
9189 if (info.mFrame == child) {
9190 gridItems->AppendElement(info);
9195 auto sz = frameRect.Size();
9196 CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
9197 gridReflowInput.mCols, 0,
9198 gridReflowInput.mCols.mSizes.Length(), wm, sz,
9199 bp.IStart(wm), bp.IEnd(wm), desiredSize.ISize(wm));
9200 CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
9201 gridReflowInput.mRows, gridReflowInput.mStartRow,
9202 gridReflowInput.mNextFragmentStartRow, wm, sz,
9203 bp.BStart(wm), bp.BEnd(wm), desiredSize.BSize(wm));
9206 if (HasAnyStateBits(NS_STATE_GRID_COMPUTED_INFO)) {
9207 // This state bit will never be cleared, since reflow can be called
9208 // multiple times in fragmented grids, and it's challenging to scope
9209 // the bit to only that sequence of calls. This is relatively harmless
9210 // since this bit is only set by accessing a ChromeOnly property, and
9211 // therefore can't unduly slow down normal web browsing.
9213 // Clear our GridFragmentInfo property, which might be holding a stale
9214 // dom::Grid object built from previously-computed info. This will
9215 // ensure that the next call to GetGridFragments will create a new one.
9216 if (mozilla::dom::Grid* grid = TakeProperty(GridFragmentInfo())) {
9217 grid->ForgetFrame();
9220 // Now that we know column and row sizes and positions, set
9221 // the ComputedGridTrackInfo and related properties
9223 const auto* subgrid = GetProperty(Subgrid::Prop());
9224 const auto* subgridColRange = subgrid && IsSubgrid(eLogicalAxisInline)
9225 ? &subgrid->SubgridCols()
9226 : nullptr;
9228 LineNameMap colLineNameMap(
9229 gridReflowInput.mGridStyle, GetImplicitNamedAreas(),
9230 gridReflowInput.mColFunctions, nullptr, subgridColRange, true);
9231 uint32_t colTrackCount = gridReflowInput.mCols.mSizes.Length();
9232 nsTArray<nscoord> colTrackPositions(colTrackCount);
9233 nsTArray<nscoord> colTrackSizes(colTrackCount);
9234 nsTArray<uint32_t> colTrackStates(colTrackCount);
9235 nsTArray<bool> colRemovedRepeatTracks(
9236 gridReflowInput.mColFunctions.mRemovedRepeatTracks.Clone());
9237 uint32_t col = 0;
9238 for (const TrackSize& sz : gridReflowInput.mCols.mSizes) {
9239 colTrackPositions.AppendElement(sz.mPosition);
9240 colTrackSizes.AppendElement(sz.mBase);
9241 bool isRepeat =
9242 ((col >= gridReflowInput.mColFunctions.mRepeatAutoStart) &&
9243 (col < gridReflowInput.mColFunctions.mRepeatAutoEnd));
9244 colTrackStates.AppendElement(
9245 isRepeat ? (uint32_t)mozilla::dom::GridTrackState::Repeat
9246 : (uint32_t)mozilla::dom::GridTrackState::Static);
9248 col++;
9250 // Get the number of explicit tracks first. The order of argument evaluation
9251 // is implementation-defined. We should be OK here because colTrackSizes is
9252 // taken by rvalue, but computing the size first prevents any changes in the
9253 // argument types of the constructor from breaking this.
9254 const uint32_t numColExplicitTracks =
9255 IsSubgrid(eLogicalAxisInline)
9256 ? colTrackSizes.Length()
9257 : gridReflowInput.mColFunctions.NumExplicitTracks();
9258 ComputedGridTrackInfo* colInfo = new ComputedGridTrackInfo(
9259 gridReflowInput.mColFunctions.mExplicitGridOffset, numColExplicitTracks,
9260 0, col, std::move(colTrackPositions), std::move(colTrackSizes),
9261 std::move(colTrackStates), std::move(colRemovedRepeatTracks),
9262 gridReflowInput.mColFunctions.mRepeatAutoStart,
9263 colLineNameMap.GetResolvedLineNamesForComputedGridTrackInfo(),
9264 IsSubgrid(eLogicalAxisInline), IsMasonry(eLogicalAxisInline));
9265 SetProperty(GridColTrackInfo(), colInfo);
9267 const auto* subgridRowRange = subgrid && IsSubgrid(eLogicalAxisBlock)
9268 ? &subgrid->SubgridRows()
9269 : nullptr;
9270 LineNameMap rowLineNameMap(
9271 gridReflowInput.mGridStyle, GetImplicitNamedAreas(),
9272 gridReflowInput.mRowFunctions, nullptr, subgridRowRange, true);
9273 uint32_t rowTrackCount = gridReflowInput.mRows.mSizes.Length();
9274 nsTArray<nscoord> rowTrackPositions(rowTrackCount);
9275 nsTArray<nscoord> rowTrackSizes(rowTrackCount);
9276 nsTArray<uint32_t> rowTrackStates(rowTrackCount);
9277 nsTArray<bool> rowRemovedRepeatTracks(
9278 gridReflowInput.mRowFunctions.mRemovedRepeatTracks.Clone());
9279 uint32_t row = 0;
9280 for (const TrackSize& sz : gridReflowInput.mRows.mSizes) {
9281 rowTrackPositions.AppendElement(sz.mPosition);
9282 rowTrackSizes.AppendElement(sz.mBase);
9283 bool isRepeat =
9284 ((row >= gridReflowInput.mRowFunctions.mRepeatAutoStart) &&
9285 (row < gridReflowInput.mRowFunctions.mRepeatAutoEnd));
9286 rowTrackStates.AppendElement(
9287 isRepeat ? (uint32_t)mozilla::dom::GridTrackState::Repeat
9288 : (uint32_t)mozilla::dom::GridTrackState::Static);
9290 row++;
9292 // Get the number of explicit tracks first. The order of argument evaluation
9293 // is implementation-defined. We should be OK here because colTrackSizes is
9294 // taken by rvalue, but computing the size first prevents any changes in the
9295 // argument types of the constructor from breaking this.
9296 const uint32_t numRowExplicitTracks =
9297 IsSubgrid(eLogicalAxisBlock)
9298 ? rowTrackSizes.Length()
9299 : gridReflowInput.mRowFunctions.NumExplicitTracks();
9300 // Row info has to accommodate fragmentation of the grid, which may happen
9301 // in later calls to Reflow. For now, presume that no more fragmentation
9302 // will occur.
9303 ComputedGridTrackInfo* rowInfo = new ComputedGridTrackInfo(
9304 gridReflowInput.mRowFunctions.mExplicitGridOffset, numRowExplicitTracks,
9305 gridReflowInput.mStartRow, row, std::move(rowTrackPositions),
9306 std::move(rowTrackSizes), std::move(rowTrackStates),
9307 std::move(rowRemovedRepeatTracks),
9308 gridReflowInput.mRowFunctions.mRepeatAutoStart,
9309 rowLineNameMap.GetResolvedLineNamesForComputedGridTrackInfo(),
9310 IsSubgrid(eLogicalAxisBlock), IsMasonry(eLogicalAxisBlock));
9311 SetProperty(GridRowTrackInfo(), rowInfo);
9313 if (prevInFlow) {
9314 // This frame is fragmenting rows from a previous frame, so patch up
9315 // the prior GridRowTrackInfo with a new end row.
9317 // FIXME: This can be streamlined and/or removed when bug 1151204 lands.
9319 ComputedGridTrackInfo* priorRowInfo =
9320 prevInFlow->GetProperty(GridRowTrackInfo());
9322 // Adjust track positions based on the first track in this fragment.
9323 if (priorRowInfo->mPositions.Length() >
9324 priorRowInfo->mStartFragmentTrack) {
9325 nscoord delta =
9326 priorRowInfo->mPositions[priorRowInfo->mStartFragmentTrack];
9327 for (nscoord& pos : priorRowInfo->mPositions) {
9328 pos -= delta;
9332 ComputedGridTrackInfo* revisedPriorRowInfo = new ComputedGridTrackInfo(
9333 priorRowInfo->mNumLeadingImplicitTracks,
9334 priorRowInfo->mNumExplicitTracks, priorRowInfo->mStartFragmentTrack,
9335 gridReflowInput.mStartRow, std::move(priorRowInfo->mPositions),
9336 std::move(priorRowInfo->mSizes), std::move(priorRowInfo->mStates),
9337 std::move(priorRowInfo->mRemovedRepeatTracks),
9338 priorRowInfo->mRepeatFirstTrack,
9339 std::move(priorRowInfo->mResolvedLineNames), priorRowInfo->mIsSubgrid,
9340 priorRowInfo->mIsMasonry);
9341 prevInFlow->SetProperty(GridRowTrackInfo(), revisedPriorRowInfo);
9344 // Generate the line info properties. We need to provide the number of
9345 // repeat tracks produced in the reflow. Only explicit names are assigned
9346 // to lines here; the mozilla::dom::GridLines class will later extract
9347 // implicit names from grid areas and assign them to the appropriate lines.
9349 auto& colFunctions = gridReflowInput.mColFunctions;
9351 // Generate column lines first.
9352 uint32_t capacity = gridReflowInput.mCols.mSizes.Length();
9353 nsTArray<nsTArray<RefPtr<nsAtom>>> columnLineNames(capacity);
9354 for (col = 0; col <= gridReflowInput.mCols.mSizes.Length(); col++) {
9355 // Offset col by the explicit grid offset, to get the original names.
9356 nsTArray<RefPtr<nsAtom>> explicitNames =
9357 colLineNameMap.GetExplicitLineNamesAtIndex(
9358 col - colFunctions.mExplicitGridOffset);
9360 columnLineNames.EmplaceBack(std::move(explicitNames));
9362 // Get the explicit names that follow a repeat auto declaration.
9363 nsTArray<RefPtr<nsAtom>> colNamesFollowingRepeat;
9364 nsTArray<RefPtr<nsAtom>> colBeforeRepeatAuto;
9365 nsTArray<RefPtr<nsAtom>> colAfterRepeatAuto;
9366 // Note: the following is only used for a non-subgridded axis.
9367 if (colLineNameMap.HasRepeatAuto()) {
9368 MOZ_ASSERT(!colFunctions.mTemplate.IsSubgrid());
9369 // The line name list after the repeatAutoIndex holds the line names
9370 // for the first explicit line after the repeat auto declaration.
9371 uint32_t repeatAutoEnd = colLineNameMap.RepeatAutoStart() + 1;
9372 for (auto* list : colLineNameMap.ExpandedLineNames()[repeatAutoEnd]) {
9373 for (auto& name : list->AsSpan()) {
9374 colNamesFollowingRepeat.AppendElement(name.AsAtom());
9377 auto names = colLineNameMap.TrackAutoRepeatLineNames();
9378 for (auto& name : names[0].AsSpan()) {
9379 colBeforeRepeatAuto.AppendElement(name.AsAtom());
9381 for (auto& name : names[1].AsSpan()) {
9382 colAfterRepeatAuto.AppendElement(name.AsAtom());
9386 ComputedGridLineInfo* columnLineInfo = new ComputedGridLineInfo(
9387 std::move(columnLineNames), std::move(colBeforeRepeatAuto),
9388 std::move(colAfterRepeatAuto), std::move(colNamesFollowingRepeat));
9389 SetProperty(GridColumnLineInfo(), columnLineInfo);
9391 // Generate row lines next.
9392 auto& rowFunctions = gridReflowInput.mRowFunctions;
9393 capacity = gridReflowInput.mRows.mSizes.Length();
9394 nsTArray<nsTArray<RefPtr<nsAtom>>> rowLineNames(capacity);
9395 for (row = 0; row <= gridReflowInput.mRows.mSizes.Length(); row++) {
9396 // Offset row by the explicit grid offset, to get the original names.
9397 nsTArray<RefPtr<nsAtom>> explicitNames =
9398 rowLineNameMap.GetExplicitLineNamesAtIndex(
9399 row - rowFunctions.mExplicitGridOffset);
9400 rowLineNames.EmplaceBack(std::move(explicitNames));
9402 // Get the explicit names that follow a repeat auto declaration.
9403 nsTArray<RefPtr<nsAtom>> rowNamesFollowingRepeat;
9404 nsTArray<RefPtr<nsAtom>> rowBeforeRepeatAuto;
9405 nsTArray<RefPtr<nsAtom>> rowAfterRepeatAuto;
9406 // Note: the following is only used for a non-subgridded axis.
9407 if (rowLineNameMap.HasRepeatAuto()) {
9408 MOZ_ASSERT(!rowFunctions.mTemplate.IsSubgrid());
9409 // The line name list after the repeatAutoIndex holds the line names
9410 // for the first explicit line after the repeat auto declaration.
9411 uint32_t repeatAutoEnd = rowLineNameMap.RepeatAutoStart() + 1;
9412 for (auto* list : rowLineNameMap.ExpandedLineNames()[repeatAutoEnd]) {
9413 for (auto& name : list->AsSpan()) {
9414 rowNamesFollowingRepeat.AppendElement(name.AsAtom());
9417 auto names = rowLineNameMap.TrackAutoRepeatLineNames();
9418 for (auto& name : names[0].AsSpan()) {
9419 rowBeforeRepeatAuto.AppendElement(name.AsAtom());
9421 for (auto& name : names[1].AsSpan()) {
9422 rowAfterRepeatAuto.AppendElement(name.AsAtom());
9426 ComputedGridLineInfo* rowLineInfo = new ComputedGridLineInfo(
9427 std::move(rowLineNames), std::move(rowBeforeRepeatAuto),
9428 std::move(rowAfterRepeatAuto), std::move(rowNamesFollowingRepeat));
9429 SetProperty(GridRowLineInfo(), rowLineInfo);
9431 // Generate area info for explicit areas. Implicit areas are handled
9432 // elsewhere.
9433 if (!gridReflowInput.mGridStyle->mGridTemplateAreas.IsNone()) {
9434 auto* areas = new StyleOwnedSlice<NamedArea>(
9435 gridReflowInput.mGridStyle->mGridTemplateAreas.AsAreas()->areas);
9436 SetProperty(ExplicitNamedAreasProperty(), areas);
9437 } else {
9438 RemoveProperty(ExplicitNamedAreasProperty());
9442 if (!prevInFlow) {
9443 SharedGridData* sharedGridData = GetProperty(SharedGridData::Prop());
9444 if (!aStatus.IsFullyComplete()) {
9445 if (!sharedGridData) {
9446 sharedGridData = new SharedGridData;
9447 SetProperty(SharedGridData::Prop(), sharedGridData);
9449 sharedGridData->mCols.mSizes = std::move(gridReflowInput.mCols.mSizes);
9450 sharedGridData->mCols.mContentBoxSize =
9451 gridReflowInput.mCols.mContentBoxSize;
9452 sharedGridData->mCols.mBaselineSubtreeAlign =
9453 gridReflowInput.mCols.mBaselineSubtreeAlign;
9454 sharedGridData->mCols.mIsMasonry = gridReflowInput.mCols.mIsMasonry;
9455 sharedGridData->mRows.mSizes = std::move(gridReflowInput.mRows.mSizes);
9456 // Save the original row grid sizes and gaps so we can restore them later
9457 // in GridReflowInput::Initialize for the continuations.
9458 auto& origRowData = sharedGridData->mOriginalRowData;
9459 origRowData.ClearAndRetainStorage();
9460 origRowData.SetCapacity(sharedGridData->mRows.mSizes.Length());
9461 nscoord prevTrackEnd = 0;
9462 for (auto& sz : sharedGridData->mRows.mSizes) {
9463 SharedGridData::RowData data = {sz.mBase, sz.mPosition - prevTrackEnd};
9464 origRowData.AppendElement(data);
9465 prevTrackEnd = sz.mPosition + sz.mBase;
9467 sharedGridData->mRows.mContentBoxSize =
9468 gridReflowInput.mRows.mContentBoxSize;
9469 sharedGridData->mRows.mBaselineSubtreeAlign =
9470 gridReflowInput.mRows.mBaselineSubtreeAlign;
9471 sharedGridData->mRows.mIsMasonry = gridReflowInput.mRows.mIsMasonry;
9472 sharedGridData->mGridItems = std::move(gridReflowInput.mGridItems);
9473 sharedGridData->mAbsPosItems = std::move(gridReflowInput.mAbsPosItems);
9475 sharedGridData->mGenerateComputedGridInfo =
9476 HasAnyStateBits(NS_STATE_GRID_COMPUTED_INFO);
9477 } else if (sharedGridData && !GetNextInFlow()) {
9478 RemoveProperty(SharedGridData::Prop());
9482 FinishAndStoreOverflow(&aDesiredSize);
9485 void nsGridContainerFrame::UpdateSubgridFrameState() {
9486 nsFrameState oldBits = GetStateBits() & kIsSubgridBits;
9487 nsFrameState newBits = ComputeSelfSubgridMasonryBits() & kIsSubgridBits;
9488 if (newBits != oldBits) {
9489 RemoveStateBits(kIsSubgridBits);
9490 if (!newBits) {
9491 RemoveProperty(Subgrid::Prop());
9492 } else {
9493 AddStateBits(newBits);
9498 nsFrameState nsGridContainerFrame::ComputeSelfSubgridMasonryBits() const {
9499 nsFrameState bits = nsFrameState(0);
9500 const auto* pos = StylePosition();
9502 // We can only have masonry layout in one axis.
9503 if (pos->mGridTemplateRows.IsMasonry()) {
9504 bits |= NS_STATE_GRID_IS_ROW_MASONRY;
9505 } else if (pos->mGridTemplateColumns.IsMasonry()) {
9506 bits |= NS_STATE_GRID_IS_COL_MASONRY;
9509 // NOTE: The rest of this function is only relevant if we're a subgrid;
9510 // hence, we return early as soon as we rule out that possibility.
9512 // 'contain:layout/paint' makes us an "independent formatting context",
9513 // which prevents us from being a subgrid in this case (but not always).
9514 // We will also need to check our containing scroll frame for this property.
9515 // https://drafts.csswg.org/css-display-3/#establish-an-independent-formatting-context
9516 if (ShouldInhibitSubgridDueToIFC(this)) {
9517 return bits;
9520 // Skip over our scroll frame and such if we have it, to find our "parent
9521 // grid", if we have one.
9523 // After this loop, 'parent' will represent the parent of the outermost frame
9524 // that shares our content node. (Normally this is just our parent frame, but
9525 // if we're e.g. a scrolled frame, then this will be the parent of our
9526 // wrapper-scrollable-frame.) If 'parent' turns out to be a grid container,
9527 // then it's our "parent grid", and we could potentially be a subgrid of it.
9528 auto* parent = GetParent();
9529 while (parent && parent->GetContent() == GetContent()) {
9530 // If we find our containing frame (e.g. our scroll frame) can't be a
9531 // subgrid, then we can't be a subgrid, for the same reasons as above. This
9532 // can happen when this frame is itself a grid item with "overflow:scroll"
9533 // or similar.
9534 if (ShouldInhibitSubgridDueToIFC(parent)) {
9535 return bits;
9537 parent = parent->GetParent();
9539 const nsGridContainerFrame* parentGrid = do_QueryFrame(parent);
9540 if (parentGrid) {
9541 bool isOrthogonal =
9542 GetWritingMode().IsOrthogonalTo(parent->GetWritingMode());
9543 bool isColSubgrid = pos->mGridTemplateColumns.IsSubgrid();
9544 // Subgridding a parent masonry axis makes us use masonry layout too,
9545 // unless our other axis is a masonry axis.
9546 if (isColSubgrid &&
9547 parent->HasAnyStateBits(isOrthogonal ? NS_STATE_GRID_IS_ROW_MASONRY
9548 : NS_STATE_GRID_IS_COL_MASONRY)) {
9549 isColSubgrid = false;
9550 if (!HasAnyStateBits(NS_STATE_GRID_IS_ROW_MASONRY)) {
9551 bits |= NS_STATE_GRID_IS_COL_MASONRY;
9554 if (isColSubgrid) {
9555 bits |= NS_STATE_GRID_IS_COL_SUBGRID;
9558 bool isRowSubgrid = pos->mGridTemplateRows.IsSubgrid();
9559 if (isRowSubgrid &&
9560 parent->HasAnyStateBits(isOrthogonal ? NS_STATE_GRID_IS_COL_MASONRY
9561 : NS_STATE_GRID_IS_ROW_MASONRY)) {
9562 isRowSubgrid = false;
9563 if (!HasAnyStateBits(NS_STATE_GRID_IS_COL_MASONRY)) {
9564 bits |= NS_STATE_GRID_IS_ROW_MASONRY;
9567 if (isRowSubgrid) {
9568 bits |= NS_STATE_GRID_IS_ROW_SUBGRID;
9571 return bits;
9574 void nsGridContainerFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
9575 nsIFrame* aPrevInFlow) {
9576 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
9578 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
9579 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
9582 nsFrameState bits = nsFrameState(0);
9583 if (MOZ_LIKELY(!aPrevInFlow)) {
9584 bits = ComputeSelfSubgridMasonryBits();
9585 } else {
9586 bits = aPrevInFlow->GetStateBits() &
9587 (NS_STATE_GRID_IS_ROW_MASONRY | NS_STATE_GRID_IS_COL_MASONRY |
9588 kIsSubgridBits | NS_STATE_GRID_HAS_COL_SUBGRID_ITEM |
9589 NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
9591 AddStateBits(bits);
9594 void nsGridContainerFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
9595 nsContainerFrame::DidSetComputedStyle(aOldStyle);
9597 if (!aOldStyle) {
9598 return; // Init() already initialized the bits.
9600 UpdateSubgridFrameState();
9603 nscoord nsGridContainerFrame::IntrinsicISize(gfxContext* aRenderingContext,
9604 IntrinsicISizeType aType) {
9605 // Calculate the sum of column sizes under intrinsic sizing.
9606 // http://dev.w3.org/csswg/css-grid/#intrinsic-sizes
9607 NormalizeChildLists();
9608 GridReflowInput state(this, *aRenderingContext);
9609 InitImplicitNamedAreas(state.mGridStyle); // XXX optimize
9611 // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
9612 // https://drafts.csswg.org/css-grid/#auto-repeat
9613 // They're only used for auto-repeat so we skip computing them otherwise.
9614 RepeatTrackSizingInput repeatSizing(state.mWM);
9615 if (!IsColSubgrid() && state.mColFunctions.mHasRepeatAuto) {
9616 repeatSizing.InitFromStyle(eLogicalAxisInline, state.mWM,
9617 state.mFrame->Style());
9619 if ((!IsRowSubgrid() && state.mRowFunctions.mHasRepeatAuto &&
9620 !(state.mGridStyle->mGridAutoFlow & StyleGridAutoFlow::ROW)) ||
9621 IsMasonry(eLogicalAxisInline)) {
9622 // Only 'grid-auto-flow:column' can create new implicit columns, so that's
9623 // the only case where our block-size can affect the number of columns.
9624 // Masonry layout always depends on how many rows we have though.
9625 repeatSizing.InitFromStyle(eLogicalAxisBlock, state.mWM,
9626 state.mFrame->Style());
9629 Grid grid;
9630 if (MOZ_LIKELY(!IsSubgrid())) {
9631 grid.PlaceGridItems(state, repeatSizing); // XXX optimize
9632 } else {
9633 auto* subgrid = GetProperty(Subgrid::Prop());
9634 state.mGridItems = subgrid->mGridItems.Clone();
9635 state.mAbsPosItems = subgrid->mAbsPosItems.Clone();
9636 grid.mGridColEnd = subgrid->mGridColEnd;
9637 grid.mGridRowEnd = subgrid->mGridRowEnd;
9640 auto constraint = aType == IntrinsicISizeType::MinISize
9641 ? SizingConstraint::MinContent
9642 : SizingConstraint::MaxContent;
9643 if (IsMasonry(eLogicalAxisInline)) {
9644 ReflowOutput desiredSize(state.mWM);
9645 nsSize containerSize;
9646 LogicalRect contentArea(state.mWM);
9647 nsReflowStatus status;
9648 state.mRows.mSizes.SetLength(grid.mGridRowEnd);
9649 state.CalculateTrackSizesForAxis(eLogicalAxisInline, grid,
9650 NS_UNCONSTRAINEDSIZE, constraint);
9651 return MasonryLayout(state, contentArea, constraint, desiredSize, status,
9652 nullptr, containerSize);
9655 if (grid.mGridColEnd == 0) {
9656 return nscoord(0);
9659 state.CalculateTrackSizesForAxis(eLogicalAxisInline, grid,
9660 NS_UNCONSTRAINEDSIZE, constraint);
9662 if (MOZ_LIKELY(!IsSubgrid())) {
9663 return state.mCols.SumOfGridTracksAndGaps();
9665 const auto& last = state.mCols.mSizes.LastElement();
9666 return last.mPosition + last.mBase;
9669 nscoord nsGridContainerFrame::GetMinISize(gfxContext* aRC) {
9670 auto* f = static_cast<nsGridContainerFrame*>(FirstContinuation());
9671 if (f != this) {
9672 return f->GetMinISize(aRC);
9675 DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize);
9676 if (mCachedMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
9677 Maybe<nscoord> containISize = ContainIntrinsicISize();
9678 mCachedMinISize = containISize
9679 ? *containISize
9680 : IntrinsicISize(aRC, IntrinsicISizeType::MinISize);
9682 return mCachedMinISize;
9685 nscoord nsGridContainerFrame::GetPrefISize(gfxContext* aRC) {
9686 auto* f = static_cast<nsGridContainerFrame*>(FirstContinuation());
9687 if (f != this) {
9688 return f->GetPrefISize(aRC);
9691 DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize);
9692 if (mCachedPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
9693 Maybe<nscoord> containISize = ContainIntrinsicISize();
9694 mCachedPrefISize = containISize
9695 ? *containISize
9696 : IntrinsicISize(aRC, IntrinsicISizeType::PrefISize);
9698 return mCachedPrefISize;
9701 void nsGridContainerFrame::MarkIntrinsicISizesDirty() {
9702 mCachedMinISize = NS_INTRINSIC_ISIZE_UNKNOWN;
9703 mCachedPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN;
9704 for (auto& perAxisBaseline : mBaseline) {
9705 for (auto& baseline : perAxisBaseline) {
9706 baseline = NS_INTRINSIC_ISIZE_UNKNOWN;
9709 nsContainerFrame::MarkIntrinsicISizesDirty();
9712 void nsGridContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
9713 const nsDisplayListSet& aLists) {
9714 DisplayBorderBackgroundOutline(aBuilder, aLists);
9715 if (GetPrevInFlow()) {
9716 DisplayOverflowContainers(aBuilder, aLists);
9719 // Our children are all grid-level boxes, which behave the same as
9720 // inline-blocks in painting, so their borders/backgrounds all go on
9721 // the BlockBorderBackgrounds list.
9722 typedef CSSOrderAwareFrameIterator::OrderState OrderState;
9723 OrderState order =
9724 HasAnyStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
9725 ? OrderState::Ordered
9726 : OrderState::Unordered;
9727 CSSOrderAwareFrameIterator iter(
9728 this, FrameChildListID::Principal,
9729 CSSOrderAwareFrameIterator::ChildFilter::IncludeAll, order);
9730 const auto flags = DisplayFlagsForFlexOrGridItem();
9731 for (; !iter.AtEnd(); iter.Next()) {
9732 nsIFrame* child = *iter;
9733 BuildDisplayListForChild(aBuilder, child, aLists, flags);
9737 bool nsGridContainerFrame::DrainSelfOverflowList() {
9738 return DrainAndMergeSelfOverflowList();
9741 void nsGridContainerFrame::AppendFrames(ChildListID aListID,
9742 nsFrameList&& aFrameList) {
9743 NoteNewChildren(aListID, aFrameList);
9744 nsContainerFrame::AppendFrames(aListID, std::move(aFrameList));
9747 void nsGridContainerFrame::InsertFrames(
9748 ChildListID aListID, nsIFrame* aPrevFrame,
9749 const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
9750 NoteNewChildren(aListID, aFrameList);
9751 nsContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
9752 std::move(aFrameList));
9755 void nsGridContainerFrame::RemoveFrame(DestroyContext& aContext,
9756 ChildListID aListID,
9757 nsIFrame* aOldFrame) {
9758 MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list");
9760 #ifdef DEBUG
9761 SetDidPushItemsBitIfNeeded(aListID, aOldFrame);
9762 #endif
9764 nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame);
9767 StyleAlignFlags nsGridContainerFrame::CSSAlignmentForAbsPosChild(
9768 const ReflowInput& aChildRI, LogicalAxis aLogicalAxis) const {
9769 MOZ_ASSERT(aChildRI.mFrame->IsAbsolutelyPositioned(),
9770 "This method should only be called for abspos children");
9772 StyleAlignFlags alignment =
9773 (aLogicalAxis == eLogicalAxisInline)
9774 ? aChildRI.mStylePosition->UsedJustifySelf(Style())._0
9775 : aChildRI.mStylePosition->UsedAlignSelf(Style())._0;
9777 // Extract and strip the flag bits
9778 StyleAlignFlags alignmentFlags = alignment & StyleAlignFlags::FLAG_BITS;
9779 alignment &= ~StyleAlignFlags::FLAG_BITS;
9781 if (alignment == StyleAlignFlags::NORMAL) {
9782 // "the 'normal' keyword behaves as 'start' on replaced
9783 // absolutely-positioned boxes, and behaves as 'stretch' on all other
9784 // absolutely-positioned boxes."
9785 // https://drafts.csswg.org/css-align/#align-abspos
9786 // https://drafts.csswg.org/css-align/#justify-abspos
9787 alignment = aChildRI.mFrame->IsReplaced() ? StyleAlignFlags::START
9788 : StyleAlignFlags::STRETCH;
9789 } else if (alignment == StyleAlignFlags::FLEX_START) {
9790 alignment = StyleAlignFlags::START;
9791 } else if (alignment == StyleAlignFlags::FLEX_END) {
9792 alignment = StyleAlignFlags::END;
9793 } else if (alignment == StyleAlignFlags::LEFT ||
9794 alignment == StyleAlignFlags::RIGHT) {
9795 if (aLogicalAxis == eLogicalAxisInline) {
9796 const bool isLeft = (alignment == StyleAlignFlags::LEFT);
9797 WritingMode wm = GetWritingMode();
9798 alignment = (isLeft == wm.IsBidiLTR()) ? StyleAlignFlags::START
9799 : StyleAlignFlags::END;
9800 } else {
9801 alignment = StyleAlignFlags::START;
9803 } else if (alignment == StyleAlignFlags::BASELINE) {
9804 alignment = StyleAlignFlags::START;
9805 } else if (alignment == StyleAlignFlags::LAST_BASELINE) {
9806 alignment = StyleAlignFlags::END;
9809 return (alignment | alignmentFlags);
9812 nscoord nsGridContainerFrame::SynthesizeBaseline(
9813 const FindItemInGridOrderResult& aGridOrderItem, LogicalAxis aAxis,
9814 BaselineSharingGroup aGroup, const nsSize& aCBPhysicalSize, nscoord aCBSize,
9815 WritingMode aCBWM) {
9816 if (MOZ_UNLIKELY(!aGridOrderItem.mItem)) {
9817 // No item in this fragment - synthesize a baseline from our border-box.
9818 return ::SynthesizeBaselineFromBorderBox(aGroup, aCBWM, aAxis, aCBSize);
9821 nsIFrame* child = aGridOrderItem.mItem->mFrame;
9822 nsGridContainerFrame* grid = do_QueryFrame(child);
9823 auto childWM = child->GetWritingMode();
9824 bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
9825 const LogicalAxis childAxis = isOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
9826 nscoord baseline;
9827 nscoord start;
9828 nscoord size;
9830 if (aAxis == eLogicalAxisBlock) {
9831 start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM);
9832 size = child->BSize(aCBWM);
9833 if (grid && aGridOrderItem.mIsInEdgeTrack) {
9834 baseline = isOrthogonal ? grid->GetIBaseline(aGroup)
9835 : grid->GetBBaseline(aGroup);
9836 } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack) {
9837 // This assertion is mostly for documentation purposes; it must hold,
9838 // given the checks in our 'if' statements. (We know aAxis is
9839 // eLogicalAxisBlock, and isOrthogonal is false, which means childAxis
9840 // must be eLogicalAxisBlock). If instead we got here with a childAxis of
9841 // eLogicalAxisInline, then our call to
9842 // Baseline::SynthesizeBaselineFromBorderBox might incorrectly think
9843 // it makes sense to use a central baseline, in an axis where that
9844 // doesn't make sense.
9845 MOZ_ASSERT(childAxis == eLogicalAxisBlock, "unexpected childAxis");
9846 baseline = child
9847 ->GetNaturalBaselineBOffset(childWM, aGroup,
9848 BaselineExportContext::Other)
9849 .valueOrFrom([aGroup, child, childWM]() {
9850 return Baseline::SynthesizeBOffsetFromBorderBox(
9851 child, childWM, aGroup);
9853 } else {
9854 baseline =
9855 ::SynthesizeBaselineFromBorderBox(aGroup, childWM, childAxis, size);
9857 } else {
9858 start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).I(aCBWM);
9859 size = child->ISize(aCBWM);
9860 if (grid && aGridOrderItem.mIsInEdgeTrack) {
9861 baseline = isOrthogonal ? grid->GetBBaseline(aGroup)
9862 : grid->GetIBaseline(aGroup);
9863 } else if (isOrthogonal && aGridOrderItem.mIsInEdgeTrack) {
9864 baseline = child
9865 ->GetNaturalBaselineBOffset(childWM, aGroup,
9866 BaselineExportContext::Other)
9867 .valueOrFrom([aGroup, childWM, childAxis, size]() {
9868 return ::SynthesizeBaselineFromBorderBox(
9869 aGroup, childWM, childAxis, size);
9871 } else {
9872 baseline =
9873 ::SynthesizeBaselineFromBorderBox(aGroup, childWM, childAxis, size);
9876 return aGroup == BaselineSharingGroup::First
9877 ? start + baseline
9878 : aCBSize - start - size + baseline;
9881 void nsGridContainerFrame::CalculateBaselines(
9882 BaselineSet aBaselineSet, CSSOrderAwareFrameIterator* aIter,
9883 const nsTArray<GridItemInfo>* aGridItems, const Tracks& aTracks,
9884 uint32_t aFragmentStartTrack, uint32_t aFirstExcludedTrack, WritingMode aWM,
9885 const nsSize& aCBPhysicalSize, nscoord aCBBorderPaddingStart,
9886 nscoord aCBBorderPaddingEnd, nscoord aCBSize) {
9887 const auto axis = aTracks.mAxis;
9888 auto firstBaseline = aTracks.mBaseline[BaselineSharingGroup::First];
9889 if (!(aBaselineSet & BaselineSet::eFirst)) {
9890 mBaseline[axis][BaselineSharingGroup::First] =
9891 ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::First, aWM,
9892 axis, aCBSize);
9893 } else if (firstBaseline == NS_INTRINSIC_ISIZE_UNKNOWN) {
9894 FindItemInGridOrderResult gridOrderFirstItem = FindFirstItemInGridOrder(
9895 *aIter, *aGridItems,
9896 axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols,
9897 axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows,
9898 aFragmentStartTrack);
9899 mBaseline[axis][BaselineSharingGroup::First] = SynthesizeBaseline(
9900 gridOrderFirstItem, axis, BaselineSharingGroup::First, aCBPhysicalSize,
9901 aCBSize, aWM);
9902 } else {
9903 // We have a 'first baseline' group in the start track in this fragment.
9904 // Convert it from track to grid container border-box coordinates.
9905 MOZ_ASSERT(!aGridItems->IsEmpty());
9906 nscoord gapBeforeStartTrack =
9907 aFragmentStartTrack == 0
9908 ? aTracks.GridLineEdge(aFragmentStartTrack,
9909 GridLineSide::AfterGridGap)
9910 : nscoord(0); // no content gap at start of fragment
9911 mBaseline[axis][BaselineSharingGroup::First] =
9912 aCBBorderPaddingStart + gapBeforeStartTrack + firstBaseline;
9915 auto lastBaseline = aTracks.mBaseline[BaselineSharingGroup::Last];
9916 if (!(aBaselineSet & BaselineSet::eLast)) {
9917 mBaseline[axis][BaselineSharingGroup::Last] =
9918 ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::Last, aWM, axis,
9919 aCBSize);
9920 } else if (lastBaseline == NS_INTRINSIC_ISIZE_UNKNOWN) {
9921 // For finding items for the 'last baseline' we need to create a reverse
9922 // iterator ('aIter' is the forward iterator from the GridReflowInput).
9923 using Iter = ReverseCSSOrderAwareFrameIterator;
9924 auto orderState = aIter->ItemsAreAlreadyInOrder()
9925 ? Iter::OrderState::Ordered
9926 : Iter::OrderState::Unordered;
9927 Iter iter(this, FrameChildListID::Principal,
9928 Iter::ChildFilter::SkipPlaceholders, orderState);
9929 iter.SetItemCount(aGridItems->Length());
9930 FindItemInGridOrderResult gridOrderLastItem = FindLastItemInGridOrder(
9931 iter, *aGridItems,
9932 axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols,
9933 axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows,
9934 aFragmentStartTrack, aFirstExcludedTrack);
9935 mBaseline[axis][BaselineSharingGroup::Last] =
9936 SynthesizeBaseline(gridOrderLastItem, axis, BaselineSharingGroup::Last,
9937 aCBPhysicalSize, aCBSize, aWM);
9938 } else {
9939 // We have a 'last baseline' group in the end track in this fragment.
9940 // Convert it from track to grid container border-box coordinates.
9941 MOZ_ASSERT(!aGridItems->IsEmpty());
9942 auto borderBoxStartToEndOfEndTrack =
9943 aCBBorderPaddingStart +
9944 aTracks.GridLineEdge(aFirstExcludedTrack, GridLineSide::BeforeGridGap) -
9945 aTracks.GridLineEdge(aFragmentStartTrack, GridLineSide::BeforeGridGap);
9946 mBaseline[axis][BaselineSharingGroup::Last] =
9947 (aCBSize - borderBoxStartToEndOfEndTrack) + lastBaseline;
9951 #ifdef DEBUG_FRAME_DUMP
9952 nsresult nsGridContainerFrame::GetFrameName(nsAString& aResult) const {
9953 return MakeFrameName(u"GridContainer"_ns, aResult);
9956 void nsGridContainerFrame::ExtraContainerFrameInfo(nsACString& aTo) const {
9957 if (const void* const subgrid = GetProperty(Subgrid::Prop())) {
9958 aTo += nsPrintfCString(" [subgrid=%p]", subgrid);
9962 #endif
9964 /* static */ nsGridContainerFrame::FindItemInGridOrderResult
9965 nsGridContainerFrame::FindFirstItemInGridOrder(
9966 CSSOrderAwareFrameIterator& aIter, const nsTArray<GridItemInfo>& aGridItems,
9967 LineRange GridArea::*aMajor, LineRange GridArea::*aMinor,
9968 uint32_t aFragmentStartTrack) {
9969 FindItemInGridOrderResult result = {nullptr, false};
9970 uint32_t minMajor = kTranslatedMaxLine + 1;
9971 uint32_t minMinor = kTranslatedMaxLine + 1;
9972 aIter.Reset();
9973 for (; !aIter.AtEnd(); aIter.Next()) {
9974 const GridItemInfo& item = aGridItems[aIter.ItemIndex()];
9975 if ((item.mArea.*aMajor).mEnd <= aFragmentStartTrack) {
9976 continue; // item doesn't span any track in this fragment
9978 uint32_t major = (item.mArea.*aMajor).mStart;
9979 uint32_t minor = (item.mArea.*aMinor).mStart;
9980 if (major < minMajor || (major == minMajor && minor < minMinor)) {
9981 minMajor = major;
9982 minMinor = minor;
9983 result.mItem = &item;
9984 result.mIsInEdgeTrack = major == 0U;
9987 return result;
9990 /* static */ nsGridContainerFrame::FindItemInGridOrderResult
9991 nsGridContainerFrame::FindLastItemInGridOrder(
9992 ReverseCSSOrderAwareFrameIterator& aIter,
9993 const nsTArray<GridItemInfo>& aGridItems, LineRange GridArea::*aMajor,
9994 LineRange GridArea::*aMinor, uint32_t aFragmentStartTrack,
9995 uint32_t aFirstExcludedTrack) {
9996 FindItemInGridOrderResult result = {nullptr, false};
9997 int32_t maxMajor = -1;
9998 int32_t maxMinor = -1;
9999 aIter.Reset();
10000 int32_t lastMajorTrack = int32_t(aFirstExcludedTrack) - 1;
10001 for (; !aIter.AtEnd(); aIter.Next()) {
10002 const GridItemInfo& item = aGridItems[aIter.ItemIndex()];
10003 // Subtract 1 from the end line to get the item's last track index.
10004 int32_t major = (item.mArea.*aMajor).mEnd - 1;
10005 // Currently, this method is only called with aFirstExcludedTrack ==
10006 // the first track in the next fragment, so we take the opportunity
10007 // to assert this item really belongs to this fragment.
10008 MOZ_ASSERT((item.mArea.*aMajor).mStart < aFirstExcludedTrack,
10009 "found an item that belongs to some later fragment");
10010 if (major < int32_t(aFragmentStartTrack)) {
10011 continue; // item doesn't span any track in this fragment
10013 int32_t minor = (item.mArea.*aMinor).mEnd - 1;
10014 MOZ_ASSERT(minor >= 0 && major >= 0, "grid item must have span >= 1");
10015 if (major > maxMajor || (major == maxMajor && minor > maxMinor)) {
10016 maxMajor = major;
10017 maxMinor = minor;
10018 result.mItem = &item;
10019 result.mIsInEdgeTrack = major == lastMajorTrack;
10022 return result;
10025 nsGridContainerFrame::UsedTrackSizes* nsGridContainerFrame::GetUsedTrackSizes()
10026 const {
10027 return GetProperty(UsedTrackSizes::Prop());
10030 void nsGridContainerFrame::StoreUsedTrackSizes(
10031 LogicalAxis aAxis, const nsTArray<TrackSize>& aSizes) {
10032 auto* uts = GetUsedTrackSizes();
10033 if (!uts) {
10034 uts = new UsedTrackSizes();
10035 SetProperty(UsedTrackSizes::Prop(), uts);
10037 uts->mSizes[aAxis] = aSizes.Clone();
10038 uts->mCanResolveLineRangeSize[aAxis] = true;
10039 // XXX is resetting these bits necessary?
10040 for (auto& sz : uts->mSizes[aAxis]) {
10041 sz.mState &= ~(TrackSize::eFrozen | TrackSize::eSkipGrowUnlimited |
10042 TrackSize::eInfinitelyGrowable);
10046 #ifdef DEBUG
10047 void nsGridContainerFrame::SetInitialChildList(ChildListID aListID,
10048 nsFrameList&& aChildList) {
10049 ChildListIDs supportedLists = {FrameChildListID::Principal};
10050 // We don't handle the FrameChildListID::Backdrop frames in any way, but it
10051 // only contains a placeholder for ::backdrop which is OK to not reflow (for
10052 // now anyway).
10053 supportedLists += FrameChildListID::Backdrop;
10054 MOZ_ASSERT(supportedLists.contains(aListID), "unexpected child list");
10056 return nsContainerFrame::SetInitialChildList(aListID, std::move(aChildList));
10059 void nsGridContainerFrame::TrackSize::DumpStateBits(StateBits aState) {
10060 printf("min:");
10061 if (aState & eAutoMinSizing) {
10062 printf("auto-min ");
10063 } else if (aState & eMinContentMinSizing) {
10064 printf("min-content ");
10065 } else if (aState & eMaxContentMinSizing) {
10066 printf("max-content ");
10068 printf(" max:");
10069 if (aState & eAutoMaxSizing) {
10070 printf("auto ");
10071 } else if (aState & eMinContentMaxSizing) {
10072 printf("min-content ");
10073 } else if (aState & eMaxContentMaxSizing) {
10074 printf("max-content ");
10075 } else if (aState & eFlexMaxSizing) {
10076 printf("flex ");
10078 if (aState & eFrozen) {
10079 printf("frozen ");
10081 if (aState & eModified) {
10082 printf("modified ");
10084 if (aState & eBreakBefore) {
10085 printf("break-before ");
10089 void nsGridContainerFrame::TrackSize::Dump() const {
10090 printf("mPosition=%d mBase=%d mLimit=%d ", mPosition, mBase, mLimit);
10091 DumpStateBits(mState);
10094 #endif // DEBUG
10096 bool nsGridContainerFrame::GridItemShouldStretch(const nsIFrame* aChild,
10097 LogicalAxis aAxis) const {
10098 MOZ_ASSERT(aChild->IsGridItem());
10100 if (aChild->IsGridContainerFrame()) {
10101 // The subgrid is always stretched in its subgridded dimensions.
10102 // https://drafts.csswg.org/css-grid/#subgrid-box-alignment
10103 const auto* gridContainer =
10104 static_cast<const nsGridContainerFrame*>(aChild);
10105 if (gridContainer->IsSubgrid(aAxis)) {
10106 return true;
10110 const auto wm = aChild->GetWritingMode();
10111 if (aChild->StyleMargin()->HasAuto(aAxis, wm)) {
10112 // Per https://drafts.csswg.org/css-grid/#auto-margins, any 'auto' margin in
10113 // an axis disables the alignment property in that axis.
10114 return false;
10117 const auto cbwm = GetWritingMode();
10118 const bool isOrthogonal = wm.IsOrthogonalTo(cbwm);
10119 if (IsMasonry(isOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis)) {
10120 // The child is in the container's masonry-axis.
10121 // AlignJustifyTracksInMasonryAxis will stretch it, so we don't report that
10122 // here.
10123 return false;
10126 const auto* pos = aChild->StylePosition();
10127 const auto alignment = (aAxis == eLogicalAxisInline) == !isOrthogonal
10128 ? pos->UsedJustifySelf(Style())._0
10129 : pos->UsedAlignSelf(Style())._0;
10130 return alignment == StyleAlignFlags::NORMAL ||
10131 alignment == StyleAlignFlags::STRETCH;
10134 bool nsGridContainerFrame::ShouldInhibitSubgridDueToIFC(
10135 const nsIFrame* aFrame) {
10136 // Just checking for things that make us establish an independent formatting
10137 // context (IFC) and hence prevent us from being a subgrid:
10138 // * Out-of-flow (e.g. abspos) frames also establish an IFC. Note, our
10139 // NS_FRAME_OUT_OF_FLOW bit potentially isn't set yet, so we check our style.
10140 // * contain:layout and contain:paint each make us establish an IFC.
10141 const auto* display = aFrame->StyleDisplay();
10142 return display->IsAbsolutelyPositionedStyle() || display->IsContainLayout() ||
10143 display->IsContainPaint();
10146 nsGridContainerFrame* nsGridContainerFrame::GetGridContainerFrame(
10147 nsIFrame* aFrame) {
10148 nsGridContainerFrame* gridFrame = nullptr;
10150 if (aFrame) {
10151 nsIFrame* inner = aFrame;
10152 if (MOZ_UNLIKELY(aFrame->IsFieldSetFrame())) {
10153 inner = static_cast<nsFieldSetFrame*>(aFrame)->GetInner();
10155 // Since "Get" methods like GetInner and GetContentInsertionFrame can
10156 // return null, we check the return values before dereferencing. Our
10157 // calling pattern makes this unlikely, but we're being careful.
10158 nsIFrame* insertionFrame =
10159 inner ? inner->GetContentInsertionFrame() : nullptr;
10160 nsIFrame* possibleGridFrame = insertionFrame ? insertionFrame : aFrame;
10161 gridFrame = possibleGridFrame->IsGridContainerFrame()
10162 ? static_cast<nsGridContainerFrame*>(possibleGridFrame)
10163 : nullptr;
10165 return gridFrame;
10168 nsGridContainerFrame* nsGridContainerFrame::GetGridFrameWithComputedInfo(
10169 nsIFrame* aFrame) {
10170 nsGridContainerFrame* gridFrame = GetGridContainerFrame(aFrame);
10171 if (!gridFrame) {
10172 return nullptr;
10175 auto HasComputedInfo = [](const nsGridContainerFrame& aFrame) -> bool {
10176 return aFrame.HasProperty(GridColTrackInfo()) &&
10177 aFrame.HasProperty(GridRowTrackInfo()) &&
10178 aFrame.HasProperty(GridColumnLineInfo()) &&
10179 aFrame.HasProperty(GridRowLineInfo());
10182 if (HasComputedInfo(*gridFrame)) {
10183 return gridFrame;
10186 // Trigger a reflow that generates additional grid property data.
10187 // Hold onto aFrame while we do this, in case reflow destroys it.
10188 AutoWeakFrame weakFrameRef(gridFrame);
10190 RefPtr<mozilla::PresShell> presShell = gridFrame->PresShell();
10191 gridFrame->AddStateBits(NS_STATE_GRID_COMPUTED_INFO);
10192 presShell->FrameNeedsReflow(gridFrame, IntrinsicDirty::None,
10193 NS_FRAME_IS_DIRTY);
10194 presShell->FlushPendingNotifications(FlushType::Layout);
10196 // If the weakFrameRef is no longer valid, then we must bail out.
10197 if (!weakFrameRef.IsAlive()) {
10198 return nullptr;
10201 // This can happen if for some reason we ended up not reflowing, like in print
10202 // preview under some circumstances.
10203 if (MOZ_UNLIKELY(!HasComputedInfo(*gridFrame))) {
10204 return nullptr;
10207 return gridFrame;
10210 // TODO: This is a rather dumb implementation of nsILineIterator, but it's
10211 // better than our pre-existing behavior. Ideally, we should probably use the
10212 // grid information to return a meaningful number of lines etc.
10213 bool nsGridContainerFrame::IsLineIteratorFlowRTL() { return false; }
10215 int32_t nsGridContainerFrame::GetNumLines() const {
10216 return mFrames.GetLength();
10219 Result<nsILineIterator::LineInfo, nsresult> nsGridContainerFrame::GetLine(
10220 int32_t aLineNumber) {
10221 if (aLineNumber < 0 || aLineNumber >= GetNumLines()) {
10222 return Err(NS_ERROR_FAILURE);
10224 LineInfo rv;
10225 nsIFrame* f = mFrames.FrameAt(aLineNumber);
10226 rv.mLineBounds = f->GetRect();
10227 rv.mFirstFrameOnLine = f;
10228 rv.mNumFramesOnLine = 1;
10229 return rv;
10232 int32_t nsGridContainerFrame::FindLineContaining(nsIFrame* aFrame,
10233 int32_t aStartLine) {
10234 const int32_t index = mFrames.IndexOf(aFrame);
10235 if (index < 0) {
10236 return -1;
10238 if (index < aStartLine) {
10239 return -1;
10241 return index;
10244 NS_IMETHODIMP
10245 nsGridContainerFrame::CheckLineOrder(int32_t aLine, bool* aIsReordered,
10246 nsIFrame** aFirstVisual,
10247 nsIFrame** aLastVisual) {
10248 *aIsReordered = false;
10249 *aFirstVisual = nullptr;
10250 *aLastVisual = nullptr;
10251 return NS_OK;
10254 NS_IMETHODIMP
10255 nsGridContainerFrame::FindFrameAt(int32_t aLineNumber, nsPoint aPos,
10256 nsIFrame** aFrameFound,
10257 bool* aPosIsBeforeFirstFrame,
10258 bool* aPosIsAfterLastFrame) {
10259 const auto wm = GetWritingMode();
10260 const LogicalPoint pos(wm, aPos, GetSize());
10262 *aFrameFound = nullptr;
10263 *aPosIsBeforeFirstFrame = true;
10264 *aPosIsAfterLastFrame = false;
10266 nsIFrame* f = mFrames.FrameAt(aLineNumber);
10267 if (!f) {
10268 return NS_OK;
10271 auto rect = f->GetLogicalRect(wm, GetSize());
10272 *aFrameFound = f;
10273 *aPosIsBeforeFirstFrame = pos.I(wm) < rect.IStart(wm);
10274 *aPosIsAfterLastFrame = pos.I(wm) > rect.IEnd(wm);
10275 return NS_OK;