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"
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
);
71 inline Span
<const StyleOwnedSlice
<StyleCustomIdent
>>
72 GridTemplate::LineNameLists(bool aIsSubgrid
) const {
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.
81 MOZ_ASSERT(IsNone() || IsMasonry() || (IsSubgrid() && !aIsSubgrid
));
86 inline const StyleTrackBreadth
& StyleTrackSize::GetMax() const {
93 MOZ_ASSERT(IsFitContent());
94 return AsFitContent();
98 inline const StyleTrackBreadth
& StyleTrackSize::GetMin() const {
99 static const StyleTrackBreadth kAuto
= StyleTrackBreadth::Auto();
101 // <flex> behaves like minmax(auto, <flex>)
102 return AsBreadth().IsFr() ? kAuto
: AsBreadth();
105 return AsMinmax()._0
;
107 MOZ_ASSERT(IsFitContent());
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
);
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();
139 aStatus
->SetIncomplete();
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
)) {
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
,
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
)
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
) {
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
);
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
);
258 enum class GridLineSide
{
263 struct nsGridContainerFrame::TrackSize
{
264 enum StateBits
: uint16_t {
266 eAutoMinSizing
= 0x1,
267 eMinContentMinSizing
= 0x2,
268 eMaxContentMinSizing
= 0x4,
269 eMinOrMaxContentMinSizing
= eMinContentMinSizing
| eMaxContentMinSizing
,
270 eIntrinsicMinSizing
= eMinOrMaxContentMinSizing
| eAutoMinSizing
,
272 eAutoMaxSizing
= 0x10,
273 eMinContentMaxSizing
= 0x20,
274 eMaxContentMaxSizing
= 0x40,
275 eAutoOrMaxContentMaxSizing
= eAutoMaxSizing
| eMaxContentMaxSizing
,
276 eIntrinsicMaxSizing
= eAutoOrMaxContentMaxSizing
| eMinContentMaxSizing
,
277 eFlexMaxSizing
= 0x80,
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
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.
294 // This bit says that the corresponding item has `auto` margin(s).
295 eItemHasAutoMargin
= 0x4,
299 StateBits
Initialize(nscoord aPercentageBasis
, const StyleTrackSize
&);
300 bool IsFrozen() const { return mState
& eFrozen
; }
302 static void DumpStateBits(StateBits aState
);
306 static bool IsDefiniteMaxSizing(StateBits aStateBits
) {
307 return (aStateBits
& (eIntrinsicMaxSizing
| eFlexMaxSizing
)) == 0;
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
;
319 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits
)
322 std::is_trivially_copyable
<nsGridContainerFrame::TrackSize
>::value
,
323 "Must be trivially copyable");
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
) {
362 mState
|= eAutoMinSizing
;
364 case Tag::MinContent
:
365 mState
|= eMinContentMinSizing
;
367 case Tag::MaxContent
:
368 mState
|= eMaxContentMinSizing
;
371 MOZ_ASSERT(!min
.IsFr(), "<flex> min-sizing is invalid as a track size");
372 mBase
= ::ResolveToDefiniteSize(min
, aPercentageBasis
);
374 switch (maxSizeTag
) {
376 mState
|= eAutoMaxSizing
;
377 mLimit
= NS_UNCONSTRAINEDSIZE
;
379 case Tag::MinContent
:
380 case Tag::MaxContent
:
381 mState
|= maxSizeTag
== Tag::MinContent
? eMinContentMaxSizing
382 : eMaxContentMaxSizing
;
383 mLimit
= NS_UNCONSTRAINEDSIZE
;
386 mState
|= eFlexMaxSizing
;
390 mLimit
= ::ResolveToDefiniteSize(max
, aPercentageBasis
);
391 if (mLimit
< mBase
) {
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
) {
417 MOZ_ASSERT(aEnd
>= kMinLine
&& aEnd
<= kMaxLine
, "invalid span");
419 MOZ_ASSERT(aStart
>= kMinLine
&& aStart
<= kMaxLine
,
420 "invalid start line");
421 MOZ_ASSERT(aEnd
== int32_t(kAutoLine
) ||
422 (aEnd
>= kMinLine
&& aEnd
<= kMaxLine
),
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'");
434 MOZ_ASSERT(mEnd
>= 1 && mEnd
< uint32_t(kMaxLine
), "invalid span");
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?");
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
;
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
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());
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
;
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
545 int32_t mUntranslatedStart
;
549 int32_t mUntranslatedEnd
;
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
);
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
;
588 struct nsGridContainerFrame::GridItemInfo
{
590 * Item state per axis.
592 enum StateBits
: uint16_t {
593 // Does the item span a flex track?
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,
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
|
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,
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)
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
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
];
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());
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
];
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
;
730 mFrame
->IsTableWrapperFrame()
731 ? mFrame
->PrincipalChildList().FirstChild()->StylePosition()
732 : mFrame
->StylePosition();
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() ||
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
)) {
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() ||
757 aContainerWM
.IsOrthogonalTo(mFrame
->GetWritingMode()) &&
758 minSize
.BehavesLikeInitialValueOnBlockAxis());
759 return isAuto
&& !mFrame
->StyleDisplay()->IsScrollableOverflow();
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
;
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
836 // It's mutable since we update the value fairly late (just before reflowing
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
;
882 void GridItemInfo::InhibitSubgrid(nsGridContainerFrame
* aParent
,
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
);
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
)
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.
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
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
);
953 lines
.AdjustAbsPosForRemovedTracks(aNumRemovedTracks
);
955 lines
.AdjustForRemovedTracks(aNumRemovedTracks
);
958 auto* subgrid
= SubgridFrame()->GetProperty(Subgrid::Prop());
960 auto& lines
= subgrid
->mArea
.LineRangeForAxis(aAxis
);
962 lines
.AdjustAbsPosForRemovedTracks(aNumRemovedTracks
);
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
,
996 /** Helper function for the above method */
997 void ResolveSubgridTrackSizesForAxis(nsGridContainerFrame
* aFrame
,
998 LogicalAxis aAxis
, Subgrid
* aSubgrid
,
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
;
1014 void nsGridContainerFrame::GridItemInfo::Dump() const {
1015 auto Dump1
= [this](const char* aMsg
, LogicalAxis aAxis
) {
1016 auto state
= mState
[aAxis
];
1021 if (state
& ItemState::eEdgeBits
) {
1022 printf("subgrid-adjacent-edges(");
1023 if (state
& ItemState::eStartEdge
) {
1026 if (state
& ItemState::eEndEdge
) {
1031 if (state
& ItemState::eAutoPlacement
) {
1032 printf("masonry-auto ");
1034 if (state
& ItemState::eIsSubgrid
) {
1037 if (state
& ItemState::eIsFlexing
) {
1040 if (state
& ItemState::eApplyAutoMinSize
) {
1041 printf("auto-min-size ");
1043 if (state
& ItemState::eClampMarginBoxMinSize
) {
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()));
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
);
1071 * Encapsulates CSS track-sizing functions.
1073 struct nsGridContainerFrame::TrackSizingFunctions
{
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");
1088 ExpandNonRepeatAutoTracks();
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());
1106 TrackSizingFunctions(const GridTemplate
& aGridTemplate
,
1107 const StyleImplicitGridTracks
& aAutoSizing
,
1109 : TrackSizingFunctions(aGridTemplate
, aAutoSizing
,
1110 aGridTemplate
.RepeatAutoIndex(), aIsSubgrid
) {}
1113 enum { ForSubgridFallbackTag
};
1114 TrackSizingFunctions(const GridTemplate
& aGridTemplate
,
1115 const StyleImplicitGridTracks
& aAutoSizing
,
1116 decltype(ForSubgridFallbackTag
))
1117 : TrackSizingFunctions(aGridTemplate
, aAutoSizing
, Nothing(),
1118 /* aIsSubgrid */ true) {}
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
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
)
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
,
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
)
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
;
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;
1192 uint32_t repeatTracks
=
1193 CalculateRepeatFillCount(aGridGap
, aMinSize
, aSize
, aMaxSize
) *
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
) {
1205 uint32_t CalculateRepeatFillCount(
1206 const NonNegativeLengthPercentageOrNormal
& aGridGap
, nscoord aMinSize
,
1207 nscoord aSize
, nscoord aMaxSize
) const {
1208 if (!mHasRepeatAuto
) {
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
1228 nscoord maxFill
= aSize
!= NS_UNCONSTRAINEDSIZE
? aSize
: aMaxSize
;
1229 if (maxFill
== NS_UNCONSTRAINEDSIZE
&& aMinSize
== 0) {
1230 // "Otherwise, the specified track list repeats only once."
1233 nscoord repeatTrackSum
= 0;
1234 // Note that one repeat() track size is included in |sum| in this loop.
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()) {
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
;
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"
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
));
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());
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
;
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()];
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, ...)`
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);
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);
1426 for (auto j
: IntegerRange(repeat
.count
.AsNumber())) {
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
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
1473 class MOZ_STACK_CLASS
nsGridContainerFrame::LineNameMap
{
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
),
1498 mIsSameDirection(aIsSameDirection
),
1499 mHasRepeatAuto(aTracks
.mHasRepeatAuto
) {
1500 if (MOZ_UNLIKELY(aRange
)) { // subgrid case
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;
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
1528 mTemplateLinesEnd
= mExpandedLineNames
.Length() -
1529 (mTrackAutoRepeatLineNames
.Length() - 2) +
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
));
1561 const auto& value
= trackListValues
[i
];
1562 if (value
.IsTrackSize()) {
1563 mExpandedLineNames
.AppendElement(std::move(names
));
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];
1581 for (auto j
: IntegerRange(repeat
.count
.AsNumber())) {
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
;
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
));
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
) {
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
=
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
) {
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
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 {
1696 MOZ_ASSERT(!aName
->IsEmpty());
1697 MOZ_ASSERT(aNth
&& *aNth
!= 0);
1699 return FindLine(aName
, aNth
, aFromIndex
, aImplicitLines
);
1701 int32_t nth
= -*aNth
;
1702 int32_t line
= RFindLine(aName
, &nth
, aFromIndex
, aImplicitLines
);
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
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
);
1726 if (MOZ_LIKELY(sameDirectionAsThis
)) {
1729 line
= max
- line
+ 1;
1731 aImplicitLines
.AppendElement(line
);
1733 auto* parent
= map
->mParentLineNameMap
;
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
];
1741 const auto start
= j
;
1742 while (j
< aImplicitLines
.Length() && aImplicitLines
[j
] == prev
) {
1746 aImplicitLines
.RemoveElementsAt(start
, j
- start
);
1752 if (MOZ_UNLIKELY(!map
->mIsSameDirection
)) {
1753 aSide
= GetOppositeSide(aSide
);
1754 sameDirectionAsThis
= !sameDirectionAsThis
;
1756 min
= map
->TranslateToParentMap(min
);
1757 max
= map
->TranslateToParentMap(max
);
1759 MOZ_ASSERT(!map
->mIsSameDirection
);
1760 std::swap(min
, max
);
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;
1773 if (map
->mAreas
&& map
->mAreas
->has(aName
)) {
1776 map
= map
->mParentLineNameMap
;
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
));
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());
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
;
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
;
1840 uint32_t i
= aFromIndex
;
1841 for (; i
< end
; i
= line
) {
1843 if (Contains(i
, aName
) || aImplicitLines
.Contains(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])
1855 return implicitLine
;
1859 MOZ_ASSERT(nth
> 0, "should have returned a valid line above already");
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
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
) {
1886 if (implicitLine
< aFromIndex
) {
1888 return implicitLine
;
1892 for (uint32_t i
= std::min(aFromIndex
, end
); i
; --i
) {
1893 if (Contains(i
- 1, aName
) || aImplicitLines
.Contains(i
)) {
1899 MOZ_ASSERT(nth
> 0, "should have returned a valid line above already");
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;
1908 if (aIndex
< map
->mTemplateLinesEnd
&& map
->HasNameAt(aIndex
, aName
)) {
1911 auto* parent
= map
->mParentLineNameMap
;
1915 uint32_t line
= map
->TranslateToParentMap(aIndex
+ 1);
1916 MOZ_ASSERT(line
>= 1, "expected a 1-based line number");
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
) {
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
)) {
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
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
]);
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
);
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
) {
2022 } else if (end
>= aMin
) {
2030 } else if (start
<= 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()) {
2046 const auto areas
= mStylePosition
->mGridTemplateAreas
.AsAreas();
2047 for (const NamedArea
& area
: areas
->areas
.AsSpan()) {
2048 if (area
.name
.AsAtom() == aName
) {
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
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}),
2095 mCanResolveLineRangeSize(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
;
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
{
2186 ContentBasedMinimums
,
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
{
2197 TrackSize::StateBits mState
;
2198 LineRange mLineRange
;
2200 nscoord mMinContentContribution
;
2201 nscoord mMaxContentContribution
;
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 {
2212 case TrackSizingPhase::IntrinsicMinimums
:
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");
2227 "SpanningItemData { mSpan: %d, mState: %d, mLineRange: (%d, %d), "
2228 "mMinSize: %d, mMinContentContribution: %d, mMaxContentContribution: "
2230 mSpan
, mState
, mLineRange
.mStart
, mLineRange
.mEnd
, mMinSize
,
2231 mMinContentContribution
, mMaxContentContribution
, mFrame
);
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
) {
2274 case TrackSizingPhase::IntrinsicMinimums
:
2275 case TrackSizingPhase::ContentBasedMinimums
:
2276 case TrackSizingPhase::MaxContentMinimums
:
2278 case TrackSizingPhase::IntrinsicMaximums
:
2279 case TrackSizingPhase::MaxContentMaximums
:
2280 if (aSize
.mLimit
== NS_UNCONSTRAINEDSIZE
) {
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
);
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
];
2347 case TrackSizingPhase::IntrinsicMinimums
:
2348 case TrackSizingPhase::ContentBasedMinimums
:
2349 case TrackSizingPhase::MaxContentMinimums
:
2350 sz
.mBase
= plan
.mBase
;
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
;
2361 case TrackSizingPhase::MaxContentMaximums
:
2362 if (plan
.mState
& TrackSize::eModified
) {
2363 sz
.mLimit
= plan
.mBase
;
2365 sz
.mState
&= ~TrackSize::eInfinitelyGrowable
;
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();
2383 nscoord spacePerTrack
= std::max
<nscoord
>(space
/ numGrowable
, 1);
2384 for (uint32_t track
: aGrowableTracks
) {
2385 TrackSize
& sz
= aPlan
[track
];
2386 if (sz
.IsFrozen()) {
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
;
2402 sz
.mState
|= TrackSize::eFrozen
;
2403 if (--numGrowable
== 0) {
2408 space
-= spacePerTrack
;
2410 MOZ_ASSERT(space
>= 0);
2416 MOZ_ASSERT_UNREACHABLE("we don't exit the loop above except by return");
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;
2446 sz
.mState
|= aSkipFlag
;
2447 MOZ_ASSERT(numGrowable
!= 0);
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
;
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"
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
);
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
)) {
2522 delta
= newBase
- sz
.mBase
;
2523 MOZ_ASSERT(delta
>= 0, "track size shouldn't shrink");
2524 sz
.mState
|= TrackSize::eSkipGrowUnlimited1
;
2530 MOZ_ASSERT(space
>= 0);
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
);
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)) {
2579 if (aAvailableSize
== NS_UNCONSTRAINEDSIZE
) {
2580 for (TrackSize
& sz
: mSizes
) {
2581 sz
.mBase
= sz
.mLimit
;
2584 // Compute free space and count growable tracks.
2585 nscoord space
= aAvailableSize
;
2586 uint32_t numGrowable
= numTracks
;
2587 for (const TrackSize
& sz
: mSizes
) {
2589 MOZ_ASSERT(sz
.mBase
<= sz
.mLimit
);
2590 if (sz
.mBase
== sz
.mLimit
) {
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
) {
2603 nscoord newBase
= sz
.mBase
+ spacePerTrack
;
2604 if (newBase
>= sz
.mLimit
) {
2605 space
-= sz
.mLimit
- sz
.mBase
;
2606 sz
.mBase
= sz
.mLimit
;
2609 space
-= spacePerTrack
;
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");
2674 MOZ_ASSERT(aLine
<= mSizes
.Length(), "mSizes is too small");
2675 if (aSide
== GridLineSide::BeforeGridGap
) {
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 {
2694 for (const TrackSize
& size
: mSizes
) {
2695 result
+= size
.mBase
;
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;
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
;
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");
2748 aRange
.ToPositionAndLength(mSizes
, &pos
, &size
);
2756 CopyableAutoTArray
<TrackSize
, 32> mSizes
;
2757 nscoord mContentBoxSize
;
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
;
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.
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
);
2781 for (uint32_t i
= 0, len
= mSizes
.Length(); i
< len
; ++i
) {
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
);
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
{
2808 : mCols(eLogicalAxisInline
),
2809 mRows(eLogicalAxisBlock
),
2810 mGenerateComputedGridInfo(false) {}
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()) {
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
) {
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
;
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());
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)
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
) {
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
;
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
));
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
,
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
);
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
)) {
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
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");
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");
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
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
;
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
;
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. */
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
;
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
),
3288 mSharedGridData(nullptr),
3289 mBorderPadding(aWM
),
3292 mNextFragmentStartRow(0),
3293 mSkipSides(aFrame
->GetWritingMode()),
3295 mInFragmentainer(false) {
3296 MOZ_ASSERT(!aReflowInput
|| aReflowInput
->mFrame
== mFrame
);
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
) {
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
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
,
3528 if (StringEndsWith(nsDependentAtomString(aString
), aSuffix
)) {
3529 *aIndex
= aString
->GetLength() - aSuffix
.Length();
3530 return *aIndex
!= 0;
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 {
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.
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
) {
3597 uint32_t IsEmptyRow(uint32_t aRow
) const {
3598 if (aRow
>= mCells
.Length()) {
3601 for (const Cell
& cell
: mCells
[aRow
]) {
3602 if (cell
.mIsOccupied
) {
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 " : ". ");
3623 nsTArray
<nsTArray
<Cell
>> mCells
;
3627 * State for each cell in the grid.
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
3656 uint32_t mExplicitGridOffsetCol
;
3657 uint32_t mExplicitGridOffsetRow
;
3660 * Our parent grid if any.
3662 const Grid
* mParentGrid
;
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());
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
,
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
);
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
);
3733 aSubgrid
->mIsOrthogonal
? GetOrthogonalAxis(aSubgridAxis
) : aSubgridAxis
;
3734 const auto& parentSizes
= aUsedTrackSizes
->mSizes
[parentAxis
];
3735 MOZ_ASSERT(aUsedTrackSizes
->mCanResolveLineRangeSize
[parentAxis
]);
3736 if (parentSizes
.IsEmpty()) {
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
),
3761 SubgridComputeMarginBorderPadding(info
, pmPercentageBasis
);
3763 const LogicalMargin
& mbp
= aSubgrid
->mMarginBorderPadding
;
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
;
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
]) {
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
3816 auto* parent
= aFrame
->ParentGridContainerForSubgrid();
3817 auto* parentSizes
= parent
->GetUsedTrackSizes();
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
);
3833 if (aFrame
->IsSubgrid(aAxis
)) {
3834 CopyUsedTrackSizes(mSizes
[aAxis
], parent
, parentSizes
, aFrame
, subgrid
,
3836 mCanResolveLineRangeSize
[aAxis
] = true;
3838 const auto& range
= subgrid
->mArea
.LineRangeForAxis(parentAxis
);
3839 nscoord contentBoxSize
= range
.ToLength(parentSizes
->mSizes
[parentAxis
]);
3840 auto parentWM
= aFrame
->GetParent()->GetWritingMode();
3842 subgrid
->mMarginBorderPadding
.StartEnd(parentAxis
, parentWM
);
3843 contentBoxSize
= std::max(nscoord(0), contentBoxSize
);
3844 ResolveSubgridTrackSizesForAxis(aFrame
, aAxis
, subgrid
, aRC
,
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();
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;
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
);
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
,
3899 useParentGaps
= gapStyle
.IsNormal();
3901 fallbackTrackSizing
.emplace(TrackSizingFunctions::ForSubgridFallback(
3902 mFrame
, subgrid
, parent
, parentAxis
));
3903 tracks
.Initialize(*fallbackTrackSizing
, gapStyle
, gridEnd
,
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(
3920 fallbackTrackSizing
? *fallbackTrackSizing
: sizingFunctions
,
3922 aAxis
== eLogicalAxisInline
? &GridArea::mCols
: &GridArea::mRows
,
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
,
3958 } else if (!useParentGaps
) {
3959 const nscoord gridGap
= tracks
.mGridGap
;
3961 for (TrackSize
& sz
: tracks
.mSizes
) {
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
),
3981 CalculateTrackSizesForAxis(eLogicalAxisBlock
, aGrid
, aContentBox
.BSize(mWM
),
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.
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
,
4039 bool isOrthogonal
= aCBWM
.IsOrthogonalTo(childWM
);
4040 LogicalAxis axis
= isOrthogonal
? eLogicalAxisInline
: eLogicalAxisBlock
;
4041 AlignJustifySelf(aAlignSelf
, axis
, flags
, baselineAdjust
, aCBSize
, aRI
, aSize
,
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
) {
4070 aCBWM
.IsBidiLTR() ? StyleAlignFlags::START
: StyleAlignFlags::END
;
4071 } else if (aJustifySelf
== StyleAlignFlags::RIGHT
) {
4073 aCBWM
.IsBidiLTR() ? StyleAlignFlags::END
: StyleAlignFlags::START
;
4074 } else if (aJustifySelf
== StyleAlignFlags::BASELINE
||
4075 aJustifySelf
== StyleAlignFlags::LAST_BASELINE
) {
4076 aJustifySelf
= aGridItem
.GetSelfBaseline(aJustifySelf
, eLogicalAxisInline
,
4080 bool isOrthogonal
= aCBWM
.IsOrthogonalTo(childWM
);
4081 LogicalAxis axis
= isOrthogonal
? eLogicalAxisBlock
: eLogicalAxisInline
;
4082 AlignJustifySelf(aJustifySelf
, axis
, flags
, baselineAdjust
, aCBSize
, aRI
,
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
) {
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
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
);
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());
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");
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,
4171 // Lazily create the ImplicitNamedAreas.
4173 aAreas
= new nsGridContainerFrame::ImplicitNamedAreas
;
4174 SetProperty(nsGridContainerFrame::ImplicitNamedAreasProperty(), aAreas
);
4177 RefPtr
<nsAtom
> name
= NS_Atomize(areaName
);
4178 auto addPtr
= aAreas
->lookupForAdd(name
);
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.
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
);
4215 MOZ_ASSERT(nameList
.IsLineNames());
4216 AddImplicitNamedAreasInternal(nameList
.AsLineNames(), areas
);
4220 if (count
>= size_t(kMaxLine
)) {
4226 void nsGridContainerFrame::InitImplicitNamedAreas(
4227 const nsStylePosition
* aStyle
) {
4228 ImplicitNamedAreas
* areas
= GetImplicitNamedAreas();
4230 // Clear it, but reuse the hashtable itself for now. We'll remove it
4231 // below if it isn't needed anymore.
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());
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
;
4266 // <integer> was omitted; treat it as 1.
4269 bool isNameOnly
= !aLine
.is_span
&& aLine
.line_num
== 0;
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");
4282 lineName
.AppendLiteral("-end");
4284 RefPtr
<nsAtom
> name
= NS_Atomize(lineName
);
4285 line
= aNameMap
.FindNamedLine(name
, &aNth
, aFromIndex
, implicitLines
);
4290 // If LineName() ends in -start/-end, try the prefix as a named area.
4291 AutoTArray
<uint32_t, 16> implicitLines
;
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
,
4306 MOZ_ASSERT(aNth
!= 0, "we found all N named lines but 'line' is zero!");
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
;
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
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
;
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()) {
4375 return LinePair(start
, 1); // XXX subgrid explicit size instead of 1?
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?
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
);
4401 int32_t nth
= aEnd
.line_num
== 0 ? 1 : aEnd
.line_num
;
4403 if (MOZ_UNLIKELY(start
< 0)) {
4404 if (aEnd
.LineName()->IsEmpty()) {
4405 return LinePair(start
, start
+ nth
);
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
));
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);
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
4460 r
.first
= clamped(r
.first
, aNameMap
.mClampMinLine
, aNameMap
.mClampMaxLine
);
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();
4487 ResolveLineRange(itemStyle
->mGridColumnStart
, itemStyle
->mGridColumnEnd
,
4488 aColLineNameMap
, eLogicalAxisInline
, mExplicitGridColEnd
,
4490 ResolveLineRange(itemStyle
->mGridRowStart
, itemStyle
->mGridRowEnd
,
4491 aRowLineNameMap
, eLogicalAxisBlock
, mExplicitGridRowEnd
,
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
);
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
);
4529 ResolveLineRange(aStart
, aEnd
, aNameMap
, aAxis
, aExplicitGridEnd
, aStyle
);
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()) {
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
) {
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'.
4594 void nsGridContainerFrame::Grid::PlaceAutoCol(uint32_t aStartCol
,
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
,
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.
4631 void nsGridContainerFrame::Grid::PlaceAutoRow(uint32_t aStartRow
,
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
) {
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
) {
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
;
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
)) {
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
;
4725 (*trackAdjust
)[line
] = numEmptyLines
;
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());
4755 subgrid
= new Subgrid(aGridItem
.mArea
, isOrthogonal
, aParentState
.mWM
);
4756 childGrid
->SetProperty(Subgrid::Prop(), subgrid
);
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()
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
);
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
4850 ParentLineMapForAxis(subgrid
->mIsOrthogonal
, eLogicalAxisInline
);
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.
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
);
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;
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
4885 ParentLineMapForAxis(subgrid
->mIsOrthogonal
, eLogicalAxisBlock
);
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.
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(
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()) {
4968 item
.MaybeInhibitSubgridInMasonry(aState
.mFrame
, gridAxisTrackCount
);
4970 if (item
.IsSubgrid()) {
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
;
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
);
5009 item
.MaybeInhibitSubgridInMasonry(aState
.mFrame
, gridAxisTrackCount
);
5011 if (item
.IsSubgrid()) {
5013 grid
.SubgridPlaceGridItems(aState
, this, item
);
5015 mCellMap
.Fill(area
);
5016 SetSubgridChildEdgeBits(item
);
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.
5051 if (minor
.mStart
< cursorMinor
) {
5054 cursorMinor
= minor
.mStart
;
5056 (this->*placeAutoMajorFunc
)(cursorMajor
, &area
, clampMaxMajorLine
);
5058 cursorMajor
= major
.mStart
;
5061 // Items with 'auto' in both dimensions.
5063 PlaceAutoAutoInRowOrder(cursorMinor
, cursorMajor
, &area
,
5064 clampMaxColLine
, clampMaxRowLine
);
5066 PlaceAutoAutoInColOrder(cursorMajor
, cursorMinor
, &area
,
5067 clampMaxColLine
, clampMaxRowLine
);
5070 cursorMajor
= major
.mStart
;
5071 cursorMinor
= minor
.mEnd
;
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");
5084 item
.MaybeInhibitSubgridInMasonry(aState
.mFrame
, gridAxisTrackCount
);
5086 if (item
.IsSubgrid()) {
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().)
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(
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
;
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
;
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
;
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
;
5162 area
.mRows
.mEnd
= std::min(area
.mRows
.mEnd
, 1U);
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()) {
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
) {
5222 item
.AdjustForRemovedTracks(eLogicalAxisInline
, *colAdjust
);
5225 item
.AdjustForRemovedTracks(eLogicalAxisBlock
, *rowAdjust
);
5228 for (auto& item
: aState
.mAbsPosItems
) {
5230 item
.AdjustForRemovedTracks(eLogicalAxisInline
, *colAdjust
);
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
));
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(
5312 LogicalSize(parent
->GetWritingMode(), 0, NS_UNCONSTRAINEDSIZE
),
5313 ReflowInput::InitFlag::DummyParentReflowInput
);
5314 rs
= dummyParentState
.ptr();
5317 // This will suppress various ABSURD_SIZE warnings for this reflow.
5318 parent
->SetProperty(nsContainerFrame::DebugReflowingWithInfiniteISize(),
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(),
5339 aChild
->RemoveProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
5341 ReflowInput
childRI(pc
, *rs
, aChild
, aAvailableSize
, Some(aCBSize
), {}, {},
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
;
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
);
5367 "[perf] MeasuringReflow accepted cached value=%d, child=%p, "
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
);
5379 parent
->RemoveProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
5382 GridItemCachedBAxisMeasurement::CanCacheMeasurement(aChild
, aCBSize
)) {
5383 GridItemCachedBAxisMeasurement
cachedMeasurement(aChild
, aCBSize
,
5384 childSize
.BSize(wm
));
5385 aChild
->SetProperty(GridItemCachedBAxisMeasurement::Prop(),
5388 "[perf] MeasuringReflow created new cached value=%d, child=%p, "
5390 cachedMeasurement
.BSize(), aChild
,
5391 aCBSize
.ISize(aChild
->GetWritingMode()));
5393 if (GridItemCachedBAxisMeasurement::CanCacheMeasurement(aChild
, aCBSize
)) {
5394 cachedMeasurement
.Update(aChild
, aCBSize
, childSize
.BSize(wm
));
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(),
5403 aChild
->RemoveProperty(GridItemCachedBAxisMeasurement::Prop());
5405 "[perf] MeasuringReflow rejected and removed cached value, "
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(),
5433 aChild
->RemoveProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
5435 ReflowInput
ri(pc
, aReflowInput
, aChild
, aAvailableSize
, Some(aCBSize
), {},
5437 if (aChildAxis
== eLogicalAxisBlock
) {
5438 ri
.SetComputedBSize(ri
.ApplyMinMaxBSize(aNewContentBoxSize
));
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
);
5485 skipStartSide
|= childRange
.mStart
!= 0;
5486 skipEndSide
|= childRange
.mEnd
!= parentRange
.Extent();
5488 skipEndSide
|= childRange
.mStart
!= 0;
5489 skipStartSide
|= childRange
.mEnd
!= parentRange
.Extent();
5491 if (skipStartSide
&& skipEndSide
) {
5495 parentSubgrid
->mMarginBorderPadding
.ConvertTo(subgridCBWM
, parentCBWM
);
5496 if (skipStartSide
) {
5497 mbp
.Start(aAxis
, subgridCBWM
) = nscoord(0);
5500 mbp
.End(aAxis
, subgridCBWM
) = nscoord(0);
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
;
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
)
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
);
5556 aAxis
== eLogicalAxisBlock
? aState
.mRows
: aState
.mCols
;
5557 auto gapDelta
= subgridGap
- tracks
.mGridGap
;
5558 if (!itemEdgeBits
) {
5559 extraMargin
+= gapDelta
;
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());
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())
5599 : eLogicalAxisInline
;
5600 uts
->ResolveTrackSizesForAxis(subgridFrame
, subgridAxis
, *aRC
);
5601 if (uts
->mCanResolveLineRangeSize
[subgridAxis
]) {
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
;
5611 MOZ_ASSERT(originalItem
, "huh?");
5612 const auto& range
= originalItem
->mArea
.LineRangeForAxis(subgridAxis
);
5614 range
.ToPositionAndLength(uts
->mSizes
[subgridAxis
], &pos
, &sz
);
5615 if (childWM
.IsOrthogonalTo(subgridFrame
->GetWritingMode())) {
5617 cbSize
.BSize(childWM
) = sz
;
5618 if (aGridItem
.mState
[aAxis
] & ItemState::eClampMarginBoxMinSize
) {
5623 cbSize
.ISize(childWM
) = sz
;
5624 if (aGridItem
.mState
[aAxis
] & ItemState::eClampMarginBoxMinSize
) {
5629 } else if (aState
.mCols
.mCanResolveLineRangeSize
) {
5630 nscoord sz
= aState
.mCols
.ResolveSize(aGridItem
.mArea
.mCols
);
5633 cbSize
.BSize(childWM
) = sz
;
5634 if (aGridItem
.mState
[aAxis
] & ItemState::eClampMarginBoxMinSize
) {
5639 cbSize
.ISize(childWM
) = sz
;
5640 if (aGridItem
.mState
[aAxis
] & ItemState::eClampMarginBoxMinSize
) {
5645 if (isOrthogonal
== (aAxis
== eLogicalAxisInline
)) {
5646 bMinSizeClamp
= aMinSizeClamp
;
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
,
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
);
5708 static nscoord
MaxContentContribution(const GridItemInfo
& aGridItem
,
5709 const GridReflowInput
& aState
,
5710 gfxContext
* aRC
, WritingMode aCBWM
,
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
);
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()) {
5753 MinContentContribution(aGridItem
, aState
, aRC
, aCBWM
, aAxis
, aCache
);
5754 aCache
->mMinSize
.emplace(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
;
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
);
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
,
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
;
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)
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
];
5857 if (sz
.mState
& TrackSize::eAutoMinSizing
) {
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
)
5866 .Resolve(aPercentageBasis
);
5867 aGridItem
.mState
[mAxis
] |= ItemState::eClampMarginBoxMinSize
;
5869 if (aConstraint
!= SizingConstraint::MaxContent
) {
5870 s
= MinContentContribution(aGridItem
, aState
, rc
, wm
, mAxis
, &cache
);
5872 s
= MaxContentContribution(aGridItem
, aState
, rc
, wm
, mAxis
, &cache
);
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
);
5887 if (sz
.mState
& TrackSize::eMinContentMaxSizing
) {
5888 auto s
= MinContentContribution(aGridItem
, aState
, rc
, wm
, mAxis
, &cache
);
5889 if (sz
.mLimit
== NS_UNCONSTRAINEDSIZE
) {
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
) {
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
)
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()) {
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.
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
);
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);
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
;
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.
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.
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
=
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
;
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.
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
) {
6069 sameSide
== (alignContent
== StyleAlignFlags::BASELINE
);
6070 } else if (selfAlignment
== StyleAlignFlags::END
||
6071 selfAlignment
== StyleAlignFlags::FLEX_END
) {
6073 sameSide
== (alignContent
== StyleAlignFlags::LAST_BASELINE
);
6077 const GridArea
& area
= gridItem
.mArea
;
6078 if (alignContent
== StyleAlignFlags::BASELINE
) {
6079 state
|= ItemState::eFirstBaseline
| ItemState::eContentBaseline
;
6081 isInlineAxis
? area
.mCols
.mStart
: area
.mRows
.mStart
;
6082 } else if (alignContent
== StyleAlignFlags::LAST_BASELINE
) {
6083 state
|= ItemState::eLastBaseline
| ItemState::eContentBaseline
;
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
;
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
);
6133 isInlineAxis
? child
->ISize(containerWM
) : child
->BSize(containerWM
);
6134 auto margin
= child
->GetLogicalUsedMargin(containerWM
);
6136 frameSize
+ (isInlineAxis
? margin
.IStartEnd(containerWM
)
6137 : margin
.BStartEnd(containerWM
));
6139 Maybe
<nscoord
> baseline
;
6141 baseline
.emplace((isOrthogonal
== isInlineAxis
)
6142 ? grid
->GetBBaseline(baselineAlignment
)
6143 : grid
->GetIBaseline(baselineAlignment
));
6145 baseline
= child
->GetNaturalBaselineBOffset(
6146 childWM
, baselineAlignment
, BaselineExportContext::Other
);
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;
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
));
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
);
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
});
6202 state
&= ~ItemState::eAllBaselineBits
;
6207 (state
& (ItemState::eFirstBaseline
| ItemState::eLastBaseline
)) !=
6208 (ItemState::eFirstBaseline
| ItemState::eLastBaseline
),
6209 "first/last baseline bits are mutually exclusive");
6211 (state
& (ItemState::eSelfBaseline
| ItemState::eContentBaseline
)) !=
6212 (ItemState::eSelfBaseline
| ItemState::eContentBaseline
),
6213 "*-self and *-content baseline bits are mutually exclusive");
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()) {
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.
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
)) {
6265 if (area
.LineRangeForAxis(mAxis
).mStart
> 0 ||
6266 (gridItem
.mState
[mAxis
] & ItemState::eIsBaselineAligned
)) {
6272 ->UsedTracksAlignment(
6273 mAxis
, area
.LineRangeForAxis(GetOrthogonalAxis(mAxis
)).mStart
)
6275 if (!aSet
.MatchTrackAlignment(trackAlign
)) {
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;
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
;
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...
6321 nsGridContainerFrame
* grid
= do_QueryFrame(child
);
6322 if (state
& ItemState::eFirstBaseline
) {
6324 if (isOrthogonal
== isInlineAxis
) {
6325 baseline
= grid
->GetBBaseline(BaselineSharingGroup::First
);
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
);
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.
6342 aTrackSizes
[gridItem
.mArea
6343 .LineRangeForAxis(GetOrthogonalAxis(mAxis
))
6347 alignSize
= frameSize
;
6348 aFirstBaselineItems
.AppendElement(ItemBaselineData(
6349 {baselineTrack
, baseline
, alignSize
, &gridItem
}));
6351 state
&= ~ItemState::eAllBaselineBits
;
6355 if (isOrthogonal
== isInlineAxis
) {
6356 baseline
= grid
->GetBBaseline(BaselineSharingGroup::Last
);
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
);
6367 aSet
.mTrackAlignmentSet
== BaselineAlignmentSet::EndStretch
) {
6368 // Convert to distance from border-box end.
6369 state
|= ItemState::eEndSideBaseline
;
6371 child
->GetLogicalNormalPosition(wm
, aContainerSize
);
6372 baseline
+= pos
.Pos(mAxis
, wm
);
6374 aTrackSizes
[gridItem
.mArea
6375 .LineRangeForAxis(GetOrthogonalAxis(mAxis
))
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
) {
6386 child
->GetLogicalNormalPosition(wm
, aContainerSize
);
6387 baseline
+= pos
.B(wm
);
6389 if (aSet
.mTrackAlignmentSet
== BaselineAlignmentSet::EndStretch
) {
6390 state
|= ItemState::eEndSideBaseline
;
6393 baseline
+ ((state
& ItemState::eEndSideBaseline
)
6394 ? (isInlineAxis
? m
.IEnd(wm
) : m
.BEnd(wm
))
6395 : (isInlineAxis
? m
.IStart(wm
) : m
.BStart(wm
)));
6397 frameSize
+ (isInlineAxis
? m
.IStartEnd(wm
) : m
.BStartEnd(wm
));
6398 aLastBaselineItems
.AppendElement(
6399 ItemBaselineData({baselineTrack
, descent
, alignSize
, &gridItem
}));
6401 state
&= ~ItemState::eAllBaselineBits
;
6406 (state
& (ItemState::eFirstBaseline
| ItemState::eLastBaseline
)) !=
6407 (ItemState::eFirstBaseline
| ItemState::eLastBaseline
),
6408 "first/last baseline bits are mutually exclusive");
6410 (state
& (ItemState::eSelfBaseline
| ItemState::eContentBaseline
)) !=
6411 (ItemState::eSelfBaseline
| ItemState::eContentBaseline
),
6412 "*-self and *-content baseline bits are mutually exclusive");
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 {
6443 auto state
= aGridItem
.mState
[mAxis
];
6444 if (!(state
& ItemState::eIsBaselineAligned
)) {
6447 const GridArea
& area
= aGridItem
.mArea
;
6448 int32_t baselineTrack
;
6449 const bool isFirstBaseline
= state
& ItemState::eFirstBaseline
;
6450 if (isFirstBaseline
) {
6452 mAxis
== eLogicalAxisBlock
? area
.mRows
.mStart
: area
.mCols
.mStart
;
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;
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
)) {
6494 if (isMaxSizingPhase
) {
6495 for (auto i
: item
.mLineRange
.Range()) {
6496 aPlan
[i
].mState
|= TrackSize::eModified
;
6499 nscoord space
= item
.SizeContributionForPhase
<phase
>();
6503 aTracks
.ClearAndRetainStorage();
6504 space
= CollectGrowable
<phase
>(space
, item
.mLineRange
, aSelector
, aTracks
);
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()) {
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);
6591 SubgridComputeMarginBorderPadding(gridItem
, percentageBasis
);
6592 LogicalMargin mbp
= SubgridAccumulatedMarginBorderPadding(
6593 gridItem
.SubgridFrame(), subgrid
, wm
, mAxis
);
6596 AddSubgridContribution(mSizes
[lineRange
.mStart
],
6597 mbp
.StartEnd(mAxis
, wm
));
6599 AddSubgridContribution(mSizes
[lineRange
.mStart
], mbp
.Start(mAxis
, wm
));
6600 AddSubgridContribution(mSizes
[lineRange
.mEnd
- 1], mbp
.End(mAxis
, wm
));
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
;
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(
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
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
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.
6682 auto fitContentClamper
= [&aFunctions
, aPercentageBasis
](uint32_t aTrack
,
6685 nscoord fitContentLimit
= ::ResolveToDefiniteSize(
6686 aFunctions
.MaxSizingFor(aTrack
), aPercentageBasis
);
6687 if (*aSize
> fitContentLimit
) {
6688 *aSize
= std::max(aMinSize
, fitContentLimit
);
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.
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.
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.
6736 GrowSizeForSpanningItems
<TrackSizingPhase::MaxContentMinimums
>(
6737 spanGroupStart
, spanGroupEnd
, tracks
, plan
, itemPlan
, selector
);
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
,
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();
6789 leftOverSpace
-= sz
.mBase
;
6790 if (leftOverSpace
<= 0) {
6796 float hypotheticalFrSize
;
6797 nsTArray
<uint32_t> flexTracks(aFlexTracks
.Clone());
6798 uint32_t numFlexTracks
= flexTracks
.Length();
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
;
6815 if (numFlexTracks
== 0 || leftOverSpace
<= 0) {
6819 // break; XXX (bug 1176621 comment 16) measure which is more common
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
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) {
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
);
6875 FindFrUnitSize(range
, itemFlexTracks
, aFunctions
, spaceToFill
);
6876 fr
= std::max(fr
, itemFr
);
6882 void nsGridContainerFrame::Tracks::StretchFlexibleTracks(
6883 GridReflowInput
& aState
, nsTArray
<GridItemInfo
>& aGridItems
,
6884 const TrackSizingFunctions
& aFunctions
, nscoord aAvailableSize
) {
6885 if (aAvailableSize
<= 0) {
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()) {
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).
6912 float fr
= FindUsedFlexFraction(aState
, aGridItems
, flexTracks
, aFunctions
,
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
);
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
);
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...
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()) {
6970 auto len
= mSizes
.Length();
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
];
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
;
7010 if (mSizes
.IsEmpty()) {
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;
7026 if (alignment
!= StyleAlignFlags::START
) {
7027 nscoord trackSizeSum
= 0;
7028 if (aIsSubgriddedAxis
) {
7029 numAutoTracks
= mSizes
.Length();
7031 for (const TrackSize
& sz
: mSizes
) {
7032 trackSizeSum
+= sz
.mBase
;
7033 if (sz
.mState
& TrackSize::eAutoMaxSizing
) {
7038 space
= aContentBoxSize
- trackSizeSum
- SumOfGridGaps();
7039 // Use the fallback value instead when applicable.
7041 (alignment
== StyleAlignFlags::SPACE_BETWEEN
&& mSizes
.Length() == 1)) {
7042 auto fallback
= ::GetAlignJustifyFallbackIfAny(aAligmentStyleValue
, aWM
,
7043 isAlign
, &overflowSafe
);
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.
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
) {
7064 } else if (alignment
== StyleAlignFlags::END
) {
7067 } else if (alignment
== StyleAlignFlags::CENTER
) {
7070 } else if (alignment
== StyleAlignFlags::STRETCH
) {
7071 distribute
= numAutoTracks
!= 0;
7074 for (TrackSize
& sz
: mSizes
) {
7076 pos
+= sz
.mBase
+ mGridGap
;
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.
7092 nscoord spacePerTrack
;
7093 roundingError
= NSCoordDivRem(space
, numAutoTracks
, &spacePerTrack
);
7095 for (TrackSize
& sz
: mSizes
) {
7097 if (!(sz
.mState
& TrackSize::eAutoMaxSizing
)) {
7098 pos
+= sz
.mBase
+ mGridGap
;
7101 nscoord stretch
= spacePerTrack
;
7102 if (roundingError
) {
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.
7122 pos
+= newBase
+ mGridGap
;
7125 MOZ_ASSERT(!roundingError
, "we didn't distribute all rounding error?");
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
);
7134 } else if (alignment
== StyleAlignFlags::SPACE_EVENLY
) {
7135 roundingError
= NSCoordDivRem(space
, mSizes
.Length() + 1, &between
);
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
) {
7145 nscoord spacing
= between
;
7146 if (roundingError
) {
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];
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
) {
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);
7195 if (mStart
== kAutoLine
) {
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
)) {
7202 ToPositionAndLength(aTracks
.mSizes
, &pos
, aLength
);
7203 *aPos
= aGridOrigin
+ pos
;
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
,
7283 aArea
.mRows
.ToPositionAndLengthForAbsPos(mRows
, aGridOrigin
.B(mWM
), &b
,
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
) {
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
);
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
;
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
7382 nscoord boxSizingAdjust
=
7383 child
->GetLogicalUsedBorderAndPadding(wm
).StartEnd(
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
) {
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 ?
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
)
7451 if (marginStyle
->mMargin
.End(masonryChildAxis
, childWM
)
7454 nscoord roundingError
= NSCoordDivRem(spaceToFill
, 2, &half
);
7455 margin
.Start(masonryChildAxis
, childWM
) = half
;
7456 margin
.End(masonryChildAxis
, childWM
) = half
+ roundingError
;
7458 margin
.Start(masonryChildAxis
, childWM
) = spaceToFill
;
7462 marginStyle
->mMargin
.End(masonryChildAxis
, childWM
).IsAuto());
7463 margin
.End(masonryChildAxis
, childWM
) = spaceToFill
;
7465 nsMargin
* propValue
=
7466 child
->GetProperty(nsIFrame::UsedMarginProperty());
7468 *propValue
= margin
.GetPhysicalMargin(childWM
);
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
) {
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()) {
7516 WritingMode wm
= aState
.mWM
;
7517 const ReflowInput
* cbRI
= gridRI
->mCBReflowInput
;
7518 for (; cbRI
; cbRI
= cbRI
->mCBReflowInput
) {
7519 nsIScrollableFrame
* sf
= do_QueryFrame(cbRI
->mFrame
);
7523 if (wm
.IsOrthogonalTo(cbRI
->GetWritingMode())) {
7526 LayoutFrameType frameType
= cbRI
->mFrame
->Type();
7527 if ((frameType
== LayoutFrameType::Canvas
&&
7528 PresContext()->IsPaginated()) ||
7529 frameType
== LayoutFrameType::ColumnSet
) {
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
);
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();
7549 bSize
= gridRI
->ApplyMinMaxBSize(bSize
);
7552 aState
.mRows
.GridLineEdge(numRows
, GridLineSide::BeforeGridGap
);
7553 data
->mCanBreakAtEnd
= bSize
> gridEnd
&& bSize
> aState
.mFragBStart
;
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());
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
]
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
);
7626 aChild
->RemoveProperty(aProp
);
7629 SetProp(eLogicalAxisBlock
,
7630 isOrthogonal
? IBaselinePadProperty() : BBaselinePadProperty());
7631 SetProp(eLogicalAxisInline
,
7632 isOrthogonal
? BBaselinePadProperty() : IBaselinePadProperty());
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.)
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
;
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
));
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
);
7707 aChild
->SetProperty(FragStretchBSizeProperty(), *aStretchBSize
);
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
) {
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
};
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
)
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
,
7757 JustifySelf(*aGridItemInfo
, align
, aCBSize
, wm
, childRI
, size
, flags
,
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
);
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.
7798 std::stable_sort(sortedItems
.begin(), sortedItems
.end(),
7799 GridItemInfo::IsStartRowLessThan
);
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
,
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
)) {
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
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
) {
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
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
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
)) {
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.
7901 aState
.mRows
.GridLineEdge(endRow
, GridLineSide::BeforeGridGap
);
7903 if (aFragmentainer
.mIsAutoBSize
) {
7904 // We only apply min-bsize once all rows are complete (when bsize is auto).
7905 if (endRow
< numRows
) {
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.
7917 bSize
= aState
.mReflowInput
->ApplyMinMaxBSize(bEndRow
);
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
;
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
;
7936 availableSize
-= bpBEnd
;
7938 // Pretend we have at least 1px available size, otherwise we'll never make
7939 // progress in consuming our bSize.
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();
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.
7959 aState
.mRows
.GridLineEdge(endRow
, GridLineSide::BeforeGridGap
);
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.
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();
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
);
8011 for (auto& sz
: aState
.mCols
.mSizes
) {
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)
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.
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
);
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;
8060 auto& sz
= aState
.mRows
.mSizes
[row
];
8061 // and only min-/max-content rows or flex rows in an auto-sized
8063 rowCanGrow
= (sz
.mState
& TrackSize::eMinOrMaxContentMinSizing
) ||
8064 ((sz
.mState
& TrackSize::eFlexMaxSizing
) &&
8065 aFragmentainer
.mIsAutoBSize
);
8068 maxRowSize
= gridAvailableSize
- aState
.mBorderPadding
.BEnd(wm
);
8070 maxRowSize
= gridAvailableSize
;
8072 maxRowSize
-= sz
.mPosition
;
8073 // ...and only if there is space for it to grow.
8074 rowCanGrow
= maxRowSize
> sz
.mBase
;
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.
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()) {
8111 !child
->GetPrevInFlow(),
8112 "continuations should never report InlineBreak::Before status");
8113 MOZ_ASSERT(!aFragmentainer
.mIsTopOfPage
,
8114 "got IsInlineBreakBefore() at top of page");
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
);
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();
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
);
8141 if (!isRowTopOfPage
) {
8142 // We can break before this row - restart with it as the new end row.
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();
8153 NS_ERROR("got InlineBreak::Before at top-of-page");
8154 childStatus
.Reset();
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
);
8176 auto childWM
= child
->GetWritingMode();
8178 !childWM
.IsOrthogonalTo(wm
) ? eLogicalAxisInline
: eLogicalAxisBlock
;
8179 auto normalPos
= child
->GetLogicalNormalPosition(wm
, aContainerSize
);
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();
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
));
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
;
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
)
8310 sortedItems
.AppendElement(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
);
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
) {
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
) {
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
) {
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
) {
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.
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
;
8438 if (!isAutoPlaced
) {
8442 gridAxisRange
.mStart
= start
;
8443 gridAxisRange
.mEnd
= start
+ extent
;
8444 bool isFirstItem
= true;
8445 for (uint32_t i
: gridAxisRange
.Range()) {
8447 isFirstItem
= false;
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;
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();
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
);
8491 // @return the distance from the masonry-box start to the end of the margin-
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
);
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
]
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
,
8546 consumeAllSpace
= handleChildStatus(item
, childStatus
);
8547 if (aStatus
.IsInlineBreakBefore()) {
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
;
8564 nscoord pos
= consumeAllSpace
? toFragmentainerEnd
8565 : offsetToMarginBoxEnd(child
);
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
) {
8605 contentArea
.Start(masonryAxis
, wm
) = pos
;
8606 child
= child
->GetPlaceholderFrame();
8607 ReflowInFlowChild(child
, nullptr, aContainerSize
, Nothing(), nullptr,
8608 aState
, contentArea
, aDesiredSize
, childStatus
);
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
) {
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
,
8663 bool consumeAllSpace
= handleChildStatus(item
, childStatus
);
8664 if (aStatus
.IsInlineBreakBefore()) {
8665 return fragStartPos
;
8668 consumeAllSpace
? toFragmentainerEnd
: offsetToMarginBoxEnd(child
);
8670 LogicalSize
percentBasis(
8671 aState
.PercentageBasisFor(eLogicalAxisInline
, *item
));
8672 IntrinsicISizeType type
= aConstraint
== SizingConstraint::MaxContent
8673 ? IntrinsicISizeType::PrefISize
8674 : IntrinsicISizeType::MinISize
;
8676 ::ContentContribution(*item
, aState
, &aState
.mRenderingContext
, wm
,
8677 masonryAxis
, Some(percentBasis
), type
);
8678 pos
= sz
+ masonrySizes
[masonryRange
.mStart
].mPosition
;
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
) {
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
) {
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()
8741 MOZ_ASSERT(IsSubgrid());
8742 nsIFrame
* p
= GetParent();
8743 while (p
->GetContent() == GetContent()) {
8746 MOZ_ASSERT(p
->IsGridContainerFrame());
8747 auto* parent
= static_cast<nsGridContainerFrame
*>(p
);
8748 MOZ_ASSERT(parent
->HasSubgridItems());
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()) {
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.
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
);
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
);
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());
8840 child
->SetProperty(GridItemContainingBlockRect(), cb
);
8842 *cb
= itemCB
.GetPhysicalRect(wm
, gridCBPhysicalSize
);
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.
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
);
8861 void nsGridContainerFrame::Reflow(nsPresContext
* aPresContext
,
8862 ReflowOutput
& aDesiredSize
,
8863 const ReflowInput
& aReflowInput
,
8864 nsReflowStatus
& aStatus
) {
8865 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
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
)) {
8878 NormalizeChildLists();
8881 mDidPushItemsBitMayLie
= false;
8882 SanityCheckChildListsBeforeReflow();
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
);
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
);
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
);
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;
8921 if (MOZ_LIKELY(!prevInFlow
)) {
8923 if (MOZ_LIKELY(!IsSubgrid())) {
8924 RepeatTrackSizingInput
repeatSizing(aReflowInput
.ComputedMinSize(),
8926 aReflowInput
.ComputedMaxSize());
8927 grid
.PlaceGridItems(gridReflowInput
, repeatSizing
);
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
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
;
8949 const LogicalSize
containLogicalSize(wm
, computedISize
, trackSizingBSize
);
8950 gridReflowInput
.CalculateTrackSizes(grid
, containLogicalSize
,
8951 SizingConstraint::NoConstraint
);
8953 bSize
= *containBSize
;
8955 if (IsMasonry(eLogicalAxisBlock
)) {
8956 bSize
= computedBSize
;
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
) {
8965 bSize
+= gridReflowInput
.mRows
.SumOfGridGaps();
8966 } else if (computedBSize
== NS_UNCONSTRAINEDSIZE
) {
8967 bSize
= gridReflowInput
.mRows
.GridLineEdge(
8968 rowSizes
.Length(), GridLineSide::BeforeGridGap
);
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
8979 if (Maybe
<nscoord
> containBSize
=
8980 aReflowInput
.mFrame
->ContainIntrinsicBSize()) {
8981 bSize
= *containBSize
;
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
);
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
,
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
,
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();
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
));
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
,
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()
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());
9238 for (const TrackSize
& sz
: gridReflowInput
.mCols
.mSizes
) {
9239 colTrackPositions
.AppendElement(sz
.mPosition
);
9240 colTrackSizes
.AppendElement(sz
.mBase
);
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
);
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()
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());
9280 for (const TrackSize
& sz
: gridReflowInput
.mRows
.mSizes
) {
9281 rowTrackPositions
.AppendElement(sz
.mPosition
);
9282 rowTrackSizes
.AppendElement(sz
.mBase
);
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
);
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
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
);
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
) {
9326 priorRowInfo
->mPositions
[priorRowInfo
->mStartFragmentTrack
];
9327 for (nscoord
& pos
: priorRowInfo
->mPositions
) {
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
9433 if (!gridReflowInput
.mGridStyle
->mGridTemplateAreas
.IsNone()) {
9434 auto* areas
= new StyleOwnedSlice
<NamedArea
>(
9435 gridReflowInput
.mGridStyle
->mGridTemplateAreas
.AsAreas()->areas
);
9436 SetProperty(ExplicitNamedAreasProperty(), areas
);
9438 RemoveProperty(ExplicitNamedAreasProperty());
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
);
9491 RemoveProperty(Subgrid::Prop());
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)) {
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"
9534 if (ShouldInhibitSubgridDueToIFC(parent
)) {
9537 parent
= parent
->GetParent();
9539 const nsGridContainerFrame
* parentGrid
= do_QueryFrame(parent
);
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.
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
;
9555 bits
|= NS_STATE_GRID_IS_COL_SUBGRID
;
9558 bool isRowSubgrid
= pos
->mGridTemplateRows
.IsSubgrid();
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
;
9568 bits
|= NS_STATE_GRID_IS_ROW_SUBGRID
;
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();
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
);
9594 void nsGridContainerFrame::DidSetComputedStyle(ComputedStyle
* aOldStyle
) {
9595 nsContainerFrame::DidSetComputedStyle(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());
9630 if (MOZ_LIKELY(!IsSubgrid())) {
9631 grid
.PlaceGridItems(state
, repeatSizing
); // XXX optimize
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) {
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());
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
9680 : IntrinsicISize(aRC
, IntrinsicISizeType::MinISize
);
9682 return mCachedMinISize
;
9685 nscoord
nsGridContainerFrame::GetPrefISize(gfxContext
* aRC
) {
9686 auto* f
= static_cast<nsGridContainerFrame
*>(FirstContinuation());
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
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
;
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");
9761 SetDidPushItemsBitIfNeeded(aListID
, aOldFrame
);
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
;
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
;
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");
9847 ->GetNaturalBaselineBOffset(childWM
, aGroup
,
9848 BaselineExportContext::Other
)
9849 .valueOrFrom([aGroup
, child
, childWM
]() {
9850 return Baseline::SynthesizeBOffsetFromBorderBox(
9851 child
, childWM
, aGroup
);
9855 ::SynthesizeBaselineFromBorderBox(aGroup
, childWM
, childAxis
, size
);
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
) {
9865 ->GetNaturalBaselineBOffset(childWM
, aGroup
,
9866 BaselineExportContext::Other
)
9867 .valueOrFrom([aGroup
, childWM
, childAxis
, size
]() {
9868 return ::SynthesizeBaselineFromBorderBox(
9869 aGroup
, childWM
, childAxis
, size
);
9873 ::SynthesizeBaselineFromBorderBox(aGroup
, childWM
, childAxis
, size
);
9876 return aGroup
== BaselineSharingGroup::First
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
,
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
,
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
,
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(
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
);
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
);
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;
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
)) {
9983 result
.mItem
= &item
;
9984 result
.mIsInEdgeTrack
= major
== 0U;
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;
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
)) {
10018 result
.mItem
= &item
;
10019 result
.mIsInEdgeTrack
= major
== lastMajorTrack
;
10025 nsGridContainerFrame::UsedTrackSizes
* nsGridContainerFrame::GetUsedTrackSizes()
10027 return GetProperty(UsedTrackSizes::Prop());
10030 void nsGridContainerFrame::StoreUsedTrackSizes(
10031 LogicalAxis aAxis
, const nsTArray
<TrackSize
>& aSizes
) {
10032 auto* uts
= GetUsedTrackSizes();
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
);
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
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
) {
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 ");
10069 if (aState
& eAutoMaxSizing
) {
10071 } else if (aState
& eMinContentMaxSizing
) {
10072 printf("min-content ");
10073 } else if (aState
& eMaxContentMaxSizing
) {
10074 printf("max-content ");
10075 } else if (aState
& eFlexMaxSizing
) {
10078 if (aState
& eFrozen
) {
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
);
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
)) {
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.
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
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;
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
)
10168 nsGridContainerFrame
* nsGridContainerFrame::GetGridFrameWithComputedInfo(
10169 nsIFrame
* aFrame
) {
10170 nsGridContainerFrame
* gridFrame
= GetGridContainerFrame(aFrame
);
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
)) {
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()) {
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
))) {
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
);
10225 nsIFrame
* f
= mFrames
.FrameAt(aLineNumber
);
10226 rv
.mLineBounds
= f
->GetRect();
10227 rv
.mFirstFrameOnLine
= f
;
10228 rv
.mNumFramesOnLine
= 1;
10232 int32_t nsGridContainerFrame::FindLineContaining(nsIFrame
* aFrame
,
10233 int32_t aStartLine
) {
10234 const int32_t index
= mFrames
.IndexOf(aFrame
);
10238 if (index
< aStartLine
) {
10245 nsGridContainerFrame::CheckLineOrder(int32_t aLine
, bool* aIsReordered
,
10246 nsIFrame
** aFirstVisual
,
10247 nsIFrame
** aLastVisual
) {
10248 *aIsReordered
= false;
10249 *aFirstVisual
= nullptr;
10250 *aLastVisual
= nullptr;
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
);
10271 auto rect
= f
->GetLogicalRect(wm
, GetSize());
10273 *aPosIsBeforeFirstFrame
= pos
.I(wm
) < rect
.IStart(wm
);
10274 *aPosIsAfterLastFrame
= pos
.I(wm
) > rect
.IEnd(wm
);