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"
13 #include <stdlib.h> // for div()
14 #include <type_traits>
15 #include "gfxContext.h"
16 #include "mozilla/AutoRestore.h"
17 #include "mozilla/Baseline.h"
18 #include "mozilla/ComputedStyle.h"
19 #include "mozilla/CSSAlignUtils.h"
20 #include "mozilla/StaticPrefs_layout.h"
21 #include "mozilla/dom/Grid.h"
22 #include "mozilla/dom/GridBinding.h"
23 #include "mozilla/IntegerRange.h"
24 #include "mozilla/Maybe.h"
25 #include "mozilla/PodOperations.h" // for PodZero
26 #include "mozilla/Poison.h"
27 #include "mozilla/PresShell.h"
28 #include "nsAbsoluteContainingBlock.h"
29 #include "nsAlgorithm.h" // for clamped()
30 #include "nsCSSAnonBoxes.h"
31 #include "nsCSSFrameConstructor.h"
32 #include "nsTHashMap.h"
33 #include "nsDisplayList.h"
34 #include "nsHashKeys.h"
35 #include "nsFieldSetFrame.h"
36 #include "nsIFrameInlines.h"
37 #include "nsPlaceholderFrame.h"
38 #include "nsPresContext.h"
39 #include "nsReadableUtils.h"
40 #include "nsTableWrapperFrame.h"
42 using namespace mozilla
;
44 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags
;
45 typedef nsGridContainerFrame::TrackSize TrackSize
;
46 typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags
;
48 using GridTemplate
= StyleGridTemplateComponent
;
49 using TrackListValue
=
50 StyleGenericTrackListValue
<LengthPercentage
, StyleInteger
>;
51 using TrackRepeat
= StyleGenericTrackRepeat
<LengthPercentage
, StyleInteger
>;
52 using NameList
= StyleOwnedSlice
<StyleCustomIdent
>;
53 using SizingConstraint
= nsGridContainerFrame::SizingConstraint
;
54 using GridItemCachedBAxisMeasurement
=
55 nsGridContainerFrame::CachedBAxisMeasurement
;
57 static mozilla::LazyLogModule
gGridContainerLog("GridContainer");
58 #define GRID_LOG(...) \
59 MOZ_LOG(gGridContainerLog, LogLevel::Debug, (__VA_ARGS__));
61 static const int32_t kMaxLine
= StyleMAX_GRID_LINE
;
62 static const int32_t kMinLine
= StyleMIN_GRID_LINE
;
63 // The maximum line number, in the zero-based translated grid.
64 static const uint32_t kTranslatedMaxLine
= uint32_t(kMaxLine
- kMinLine
);
65 static const uint32_t kAutoLine
= kTranslatedMaxLine
+ 3457U;
67 static const nsFrameState kIsSubgridBits
=
68 (NS_STATE_GRID_IS_COL_SUBGRID
| NS_STATE_GRID_IS_ROW_SUBGRID
);
73 inline Span
<const StyleOwnedSlice
<StyleCustomIdent
>>
74 GridTemplate::LineNameLists(bool aIsSubgrid
) const {
76 return AsTrackList()->line_names
.AsSpan();
78 if (IsSubgrid() && aIsSubgrid
) {
79 return AsSubgrid()->names
.AsSpan();
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-baselines
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.
165 static nscoord
SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup
,
167 nscoord aBorderBoxSize
) {
168 if (aGroup
== BaselineSharingGroup::First
) {
169 return aWM
.IsAlphabeticalBaseline() ? aBorderBoxSize
: aBorderBoxSize
/ 2;
171 MOZ_ASSERT(aGroup
== BaselineSharingGroup::Last
);
172 // Round up for central baseline offset, to be consistent with eFirst.
173 return aWM
.IsAlphabeticalBaseline()
175 : (aBorderBoxSize
/ 2) + (aBorderBoxSize
% 2);
178 // The input sizes for calculating the number of repeat(auto-fill/fit) tracks.
179 // https://drafts.csswg.org/css-grid/#auto-repeat
180 struct RepeatTrackSizingInput
{
181 explicit RepeatTrackSizingInput(WritingMode aWM
)
183 mSize(aWM
, NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
),
184 mMax(aWM
, NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
) {}
185 RepeatTrackSizingInput(const LogicalSize
& aMin
, const LogicalSize
& aSize
,
186 const LogicalSize
& aMax
)
187 : mMin(aMin
), mSize(aSize
), mMax(aMax
) {}
189 // This should be used in intrinsic sizing (i.e. when we can't initialize
190 // the sizes directly from ReflowInput values).
191 void InitFromStyle(LogicalAxis aAxis
, WritingMode aWM
,
192 const ComputedStyle
* aStyle
) {
193 const auto& pos
= aStyle
->StylePosition();
194 const bool borderBoxSizing
= pos
->mBoxSizing
== StyleBoxSizing::Border
;
195 nscoord bp
= NS_UNCONSTRAINEDSIZE
; // a sentinel to calculate it only once
196 auto adjustForBoxSizing
= [borderBoxSizing
, aWM
, aAxis
, aStyle
,
197 &bp
](nscoord aSize
) {
198 if (!borderBoxSizing
) {
201 if (bp
== NS_UNCONSTRAINEDSIZE
) {
202 const auto& padding
= aStyle
->StylePadding()->mPadding
;
203 LogicalMargin
border(aWM
, aStyle
->StyleBorder()->GetComputedBorder());
204 // We can use zero percentage basis since this is only called from
205 // intrinsic sizing code.
206 const nscoord percentageBasis
= 0;
207 if (aAxis
== eLogicalAxisInline
) {
208 bp
= std::max(padding
.GetIStart(aWM
).Resolve(percentageBasis
), 0) +
209 std::max(padding
.GetIEnd(aWM
).Resolve(percentageBasis
), 0) +
210 border
.IStartEnd(aWM
);
212 bp
= std::max(padding
.GetBStart(aWM
).Resolve(percentageBasis
), 0) +
213 std::max(padding
.GetBEnd(aWM
).Resolve(percentageBasis
), 0) +
214 border
.BStartEnd(aWM
);
217 return std::max(aSize
- bp
, 0);
219 nscoord
& min
= mMin
.Size(aAxis
, aWM
);
220 nscoord
& size
= mSize
.Size(aAxis
, aWM
);
221 nscoord
& max
= mMax
.Size(aAxis
, aWM
);
222 const auto& minCoord
=
223 aAxis
== eLogicalAxisInline
? pos
->MinISize(aWM
) : pos
->MinBSize(aWM
);
224 if (minCoord
.ConvertsToLength()) {
225 min
= adjustForBoxSizing(minCoord
.ToLength());
227 const auto& maxCoord
=
228 aAxis
== eLogicalAxisInline
? pos
->MaxISize(aWM
) : pos
->MaxBSize(aWM
);
229 if (maxCoord
.ConvertsToLength()) {
230 max
= std::max(min
, adjustForBoxSizing(maxCoord
.ToLength()));
232 const auto& sizeCoord
=
233 aAxis
== eLogicalAxisInline
? pos
->ISize(aWM
) : pos
->BSize(aWM
);
234 if (sizeCoord
.ConvertsToLength()) {
235 size
= Clamp(adjustForBoxSizing(sizeCoord
.ToLength()), min
, max
);
244 enum class GridLineSide
{
249 struct nsGridContainerFrame::TrackSize
{
250 enum StateBits
: uint16_t {
252 eAutoMinSizing
= 0x1,
253 eMinContentMinSizing
= 0x2,
254 eMaxContentMinSizing
= 0x4,
255 eMinOrMaxContentMinSizing
= eMinContentMinSizing
| eMaxContentMinSizing
,
256 eIntrinsicMinSizing
= eMinOrMaxContentMinSizing
| eAutoMinSizing
,
258 eAutoMaxSizing
= 0x10,
259 eMinContentMaxSizing
= 0x20,
260 eMaxContentMaxSizing
= 0x40,
261 eAutoOrMaxContentMaxSizing
= eAutoMaxSizing
| eMaxContentMaxSizing
,
262 eIntrinsicMaxSizing
= eAutoOrMaxContentMaxSizing
| eMinContentMaxSizing
,
263 eFlexMaxSizing
= 0x80,
265 eSkipGrowUnlimited1
= 0x200,
266 eSkipGrowUnlimited2
= 0x400,
267 eSkipGrowUnlimited
= eSkipGrowUnlimited1
| eSkipGrowUnlimited2
,
268 eBreakBefore
= 0x800,
269 eFitContent
= 0x1000,
270 eInfinitelyGrowable
= 0x2000,
272 // These are only used in the masonry axis. They share the same value
273 // as *MinSizing above, but that's OK because we don't use those in
276 // This track corresponds to an item margin-box size that is stretching.
277 eItemStretchSize
= 0x1,
278 // This bit says that we should clamp that size to mLimit.
280 // This bit says that the corresponding item has `auto` margin(s).
281 eItemHasAutoMargin
= 0x4,
285 StateBits
Initialize(nscoord aPercentageBasis
, const StyleTrackSize
&);
286 bool IsFrozen() const { return mState
& eFrozen
; }
288 static void DumpStateBits(StateBits aState
);
292 static bool IsDefiniteMaxSizing(StateBits aStateBits
) {
293 return (aStateBits
& (eIntrinsicMaxSizing
| eFlexMaxSizing
)) == 0;
298 nscoord mPosition
; // zero until we apply 'align/justify-content'
299 // mBaselineSubtreeSize is the size of a baseline-aligned subtree within
300 // this track. One subtree per baseline-sharing group (per track).
301 PerBaseline
<nscoord
> mBaselineSubtreeSize
;
305 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits
)
308 std::is_trivially_copyable
<nsGridContainerFrame::TrackSize
>::value
,
309 "Must be trivially copyable");
311 std::is_trivially_destructible
<nsGridContainerFrame::TrackSize
>::value
,
312 "Must be trivially destructible");
314 TrackSize::StateBits
nsGridContainerFrame::TrackSize::Initialize(
315 nscoord aPercentageBasis
, const StyleTrackSize
& aSize
) {
316 using Tag
= StyleTrackBreadth::Tag
;
318 MOZ_ASSERT(mBase
== 0 && mLimit
== 0 && mState
== 0,
319 "track size data is expected to be initialized to zero");
320 mBaselineSubtreeSize
[BaselineSharingGroup::First
] = nscoord(0);
321 mBaselineSubtreeSize
[BaselineSharingGroup::Last
] = nscoord(0);
323 auto& min
= aSize
.GetMin();
324 auto& max
= aSize
.GetMax();
326 Tag minSizeTag
= min
.tag
;
327 Tag maxSizeTag
= max
.tag
;
328 if (aSize
.IsFitContent()) {
329 // In layout, fit-content(size) behaves as minmax(auto, max-content), with
330 // 'size' as an additional upper-bound.
331 mState
= eFitContent
;
332 minSizeTag
= Tag::Auto
;
333 maxSizeTag
= Tag::MaxContent
;
335 if (::IsPercentOfIndefiniteSize(min
, aPercentageBasis
)) {
336 // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-percentage
337 // "If the inline or block size of the grid container is indefinite,
338 // <percentage> values relative to that size are treated as 'auto'."
339 minSizeTag
= Tag::Auto
;
341 if (::IsPercentOfIndefiniteSize(max
, aPercentageBasis
)) {
342 maxSizeTag
= Tag::Auto
;
345 // http://dev.w3.org/csswg/css-grid/#algo-init
346 switch (minSizeTag
) {
348 mState
|= eAutoMinSizing
;
350 case Tag::MinContent
:
351 mState
|= eMinContentMinSizing
;
353 case Tag::MaxContent
:
354 mState
|= eMaxContentMinSizing
;
357 MOZ_ASSERT(!min
.IsFr(), "<flex> min-sizing is invalid as a track size");
358 mBase
= ::ResolveToDefiniteSize(min
, aPercentageBasis
);
360 switch (maxSizeTag
) {
362 mState
|= eAutoMaxSizing
;
363 mLimit
= NS_UNCONSTRAINEDSIZE
;
365 case Tag::MinContent
:
366 case Tag::MaxContent
:
367 mState
|= maxSizeTag
== Tag::MinContent
? eMinContentMaxSizing
368 : eMaxContentMaxSizing
;
369 mLimit
= NS_UNCONSTRAINEDSIZE
;
372 mState
|= eFlexMaxSizing
;
376 mLimit
= ::ResolveToDefiniteSize(max
, aPercentageBasis
);
377 if (mLimit
< mBase
) {
385 * A LineRange can be definite or auto - when it's definite it represents
386 * a consecutive set of tracks between a starting line and an ending line.
387 * Before it's definite it can also represent an auto position with a span,
388 * where mStart == kAutoLine and mEnd is the (non-zero positive) span.
389 * For normal-flow items, the invariant mStart < mEnd holds when both
390 * lines are definite.
392 * For abs.pos. grid items, mStart and mEnd may both be kAutoLine, meaning
393 * "attach this side to the grid container containing block edge".
394 * Additionally, mStart <= mEnd holds when both are definite (non-kAutoLine),
395 * i.e. the invariant is slightly relaxed compared to normal flow items.
397 struct nsGridContainerFrame::LineRange
{
398 LineRange(int32_t aStart
, int32_t aEnd
)
399 : mUntranslatedStart(aStart
), mUntranslatedEnd(aEnd
) {
403 MOZ_ASSERT(aEnd
>= kMinLine
&& aEnd
<= kMaxLine
, "invalid span");
405 MOZ_ASSERT(aStart
>= kMinLine
&& aStart
<= kMaxLine
,
406 "invalid start line");
407 MOZ_ASSERT(aEnd
== int32_t(kAutoLine
) ||
408 (aEnd
>= kMinLine
&& aEnd
<= kMaxLine
),
414 bool IsAutoAuto() const { return mStart
== kAutoLine
&& mEnd
== kAutoLine
; }
415 bool IsAuto() const { return mStart
== kAutoLine
; }
416 bool IsDefinite() const { return mStart
!= kAutoLine
; }
417 uint32_t Extent() const {
418 MOZ_ASSERT(mEnd
!= kAutoLine
, "Extent is undefined for abs.pos. 'auto'");
420 MOZ_ASSERT(mEnd
>= 1 && mEnd
< uint32_t(kMaxLine
), "invalid span");
423 return mEnd
- mStart
;
427 * Return an object suitable for iterating this range.
429 auto Range() const { return IntegerRange
<uint32_t>(mStart
, mEnd
); }
432 * Resolve this auto range to start at aStart, making it definite.
433 * @param aClampMaxLine the maximum allowed line number (zero-based)
434 * Precondition: this range IsAuto()
436 void ResolveAutoPosition(uint32_t aStart
, uint32_t aClampMaxLine
) {
437 MOZ_ASSERT(IsAuto(), "Why call me?");
440 // Clamp to aClampMaxLine, which is where kMaxLine is in the explicit
441 // grid in a non-subgrid axis; this implements clamping per
442 // http://dev.w3.org/csswg/css-grid/#overlarge-grids
443 // In a subgrid axis it's the end of the grid in that axis.
444 if (MOZ_UNLIKELY(mStart
>= aClampMaxLine
)) {
445 mEnd
= aClampMaxLine
;
447 } else if (MOZ_UNLIKELY(mEnd
> aClampMaxLine
)) {
448 mEnd
= aClampMaxLine
;
452 * Translate the lines to account for (empty) removed tracks. This method
453 * is only for grid items and should only be called after placement.
454 * aNumRemovedTracks contains a count for each line in the grid how many
455 * tracks were removed between the start of the grid and that line.
457 void AdjustForRemovedTracks(const nsTArray
<uint32_t>& aNumRemovedTracks
) {
458 MOZ_ASSERT(mStart
!= kAutoLine
, "invalid resolved line for a grid item");
459 MOZ_ASSERT(mEnd
!= kAutoLine
, "invalid resolved line for a grid item");
460 uint32_t numRemovedTracks
= aNumRemovedTracks
[mStart
];
461 MOZ_ASSERT(numRemovedTracks
== aNumRemovedTracks
[mEnd
],
462 "tracks that a grid item spans can't be removed");
463 mStart
-= numRemovedTracks
;
464 mEnd
-= numRemovedTracks
;
467 * Translate the lines to account for (empty) removed tracks. This method
468 * is only for abs.pos. children and should only be called after placement.
469 * Same as for in-flow items, but we don't touch 'auto' lines here and we
470 * also need to adjust areas that span into the removed tracks.
472 void AdjustAbsPosForRemovedTracks(
473 const nsTArray
<uint32_t>& aNumRemovedTracks
) {
474 if (mStart
!= kAutoLine
) {
475 mStart
-= aNumRemovedTracks
[mStart
];
477 if (mEnd
!= kAutoLine
) {
478 MOZ_ASSERT(mStart
== kAutoLine
|| mEnd
> mStart
, "invalid line range");
479 mEnd
-= aNumRemovedTracks
[mEnd
];
483 * Return the contribution of this line range for step 2 in
484 * http://dev.w3.org/csswg/css-grid/#auto-placement-algo
486 uint32_t HypotheticalEnd() const { return mEnd
; }
488 * Given an array of track sizes, return the starting position and length
489 * of the tracks in this line range.
491 void ToPositionAndLength(const nsTArray
<TrackSize
>& aTrackSizes
,
492 nscoord
* aPos
, nscoord
* aLength
) const;
494 * Given an array of track sizes, return the length of the tracks in this
497 nscoord
ToLength(const nsTArray
<TrackSize
>& aTrackSizes
) const;
499 * Given an array of track sizes and a grid origin coordinate, adjust the
500 * abs.pos. containing block along an axis given by aPos and aLength.
501 * aPos and aLength should already be initialized to the grid container
502 * containing block for this axis before calling this method.
504 void ToPositionAndLengthForAbsPos(const Tracks
& aTracks
, nscoord aGridOrigin
,
505 nscoord
* aPos
, nscoord
* aLength
) const;
507 void Translate(int32_t aOffset
) {
508 MOZ_ASSERT(IsDefinite());
513 /** Swap the start/end sides of this range. */
514 void ReverseDirection(uint32_t aGridEnd
) {
515 MOZ_ASSERT(IsDefinite());
516 MOZ_ASSERT(aGridEnd
>= mEnd
);
517 uint32_t newStart
= aGridEnd
- mEnd
;
518 mEnd
= aGridEnd
- mStart
;
523 * @note We'll use the signed member while resolving definite positions
524 * to line numbers (1-based), which may become negative for implicit lines
525 * to the top/left of the explicit grid. PlaceGridItems() then translates
526 * the whole grid to a 0,0 origin and we'll use the unsigned member from
531 int32_t mUntranslatedStart
;
535 int32_t mUntranslatedEnd
;
539 LineRange() : mStart(0), mEnd(0) {}
543 * Helper class to construct a LineRange from translated lines.
544 * The ctor only accepts translated definite line numbers.
546 struct nsGridContainerFrame::TranslatedLineRange
: public LineRange
{
547 TranslatedLineRange(uint32_t aStart
, uint32_t aEnd
) {
548 MOZ_ASSERT(aStart
< aEnd
&& aEnd
<= kTranslatedMaxLine
);
555 * A GridArea is the area in the grid for a grid item.
556 * The area is represented by two LineRanges, both of which can be auto
557 * (@see LineRange) in intermediate steps while the item is being placed.
558 * @see PlaceGridItems
560 struct nsGridContainerFrame::GridArea
{
561 GridArea(const LineRange
& aCols
, const LineRange
& aRows
)
562 : mCols(aCols
), mRows(aRows
) {}
563 bool IsDefinite() const { return mCols
.IsDefinite() && mRows
.IsDefinite(); }
564 LineRange
& LineRangeForAxis(LogicalAxis aAxis
) {
565 return aAxis
== eLogicalAxisInline
? mCols
: mRows
;
567 const LineRange
& LineRangeForAxis(LogicalAxis aAxis
) const {
568 return aAxis
== eLogicalAxisInline
? mCols
: mRows
;
574 struct nsGridContainerFrame::GridItemInfo
{
576 * Item state per axis.
578 enum StateBits
: uint16_t {
580 eIsFlexing
= 0x1, // does the item span a flex track?
581 eFirstBaseline
= 0x2, // participate in 'first baseline' alignment?
582 // ditto 'last baseline', mutually exclusive w. eFirstBaseline
584 eIsBaselineAligned
= eFirstBaseline
| eLastBaseline
,
585 // One of e[Self|Content]Baseline is set when eIsBaselineAligned is true
586 eSelfBaseline
= 0x8, // is it *-self:[last ]baseline alignment?
587 // Ditto *-content:[last ]baseline. Mutually exclusive w. eSelfBaseline.
588 eContentBaseline
= 0x10,
589 // The baseline affects the margin or padding on the item's end side when
590 // this bit is set. In a grid-axis it's always set for eLastBaseline and
591 // always unset for eFirstBaseline. In a masonry-axis, it's set for
592 // baseline groups in the EndStretch set and unset for the StartStretch set.
593 eEndSideBaseline
= 0x20,
594 eAllBaselineBits
= eIsBaselineAligned
| eSelfBaseline
| eContentBaseline
|
596 // Should apply Automatic Minimum Size per:
597 // https://drafts.csswg.org/css-grid/#min-size-auto
598 eApplyAutoMinSize
= 0x40,
599 // Clamp per https://drafts.csswg.org/css-grid/#min-size-auto
600 eClampMarginBoxMinSize
= 0x80,
602 // set on subgrids and items in subgrids if they are adjacent to the grid
603 // start/end edge (excluding grid-aligned abs.pos. frames)
606 eEdgeBits
= eStartEdge
| eEndEdge
,
607 // Set if this item was auto-placed in this axis.
608 eAutoPlacement
= 0x800,
609 // Set if this item is the last item in its track (masonry layout only)
610 eIsLastItemInMasonryTrack
= 0x1000,
614 GridItemInfo(nsIFrame
* aFrame
, const GridArea
& aArea
);
616 static bool BaselineAlignmentAffectsEndSide(StateBits state
) {
617 return state
& StateBits::eEndSideBaseline
;
621 * Inhibit subgrid layout unless the item is placed in the first "track" in
622 * a parent masonry-axis, or has definite placement or spans all tracks in
623 * the parent grid-axis.
624 * TODO: this is stricter than what the Masonry proposal currently states
627 void MaybeInhibitSubgridInMasonry(nsGridContainerFrame
* aParent
,
628 uint32_t aGridAxisTrackCount
);
631 * Inhibit subgridding in aAxis for this item.
633 void InhibitSubgrid(nsGridContainerFrame
* aParent
, LogicalAxis aAxis
);
636 * Return a copy of this item with its row/column data swapped.
638 GridItemInfo
Transpose() const {
639 GridItemInfo
info(mFrame
, GridArea(mArea
.mRows
, mArea
.mCols
));
640 info
.mState
[0] = mState
[1];
641 info
.mState
[1] = mState
[0];
642 info
.mBaselineOffset
[0] = mBaselineOffset
[1];
643 info
.mBaselineOffset
[1] = mBaselineOffset
[0];
647 /** Swap the start/end sides in aAxis. */
648 inline void ReverseDirection(LogicalAxis aAxis
, uint32_t aGridEnd
);
650 // Is this item a subgrid in the given container axis?
651 bool IsSubgrid(LogicalAxis aAxis
) const {
652 return mState
[aAxis
] & StateBits::eIsSubgrid
;
655 // Is this item a subgrid in either axis?
656 bool IsSubgrid() const {
657 return IsSubgrid(eLogicalAxisInline
) || IsSubgrid(eLogicalAxisBlock
);
660 // Return the (inner) grid container frame associated with this subgrid item.
661 nsGridContainerFrame
* SubgridFrame() const {
662 MOZ_ASSERT(IsSubgrid());
663 nsGridContainerFrame
* gridFrame
= GetGridContainerFrame(mFrame
);
664 MOZ_ASSERT(gridFrame
&& gridFrame
->IsSubgrid());
669 * Adjust our grid areas to account for removed auto-fit tracks in aAxis.
671 void AdjustForRemovedTracks(LogicalAxis aAxis
,
672 const nsTArray
<uint32_t>& aNumRemovedTracks
);
675 * If the item is [align|justify]-self:[last ]baseline aligned in the given
676 * axis then set aBaselineOffset to the baseline offset and return aAlign.
677 * Otherwise, return a fallback alignment.
679 StyleAlignFlags
GetSelfBaseline(StyleAlignFlags aAlign
, LogicalAxis aAxis
,
680 nscoord
* aBaselineOffset
) const {
681 MOZ_ASSERT(aAlign
== StyleAlignFlags::BASELINE
||
682 aAlign
== StyleAlignFlags::LAST_BASELINE
);
683 if (!(mState
[aAxis
] & eSelfBaseline
)) {
684 return aAlign
== StyleAlignFlags::BASELINE
? StyleAlignFlags::SELF_START
685 : StyleAlignFlags::SELF_END
;
687 *aBaselineOffset
= mBaselineOffset
[aAxis
];
691 // Return true if we should apply Automatic Minimum Size to this item.
692 // https://drafts.csswg.org/css-grid/#min-size-auto
693 // @note the caller should also check that the item spans at least one track
694 // that has a min track sizing function that is 'auto' before applying it.
695 bool ShouldApplyAutoMinSize(WritingMode aContainerWM
,
696 LogicalAxis aContainerAxis
,
697 nscoord aPercentageBasis
) const {
698 const bool isInlineAxis
= aContainerAxis
== eLogicalAxisInline
;
700 mFrame
->IsTableWrapperFrame()
701 ? mFrame
->PrincipalChildList().FirstChild()->StylePosition()
702 : mFrame
->StylePosition();
704 isInlineAxis
? pos
->ISize(aContainerWM
) : pos
->BSize(aContainerWM
);
705 // max-content and min-content should behave as initial value in block axis.
706 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
707 // for block size dimension on sizing properties (e.g. height), so we
708 // treat it as `auto`.
709 bool isAuto
= size
.IsAuto() ||
711 aContainerWM
.IsOrthogonalTo(mFrame
->GetWritingMode()) &&
712 size
.BehavesLikeInitialValueOnBlockAxis());
713 // NOTE: if we have a definite size then our automatic minimum size
714 // can't affect our size. Excluding these simplifies applying
715 // the clamping in the right cases later.
716 if (!isAuto
&& !::IsPercentOfIndefiniteSize(size
, aPercentageBasis
)) {
719 const auto& minSize
= isInlineAxis
? pos
->MinISize(aContainerWM
)
720 : pos
->MinBSize(aContainerWM
);
721 // max-content and min-content should behave as initial value in block axis.
722 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
723 // for block size dimension on sizing properties (e.g. height), so we
724 // treat it as `auto`.
725 isAuto
= minSize
.IsAuto() ||
727 aContainerWM
.IsOrthogonalTo(mFrame
->GetWritingMode()) &&
728 minSize
.BehavesLikeInitialValueOnBlockAxis());
730 mFrame
->StyleDisplay()->mOverflowX
== StyleOverflow::Visible
;
737 static bool IsStartRowLessThan(const GridItemInfo
* a
, const GridItemInfo
* b
) {
738 return a
->mArea
.mRows
.mStart
< b
->mArea
.mRows
.mStart
;
741 // Sorting functions for 'masonry-auto-flow:next'. We sort the items that
742 // were placed into the first track by the Grid placement algorithm first
743 // (to honor that placement). All other items will be placed by the Masonry
744 // layout algorithm (their Grid placement in the masonry axis is irrelevant).
745 static bool RowMasonryOrdered(const GridItemInfo
* a
, const GridItemInfo
* b
) {
746 return a
->mArea
.mRows
.mStart
== 0 && b
->mArea
.mRows
.mStart
!= 0 &&
747 !a
->mFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
);
749 static bool ColMasonryOrdered(const GridItemInfo
* a
, const GridItemInfo
* b
) {
750 return a
->mArea
.mCols
.mStart
== 0 && b
->mArea
.mCols
.mStart
!= 0 &&
751 !a
->mFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
);
754 // Sorting functions for 'masonry-auto-flow:definite-first'. Similar to
755 // the above, but here we also sort items with a definite item placement in
756 // the grid axis in track order before 'auto'-placed items. We also sort all
757 // continuations first since they use the same placement as their
758 // first-in-flow (we treat them as "definite" regardless of eAutoPlacement).
759 static bool RowMasonryDefiniteFirst(const GridItemInfo
* a
,
760 const GridItemInfo
* b
) {
761 bool isContinuationA
= a
->mFrame
->GetPrevInFlow();
762 bool isContinuationB
= b
->mFrame
->GetPrevInFlow();
763 if (isContinuationA
!= isContinuationB
) {
764 return isContinuationA
;
766 auto masonryA
= a
->mArea
.mRows
.mStart
;
767 auto gridA
= a
->mState
[eLogicalAxisInline
] & StateBits::eAutoPlacement
;
768 auto masonryB
= b
->mArea
.mRows
.mStart
;
769 auto gridB
= b
->mState
[eLogicalAxisInline
] & StateBits::eAutoPlacement
;
770 return (masonryA
== 0 ? masonryB
!= 0 : (masonryB
!= 0 && gridA
< gridB
)) &&
771 !a
->mFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
);
773 static bool ColMasonryDefiniteFirst(const GridItemInfo
* a
,
774 const GridItemInfo
* b
) {
775 MOZ_ASSERT(!a
->mFrame
->GetPrevInFlow() && !b
->mFrame
->GetPrevInFlow(),
776 "fragmentation not supported in inline axis");
777 auto masonryA
= a
->mArea
.mCols
.mStart
;
778 auto gridA
= a
->mState
[eLogicalAxisBlock
] & StateBits::eAutoPlacement
;
779 auto masonryB
= b
->mArea
.mCols
.mStart
;
780 auto gridB
= b
->mState
[eLogicalAxisBlock
] & StateBits::eAutoPlacement
;
781 return (masonryA
== 0 ? masonryB
!= 0 : (masonryB
!= 0 && gridA
< gridB
)) &&
782 !a
->mFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
);
785 nsIFrame
* const mFrame
;
787 // Offset from the margin edge to the baseline (LogicalAxis index). It's from
788 // the start edge when eFirstBaseline is set, end edge otherwise. It's mutable
789 // since we update the value fairly late (just before reflowing the item).
790 mutable nscoord mBaselineOffset
[2];
791 mutable StateBits mState
[2]; // state bits per axis (LogicalAxis index)
792 static_assert(mozilla::eLogicalAxisBlock
== 0, "unexpected index value");
793 static_assert(mozilla::eLogicalAxisInline
== 1, "unexpected index value");
796 using GridItemInfo
= nsGridContainerFrame::GridItemInfo
;
797 using ItemState
= GridItemInfo::StateBits
;
798 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ItemState
)
800 GridItemInfo::GridItemInfo(nsIFrame
* aFrame
, const GridArea
& aArea
)
801 : mFrame(aFrame
), mArea(aArea
) {
802 mState
[eLogicalAxisBlock
] =
803 StateBits(mArea
.mRows
.mStart
== kAutoLine
? eAutoPlacement
: 0);
804 mState
[eLogicalAxisInline
] =
805 StateBits(mArea
.mCols
.mStart
== kAutoLine
? eAutoPlacement
: 0);
806 if (auto* gridFrame
= GetGridContainerFrame(mFrame
)) {
807 auto parentWM
= aFrame
->GetParent()->GetWritingMode();
808 bool isOrthogonal
= parentWM
.IsOrthogonalTo(gridFrame
->GetWritingMode());
809 if (gridFrame
->IsColSubgrid()) {
810 mState
[isOrthogonal
? eLogicalAxisBlock
: eLogicalAxisInline
] |=
811 StateBits::eIsSubgrid
;
813 if (gridFrame
->IsRowSubgrid()) {
814 mState
[isOrthogonal
? eLogicalAxisInline
: eLogicalAxisBlock
] |=
815 StateBits::eIsSubgrid
;
818 mBaselineOffset
[eLogicalAxisBlock
] = nscoord(0);
819 mBaselineOffset
[eLogicalAxisInline
] = nscoord(0);
822 void GridItemInfo::ReverseDirection(LogicalAxis aAxis
, uint32_t aGridEnd
) {
823 mArea
.LineRangeForAxis(aAxis
).ReverseDirection(aGridEnd
);
824 ItemState
& state
= mState
[aAxis
];
825 ItemState newState
= state
& ~ItemState::eEdgeBits
;
826 if (state
& ItemState::eStartEdge
) {
827 newState
|= ItemState::eEndEdge
;
829 if (state
& ItemState::eEndEdge
) {
830 newState
|= ItemState::eStartEdge
;
835 void GridItemInfo::InhibitSubgrid(nsGridContainerFrame
* aParent
,
837 MOZ_ASSERT(IsSubgrid(aAxis
));
838 auto bit
= NS_STATE_GRID_IS_COL_SUBGRID
;
839 if (aParent
->GetWritingMode().IsOrthogonalTo(mFrame
->GetWritingMode()) !=
840 (aAxis
== eLogicalAxisBlock
)) {
841 bit
= NS_STATE_GRID_IS_ROW_SUBGRID
;
843 MOZ_ASSERT(SubgridFrame()->HasAnyStateBits(bit
));
844 SubgridFrame()->RemoveStateBits(bit
);
845 mState
[aAxis
] &= StateBits(~StateBits::eIsSubgrid
);
848 void GridItemInfo::MaybeInhibitSubgridInMasonry(nsGridContainerFrame
* aParent
,
849 uint32_t aGridAxisTrackCount
) {
850 if (IsSubgrid(eLogicalAxisInline
) && aParent
->IsMasonry(eLogicalAxisBlock
) &&
851 mArea
.mRows
.mStart
!= 0 && mArea
.mCols
.Extent() != aGridAxisTrackCount
&&
852 (mState
[eLogicalAxisInline
] & eAutoPlacement
)) {
853 InhibitSubgrid(aParent
, eLogicalAxisInline
);
856 if (IsSubgrid(eLogicalAxisBlock
) && aParent
->IsMasonry(eLogicalAxisInline
) &&
857 mArea
.mCols
.mStart
!= 0 && mArea
.mRows
.Extent() != aGridAxisTrackCount
&&
858 (mState
[eLogicalAxisBlock
] & eAutoPlacement
)) {
859 InhibitSubgrid(aParent
, eLogicalAxisBlock
);
863 // Each subgrid stores this data about its items etc on a frame property.
864 struct nsGridContainerFrame::Subgrid
{
865 Subgrid(const GridArea
& aArea
, bool aIsOrthogonal
, WritingMode aCBWM
)
869 mMarginBorderPadding(aCBWM
),
870 mIsOrthogonal(aIsOrthogonal
) {}
872 // Return the relevant line range for the subgrid column axis.
873 const LineRange
& SubgridCols() const {
874 return mIsOrthogonal
? mArea
.mRows
: mArea
.mCols
;
876 // Return the relevant line range for the subgrid row axis.
877 const LineRange
& SubgridRows() const {
878 return mIsOrthogonal
? mArea
.mCols
: mArea
.mRows
;
881 // The subgrid's items.
882 nsTArray
<GridItemInfo
> mGridItems
;
883 // The subgrid's abs.pos. items.
884 nsTArray
<GridItemInfo
> mAbsPosItems
;
885 // The subgrid's area as a grid item, i.e. in its parent's grid space.
887 // The (inner) grid size for the subgrid, zero-based.
888 uint32_t mGridColEnd
;
889 uint32_t mGridRowEnd
;
890 // The margin+border+padding for the subgrid box in its parent grid's WM.
891 // (This also includes the size of any scrollbars.)
892 LogicalMargin mMarginBorderPadding
;
893 // Does the subgrid frame have orthogonal writing-mode to its parent grid
897 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop
, Subgrid
)
899 using Subgrid
= nsGridContainerFrame::Subgrid
;
901 void GridItemInfo::AdjustForRemovedTracks(
902 LogicalAxis aAxis
, const nsTArray
<uint32_t>& aNumRemovedTracks
) {
903 const bool abspos
= mFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
);
904 auto& lines
= mArea
.LineRangeForAxis(aAxis
);
906 lines
.AdjustAbsPosForRemovedTracks(aNumRemovedTracks
);
908 lines
.AdjustForRemovedTracks(aNumRemovedTracks
);
911 auto* subgrid
= SubgridFrame()->GetProperty(Subgrid::Prop());
913 auto& lines
= subgrid
->mArea
.LineRangeForAxis(aAxis
);
915 lines
.AdjustAbsPosForRemovedTracks(aNumRemovedTracks
);
917 lines
.AdjustForRemovedTracks(aNumRemovedTracks
);
924 * Track size data for use by subgrids (which don't do sizing of their own
925 * in a subgridded axis). A non-subgrid container stores its resolved sizes,
926 * but only if it has any subgrid children. A subgrid always stores one.
927 * In a subgridded axis, we copy the parent's sizes (see CopyUsedTrackSizes).
929 * This struct us stored on a frame property, which may be null before the track
930 * sizing step for the given container. A null property is semantically
931 * equivalent to mCanResolveLineRangeSize being false in both axes.
932 * @note the axis used to access this data is in the grid container's own
933 * writing-mode, same as in other track-sizing functions.
935 struct nsGridContainerFrame::UsedTrackSizes
{
936 UsedTrackSizes() : mCanResolveLineRangeSize
{false, false} {}
939 * Setup mSizes by copying track sizes from aFrame's grid container
940 * parent when aAxis is subgridded (and recurse if the parent is a subgrid
941 * that doesn't have sizes yet), or by running the Track Sizing Algo when
942 * the axis is not subgridded (for a subgrid).
943 * Set mCanResolveLineRangeSize[aAxis] to true once we have obtained
944 * sizes for an axis (if it's already true then this method is a NOP).
946 void ResolveTrackSizesForAxis(nsGridContainerFrame
* aFrame
, LogicalAxis aAxis
,
949 /** Helper function for the above method */
950 void ResolveSubgridTrackSizesForAxis(nsGridContainerFrame
* aFrame
,
951 LogicalAxis aAxis
, Subgrid
* aSubgrid
,
953 nscoord aContentBoxSize
);
955 // This only has valid sizes when mCanResolveLineRangeSize is true in
956 // the same axis. It may have zero tracks (a grid with only abs.pos.
957 // subgrids/items may have zero tracks).
958 PerLogicalAxis
<nsTArray
<TrackSize
>> mSizes
;
959 // True if mSizes can be used to resolve line range sizes in an axis.
960 PerLogicalAxis
<bool> mCanResolveLineRangeSize
;
962 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop
, UsedTrackSizes
)
964 using UsedTrackSizes
= nsGridContainerFrame::UsedTrackSizes
;
967 void nsGridContainerFrame::GridItemInfo::Dump() const {
968 auto Dump1
= [this](const char* aMsg
, LogicalAxis aAxis
) {
969 auto state
= mState
[aAxis
];
974 if (state
& ItemState::eEdgeBits
) {
975 printf("subgrid-adjacent-edges(");
976 if (state
& ItemState::eStartEdge
) {
979 if (state
& ItemState::eEndEdge
) {
984 if (state
& ItemState::eAutoPlacement
) {
985 printf("masonry-auto ");
987 if (state
& ItemState::eIsSubgrid
) {
990 if (state
& ItemState::eIsFlexing
) {
993 if (state
& ItemState::eApplyAutoMinSize
) {
994 printf("auto-min-size ");
996 if (state
& ItemState::eClampMarginBoxMinSize
) {
999 if (state
& ItemState::eIsLastItemInMasonryTrack
) {
1000 printf("last-in-track ");
1002 if (state
& ItemState::eFirstBaseline
) {
1003 printf("first baseline %s-alignment ",
1004 (state
& ItemState::eSelfBaseline
) ? "self" : "content");
1006 if (state
& ItemState::eLastBaseline
) {
1007 printf("last baseline %s-alignment ",
1008 (state
& ItemState::eSelfBaseline
) ? "self" : "content");
1010 if (state
& ItemState::eIsBaselineAligned
) {
1011 printf("%.2fpx", NSAppUnitsToFloatPixels(mBaselineOffset
[aAxis
],
1012 AppUnitsPerCSSPixel()));
1016 printf("grid-row: %d %d\n", mArea
.mRows
.mStart
, mArea
.mRows
.mEnd
);
1017 Dump1(" grid block-axis: ", eLogicalAxisBlock
);
1018 printf("grid-column: %d %d\n", mArea
.mCols
.mStart
, mArea
.mCols
.mEnd
);
1019 Dump1(" grid inline-axis: ", eLogicalAxisInline
);
1024 * Encapsulates CSS track-sizing functions.
1026 struct nsGridContainerFrame::TrackSizingFunctions
{
1028 TrackSizingFunctions(const GridTemplate
& aTemplate
,
1029 const StyleImplicitGridTracks
& aAutoSizing
,
1030 const Maybe
<size_t>& aRepeatAutoIndex
, bool aIsSubgrid
)
1031 : mTemplate(aTemplate
),
1032 mTrackListValues(aTemplate
.TrackListValues()),
1033 mAutoSizing(aAutoSizing
),
1034 mExplicitGridOffset(0),
1035 mRepeatAutoStart(aRepeatAutoIndex
.valueOr(0)),
1036 mRepeatAutoEnd(mRepeatAutoStart
),
1037 mHasRepeatAuto(aRepeatAutoIndex
.isSome()) {
1038 MOZ_ASSERT(!mHasRepeatAuto
|| !aIsSubgrid
,
1039 "a track-list for a subgrid can't have an <auto-repeat> track");
1041 ExpandNonRepeatAutoTracks();
1045 if (mHasRepeatAuto
) {
1046 MOZ_ASSERT(mExpandedTracks
.Length() >= 1);
1047 const unsigned maxTrack
= kMaxLine
- 1;
1048 // If the exanded tracks are out of range of the maximum track, we
1049 // can't compare the repeat-auto start. It will be removed later during
1050 // grid item placement in that situation.
1051 if (mExpandedTracks
.Length() < maxTrack
) {
1052 MOZ_ASSERT(mRepeatAutoStart
< mExpandedTracks
.Length());
1059 TrackSizingFunctions(const GridTemplate
& aGridTemplate
,
1060 const StyleImplicitGridTracks
& aAutoSizing
,
1062 : TrackSizingFunctions(aGridTemplate
, aAutoSizing
,
1063 aGridTemplate
.RepeatAutoIndex(), aIsSubgrid
) {}
1066 enum { ForSubgridFallbackTag
};
1067 TrackSizingFunctions(const GridTemplate
& aGridTemplate
,
1068 const StyleImplicitGridTracks
& aAutoSizing
,
1069 decltype(ForSubgridFallbackTag
))
1070 : TrackSizingFunctions(aGridTemplate
, aAutoSizing
, Nothing(),
1071 /* aIsSubgrid */ true) {}
1075 * This is used in a subgridded axis to resolve sizes before its parent's
1076 * sizes are known for intrinsic sizing purposes. It copies the slice of
1077 * the nearest non-subgridded axis' track sizing functions spanned by
1080 * FIXME: this was written before there was a spec... the spec now says:
1081 * "If calculating the layout of a grid item in this step depends on
1082 * the available space in the block axis, assume the available space
1083 * that it would have if any row with a definite max track sizing
1084 * function had that size and all other rows were infinite."
1085 * https://drafts.csswg.org/css-grid-2/#subgrid-sizing
1087 static TrackSizingFunctions
ForSubgridFallback(
1088 nsGridContainerFrame
* aSubgridFrame
, const Subgrid
* aSubgrid
,
1089 nsGridContainerFrame
* aParentGridContainer
, LogicalAxis aParentAxis
) {
1090 MOZ_ASSERT(aSubgrid
);
1091 MOZ_ASSERT(aSubgridFrame
->IsSubgrid(aSubgrid
->mIsOrthogonal
1092 ? GetOrthogonalAxis(aParentAxis
)
1094 nsGridContainerFrame
* parent
= aParentGridContainer
;
1095 auto parentAxis
= aParentAxis
;
1096 LineRange range
= aSubgrid
->mArea
.LineRangeForAxis(parentAxis
);
1097 // Find our nearest non-subgridded axis and use its track sizing functions.
1098 while (parent
->IsSubgrid(parentAxis
)) {
1099 const auto* parentSubgrid
= parent
->GetProperty(Subgrid::Prop());
1100 auto* grandParent
= parent
->ParentGridContainerForSubgrid();
1101 auto grandParentWM
= grandParent
->GetWritingMode();
1102 bool isSameDirInAxis
=
1103 parent
->GetWritingMode().ParallelAxisStartsOnSameSide(parentAxis
,
1105 if (MOZ_UNLIKELY(!isSameDirInAxis
)) {
1106 auto end
= parentAxis
== eLogicalAxisBlock
? parentSubgrid
->mGridRowEnd
1107 : parentSubgrid
->mGridColEnd
;
1108 range
.ReverseDirection(end
);
1109 // range is now in the same direction as the grand-parent's axis
1111 auto grandParentAxis
= parentSubgrid
->mIsOrthogonal
1112 ? GetOrthogonalAxis(parentAxis
)
1114 const auto& parentRange
=
1115 parentSubgrid
->mArea
.LineRangeForAxis(grandParentAxis
);
1116 range
.Translate(parentRange
.mStart
);
1117 // range is now in the grand-parent's coordinates
1118 parentAxis
= grandParentAxis
;
1119 parent
= grandParent
;
1121 const auto* pos
= parent
->StylePosition();
1122 const auto isInlineAxis
= parentAxis
== eLogicalAxisInline
;
1124 isInlineAxis
? pos
->mGridTemplateRows
: pos
->mGridTemplateColumns
;
1125 const auto& autoSizing
=
1126 isInlineAxis
? pos
->mGridAutoColumns
: pos
->mGridAutoRows
;
1127 return TrackSizingFunctions(szf
, autoSizing
, ForSubgridFallbackTag
);
1131 * Initialize the number of auto-fill/fit tracks to use.
1132 * This can be zero if no auto-fill/fit track was specified, or if the repeat
1133 * begins after the maximum allowed track.
1135 void InitRepeatTracks(const NonNegativeLengthPercentageOrNormal
& aGridGap
,
1136 nscoord aMinSize
, nscoord aSize
, nscoord aMaxSize
) {
1137 const uint32_t maxTrack
= kMaxLine
- 1;
1138 // Check for a repeat after the maximum allowed track.
1139 if (MOZ_UNLIKELY(mRepeatAutoStart
>= maxTrack
)) {
1140 mHasRepeatAuto
= false;
1141 mRepeatAutoStart
= 0;
1145 uint32_t repeatTracks
=
1146 CalculateRepeatFillCount(aGridGap
, aMinSize
, aSize
, aMaxSize
) *
1148 // Clamp the number of repeat tracks to the maximum possible track.
1149 repeatTracks
= std::min(repeatTracks
, maxTrack
- mRepeatAutoStart
);
1150 SetNumRepeatTracks(repeatTracks
);
1151 // Blank out the removed flags for each of these tracks.
1152 mRemovedRepeatTracks
.SetLength(repeatTracks
);
1153 for (auto& track
: mRemovedRepeatTracks
) {
1158 uint32_t CalculateRepeatFillCount(
1159 const NonNegativeLengthPercentageOrNormal
& aGridGap
, nscoord aMinSize
,
1160 nscoord aSize
, nscoord aMaxSize
) const {
1161 if (!mHasRepeatAuto
) {
1164 // At this point no tracks will have been collapsed, so the RepeatEndDelta
1165 // should not be negative.
1166 MOZ_ASSERT(RepeatEndDelta() >= 0);
1167 // Note that this uses NumRepeatTracks and mRepeatAutoStart/End, although
1168 // the result of this method is used to change those values to a fully
1169 // expanded value. Spec quotes are from
1170 // https://drafts.csswg.org/css-grid/#repeat-notation
1171 const uint32_t numTracks
= mExpandedTracks
.Length() + RepeatEndDelta();
1172 MOZ_ASSERT(numTracks
>= 1, "expected at least the repeat() track");
1173 if (MOZ_UNLIKELY(numTracks
>= kMaxLine
)) {
1174 // The fixed tracks plus an entire repetition is either larger or as
1175 // large as the maximum track, so we do not need to measure how many
1176 // repetitions will fit. This also avoids needing to check for if
1177 // kMaxLine - numTracks would underflow at the end where we clamp the
1181 nscoord maxFill
= aSize
!= NS_UNCONSTRAINEDSIZE
? aSize
: aMaxSize
;
1182 if (maxFill
== NS_UNCONSTRAINEDSIZE
&& aMinSize
== 0) {
1183 // "Otherwise, the specified track list repeats only once."
1186 nscoord repeatTrackSum
= 0;
1187 // Note that one repeat() track size is included in |sum| in this loop.
1189 const nscoord percentBasis
= aSize
;
1190 for (uint32_t i
= 0; i
< numTracks
; ++i
) {
1191 // "treating each track as its max track sizing function if that is
1192 // definite or as its minimum track sizing function otherwise"
1193 // https://drafts.csswg.org/css-grid/#valdef-repeat-auto-fill
1194 const auto& sizingFunction
= SizingFor(i
);
1195 const auto& maxCoord
= sizingFunction
.GetMax();
1196 const auto* coord
= &maxCoord
;
1197 if (!coord
->IsBreadth()) {
1198 coord
= &sizingFunction
.GetMin();
1199 if (!coord
->IsBreadth()) {
1203 nscoord trackSize
= ::ResolveToDefiniteSize(*coord
, percentBasis
);
1204 if (i
>= mRepeatAutoStart
&& i
< mRepeatAutoEnd
) {
1205 // Use a minimum 1px for the repeat() track-size.
1206 if (trackSize
< AppUnitsPerCSSPixel()) {
1207 trackSize
= AppUnitsPerCSSPixel();
1209 repeatTrackSum
+= trackSize
;
1213 nscoord gridGap
= nsLayoutUtils::ResolveGapToLength(aGridGap
, aSize
);
1214 if (numTracks
> 1) {
1215 // Add grid-gaps for all the tracks including the repeat() track.
1216 sum
+= gridGap
* (numTracks
- 1);
1218 // Calculate the max number of tracks that fits without overflow.
1219 nscoord available
= maxFill
!= NS_UNCONSTRAINEDSIZE
? maxFill
: aMinSize
;
1220 nscoord spaceToFill
= available
- sum
;
1221 if (spaceToFill
<= 0) {
1222 // "if any number of repetitions would overflow, then 1 repetition"
1225 // Calculate the max number of tracks that fits without overflow.
1226 // Since we already have one repetition in sum, we can simply add one grid
1227 // gap for each element in the repeat.
1228 div_t q
= div(spaceToFill
, repeatTrackSum
+ gridGap
* NumRepeatTracks());
1229 // The +1 here is for the one repeat track we already accounted for above.
1230 uint32_t numRepeatTracks
= q
.quot
+ 1;
1231 if (q
.rem
!= 0 && maxFill
== NS_UNCONSTRAINEDSIZE
) {
1232 // "Otherwise, if the grid container has a definite min size in
1233 // the relevant axis, the number of repetitions is the largest possible
1234 // positive integer that fulfills that minimum requirement."
1235 ++numRepeatTracks
; // one more to ensure the grid is at least min-size
1237 // Clamp the number of repeat tracks so that the last line <= kMaxLine.
1238 // (note that |numTracks| already includes one repeat() track)
1239 MOZ_ASSERT(numTracks
>= NumRepeatTracks());
1240 const uint32_t maxRepeatTrackCount
= kMaxLine
- numTracks
;
1241 const uint32_t maxRepetitions
= maxRepeatTrackCount
/ NumRepeatTracks();
1242 return std::min(numRepeatTracks
, maxRepetitions
);
1246 * Compute the explicit grid end line number (in a zero-based grid).
1247 * @param aGridTemplateAreasEnd 'grid-template-areas' end line in this axis
1249 uint32_t ComputeExplicitGridEnd(uint32_t aGridTemplateAreasEnd
) {
1250 uint32_t end
= NumExplicitTracks() + 1;
1251 end
= std::max(end
, aGridTemplateAreasEnd
);
1252 end
= std::min(end
, uint32_t(kMaxLine
));
1255 const StyleTrackSize
& SizingFor(uint32_t aTrackIndex
) const {
1256 static const StyleTrackSize kAutoTrackSize
=
1257 StyleTrackSize::Breadth(StyleTrackBreadth::Auto());
1258 // |aIndex| is the relative index to mAutoSizing. A negative value means it
1259 // is the last Nth element.
1260 auto getImplicitSize
= [this](int32_t aIndex
) -> const StyleTrackSize
& {
1261 MOZ_ASSERT(!(mAutoSizing
.Length() == 1 &&
1262 mAutoSizing
.AsSpan()[0] == kAutoTrackSize
),
1263 "It's impossible to have one track with auto value because we "
1264 "filter out this case during parsing");
1266 if (mAutoSizing
.IsEmpty()) {
1267 return kAutoTrackSize
;
1270 // If multiple track sizes are given, the pattern is repeated as necessary
1271 // to find the size of the implicit tracks.
1272 int32_t i
= aIndex
% int32_t(mAutoSizing
.Length());
1274 i
+= mAutoSizing
.Length();
1276 return mAutoSizing
.AsSpan()[i
];
1279 if (MOZ_UNLIKELY(aTrackIndex
< mExplicitGridOffset
)) {
1280 // The last implicit grid track before the explicit grid receives the
1281 // last specified size, and so on backwards. Therefore we pass the
1282 // negative relative index to imply that we should get the implicit size
1283 // from the last Nth specified grid auto size.
1284 return getImplicitSize(int32_t(aTrackIndex
) -
1285 int32_t(mExplicitGridOffset
));
1287 uint32_t index
= aTrackIndex
- mExplicitGridOffset
;
1288 MOZ_ASSERT(mRepeatAutoStart
<= mRepeatAutoEnd
);
1290 if (index
>= mRepeatAutoStart
) {
1291 if (index
< mRepeatAutoEnd
) {
1292 // Expand the repeat tracks.
1293 const auto& indices
= mExpandedTracks
[mRepeatAutoStart
];
1294 const TrackListValue
& value
= mTrackListValues
[indices
.first
];
1296 // We expect the default to be used for all track repeats.
1297 MOZ_ASSERT(indices
.second
== 0);
1299 const auto& repeatTracks
= value
.AsTrackRepeat().track_sizes
.AsSpan();
1301 // Find the repeat track to use, skipping over any collapsed tracks.
1302 const uint32_t finalRepeatIndex
= (index
- mRepeatAutoStart
);
1303 uint32_t repeatWithCollapsed
= 0;
1304 // NOTE: We need SizingFor before the final collapsed tracks are known.
1305 // We know that it's invalid to have empty mRemovedRepeatTracks when
1306 // there are any repeat tracks, so we can detect that situation here.
1307 if (mRemovedRepeatTracks
.IsEmpty()) {
1308 repeatWithCollapsed
= finalRepeatIndex
;
1310 // Count up through the repeat tracks, until we have seen
1311 // finalRepeatIndex number of non-collapsed tracks.
1312 for (uint32_t repeatNoCollapsed
= 0;
1313 repeatNoCollapsed
< finalRepeatIndex
; repeatWithCollapsed
++) {
1314 if (!mRemovedRepeatTracks
[repeatWithCollapsed
]) {
1315 repeatNoCollapsed
++;
1318 // If we stopped iterating on a collapsed track, continue to the next
1319 // non-collapsed track.
1320 while (mRemovedRepeatTracks
[repeatWithCollapsed
]) {
1321 repeatWithCollapsed
++;
1324 return repeatTracks
[repeatWithCollapsed
% repeatTracks
.Length()];
1326 // The index is after the repeat auto range, adjust it to skip over the
1327 // repeat value. This will have no effect if there is no auto repeat,
1328 // since then RepeatEndDelta will return zero.
1329 index
-= RepeatEndDelta();
1332 if (index
>= mExpandedTracks
.Length()) {
1333 return getImplicitSize(index
- mExpandedTracks
.Length());
1335 auto& indices
= mExpandedTracks
[index
];
1336 const TrackListValue
& value
= mTrackListValues
[indices
.first
];
1337 if (value
.IsTrackSize()) {
1338 MOZ_ASSERT(indices
.second
== 0);
1339 return value
.AsTrackSize();
1341 return value
.AsTrackRepeat().track_sizes
.AsSpan()[indices
.second
];
1343 const StyleTrackBreadth
& MaxSizingFor(uint32_t aTrackIndex
) const {
1344 return SizingFor(aTrackIndex
).GetMax();
1346 const StyleTrackBreadth
& MinSizingFor(uint32_t aTrackIndex
) const {
1347 return SizingFor(aTrackIndex
).GetMin();
1349 uint32_t NumExplicitTracks() const {
1350 return mExpandedTracks
.Length() + RepeatEndDelta();
1352 uint32_t NumRepeatTracks() const { return mRepeatAutoEnd
- mRepeatAutoStart
; }
1353 // The difference between mExplicitGridEnd and mSizingFunctions.Length().
1354 int32_t RepeatEndDelta() const {
1355 return mHasRepeatAuto
? int32_t(NumRepeatTracks()) - 1 : 0;
1357 void SetNumRepeatTracks(uint32_t aNumRepeatTracks
) {
1358 MOZ_ASSERT(mHasRepeatAuto
|| aNumRepeatTracks
== 0);
1359 mRepeatAutoEnd
= mRepeatAutoStart
+ aNumRepeatTracks
;
1362 // Store mTrackListValues into mExpandedTracks with `repeat(INTEGER, ...)`
1364 void ExpandNonRepeatAutoTracks() {
1365 for (size_t i
= 0; i
< mTrackListValues
.Length(); ++i
) {
1366 auto& value
= mTrackListValues
[i
];
1367 if (value
.IsTrackSize()) {
1368 mExpandedTracks
.EmplaceBack(i
, 0);
1371 auto& repeat
= value
.AsTrackRepeat();
1372 if (!repeat
.count
.IsNumber()) {
1373 MOZ_ASSERT(i
== mRepeatAutoStart
);
1374 mRepeatAutoStart
= mExpandedTracks
.Length();
1375 mRepeatAutoEnd
= mRepeatAutoStart
+ repeat
.track_sizes
.Length();
1376 mExpandedTracks
.EmplaceBack(i
, 0);
1379 for (auto j
: IntegerRange(repeat
.count
.AsNumber())) {
1381 size_t trackSizesCount
= repeat
.track_sizes
.Length();
1382 for (auto k
: IntegerRange(trackSizesCount
)) {
1383 mExpandedTracks
.EmplaceBack(i
, k
);
1387 if (MOZ_UNLIKELY(mExpandedTracks
.Length() > kMaxLine
- 1)) {
1388 mExpandedTracks
.TruncateLength(kMaxLine
- 1);
1389 if (mHasRepeatAuto
&& mRepeatAutoStart
> kMaxLine
- 1) {
1390 // The `repeat(auto-fill/fit)` track is outside the clamped grid.
1391 mHasRepeatAuto
= false;
1396 // Some style data references, for easy access.
1397 const GridTemplate
& mTemplate
;
1398 const Span
<const TrackListValue
> mTrackListValues
;
1399 const StyleImplicitGridTracks
& mAutoSizing
;
1400 // An array from expanded track sizes (without expanding auto-repeat, which is
1401 // included just once at `mRepeatAutoStart`).
1403 // Each entry contains two indices, the first into mTrackListValues, and a
1404 // second one inside mTrackListValues' repeat value, if any, or zero
1406 nsTArray
<std::pair
<size_t, size_t>> mExpandedTracks
;
1407 // Offset from the start of the implicit grid to the first explicit track.
1408 uint32_t mExplicitGridOffset
;
1409 // The index of the repeat(auto-fill/fit) track, or zero if there is none.
1410 // Relative to mExplicitGridOffset (repeat tracks are explicit by definition).
1411 uint32_t mRepeatAutoStart
;
1412 // The (hypothetical) index of the last such repeat() track.
1413 uint32_t mRepeatAutoEnd
;
1414 // True if there is a specified repeat(auto-fill/fit) track.
1415 bool mHasRepeatAuto
;
1416 // True if this track (relative to mRepeatAutoStart) is a removed auto-fit.
1417 // Indexed relative to mExplicitGridOffset + mRepeatAutoStart.
1418 nsTArray
<bool> mRemovedRepeatTracks
;
1422 * Utility class to find line names. It provides an interface to lookup line
1423 * names with a dynamic number of repeat(auto-fill/fit) tracks taken into
1426 class MOZ_STACK_CLASS
nsGridContainerFrame::LineNameMap
{
1429 * Create a LineNameMap.
1430 * @param aStylePosition the style for the grid container
1431 * @param aImplicitNamedAreas the implicit areas for the grid container
1432 * @param aGridTemplate is the grid-template-rows/columns data for this axis
1433 * @param aParentLineNameMap the parent grid's map parallel to this map, or
1434 * null if this map isn't for a subgrid
1435 * @param aRange the subgrid's range in the parent grid, or null
1436 * @param aIsSameDirection true if our axis progresses in the same direction
1437 * in the subgrid and parent
1439 LineNameMap(const nsStylePosition
* aStylePosition
,
1440 const ImplicitNamedAreas
* aImplicitNamedAreas
,
1441 const TrackSizingFunctions
& aTracks
,
1442 const LineNameMap
* aParentLineNameMap
, const LineRange
* aRange
,
1443 bool aIsSameDirection
)
1444 : mStylePosition(aStylePosition
),
1445 mAreas(aImplicitNamedAreas
),
1446 mRepeatAutoStart(aTracks
.mRepeatAutoStart
),
1447 mRepeatAutoEnd(aTracks
.mRepeatAutoEnd
),
1448 mRepeatEndDelta(aTracks
.RepeatEndDelta()),
1449 mParentLineNameMap(aParentLineNameMap
),
1451 mIsSameDirection(aIsSameDirection
),
1452 mHasRepeatAuto(aTracks
.mHasRepeatAuto
) {
1453 if (MOZ_UNLIKELY(aRange
)) { // subgrid case
1455 mClampMaxLine
= 1 + aRange
->Extent();
1456 mRepeatAutoEnd
= mRepeatAutoStart
;
1457 const auto& styleSubgrid
= aTracks
.mTemplate
.AsSubgrid();
1458 const auto fillLen
= styleSubgrid
->fill_len
;
1459 mHasRepeatAuto
= fillLen
!= 0;
1460 if (mHasRepeatAuto
) {
1461 const auto& lineNameLists
= styleSubgrid
->names
;
1462 const int32_t extraAutoFillLineCount
=
1463 mClampMaxLine
- lineNameLists
.Length();
1464 // Maximum possible number of repeat name lists. This must be reduced
1465 // to a whole number of repetitions of the fill length.
1466 const uint32_t possibleRepeatLength
=
1467 std::max
<int32_t>(0, extraAutoFillLineCount
+ fillLen
);
1468 const uint32_t repeatRemainder
= possibleRepeatLength
% fillLen
;
1469 mRepeatAutoStart
= styleSubgrid
->fill_start
;
1471 mRepeatAutoStart
+ possibleRepeatLength
- repeatRemainder
;
1474 mClampMinLine
= kMinLine
;
1475 mClampMaxLine
= kMaxLine
;
1476 if (mHasRepeatAuto
) {
1477 mTrackAutoRepeatLineNames
=
1478 aTracks
.mTemplate
.GetRepeatAutoValue()->line_names
.AsSpan();
1481 ExpandRepeatLineNames(!!aRange
, aTracks
);
1482 if (mHasRepeatAuto
) {
1483 // We need mTemplateLinesEnd to be after all line names.
1484 // mExpandedLineNames has one repetition of the repeat(auto-fit/fill)
1485 // track name lists already, so we must subtract the number of repeat
1486 // track name lists to get to the number of non-repeat tracks, minus 2
1487 // because the first and last line name lists are shared with the
1488 // preceding and following non-repeat line name lists. We then add
1489 // mRepeatEndDelta to include the interior line name lists from repeat
1491 mTemplateLinesEnd
= mExpandedLineNames
.Length() -
1492 (mTrackAutoRepeatLineNames
.Length() - 2) +
1495 mTemplateLinesEnd
= mExpandedLineNames
.Length();
1497 MOZ_ASSERT(mHasRepeatAuto
|| mRepeatEndDelta
<= 0);
1498 MOZ_ASSERT(!mHasRepeatAuto
|| aRange
||
1499 (mExpandedLineNames
.Length() >= 2 &&
1500 mRepeatAutoStart
<= mExpandedLineNames
.Length()));
1503 // Store line names into mExpandedLineNames with `repeat(INTEGER, ...)`
1504 // expanded (for non-subgrid), and all `repeat(...)` expanded (for subgrid).
1505 void ExpandRepeatLineNames(bool aIsSubgrid
,
1506 const TrackSizingFunctions
& aTracks
) {
1507 auto lineNameLists
= aTracks
.mTemplate
.LineNameLists(aIsSubgrid
);
1509 const auto& trackListValues
= aTracks
.mTrackListValues
;
1510 const NameList
* nameListToMerge
= nullptr;
1511 // NOTE(emilio): We rely on std::move clearing out the array.
1512 SmallPointerArray
<const NameList
> names
;
1513 // This adjusts for outputting the repeat auto names in subgrid. In that
1514 // case, all of the repeat values are handled in a single iteration.
1515 const uint32_t subgridRepeatDelta
=
1516 (aIsSubgrid
&& mHasRepeatAuto
)
1517 ? (aTracks
.mTemplate
.AsSubgrid()->fill_len
- 1)
1519 const uint32_t end
= std::min
<uint32_t>(
1520 lineNameLists
.Length() - subgridRepeatDelta
, mClampMaxLine
+ 1);
1521 for (uint32_t i
= 0; i
< end
; ++i
) {
1523 if (MOZ_UNLIKELY(mHasRepeatAuto
&& i
== mRepeatAutoStart
)) {
1524 // XXX expand 'auto-fill' names for subgrid for now since HasNameAt()
1525 // only deals with auto-repeat **tracks** currently.
1526 const auto& styleSubgrid
= aTracks
.mTemplate
.AsSubgrid();
1527 MOZ_ASSERT(styleSubgrid
->fill_len
> 0);
1528 for (auto j
= i
; j
< mRepeatAutoEnd
; ++j
) {
1529 const auto repeatIndex
= (j
- i
) % styleSubgrid
->fill_len
;
1530 names
.AppendElement(
1531 &lineNameLists
[styleSubgrid
->fill_start
+ repeatIndex
]);
1532 mExpandedLineNames
.AppendElement(std::move(names
));
1534 } else if (mHasRepeatAuto
&& i
> mRepeatAutoStart
) {
1535 const auto& styleSubgrid
= aTracks
.mTemplate
.AsSubgrid();
1536 names
.AppendElement(&lineNameLists
[i
+ styleSubgrid
->fill_len
- 1]);
1537 mExpandedLineNames
.AppendElement(std::move(names
));
1539 names
.AppendElement(&lineNameLists
[i
]);
1540 mExpandedLineNames
.AppendElement(std::move(names
));
1542 // XXX expand repeat(<integer>, ...) line names here (bug 1583429)
1546 if (nameListToMerge
) {
1547 names
.AppendElement(nameListToMerge
);
1548 nameListToMerge
= nullptr;
1550 names
.AppendElement(&lineNameLists
[i
]);
1551 if (i
>= trackListValues
.Length()) {
1552 mExpandedLineNames
.AppendElement(std::move(names
));
1555 const auto& value
= trackListValues
[i
];
1556 if (value
.IsTrackSize()) {
1557 mExpandedLineNames
.AppendElement(std::move(names
));
1560 const auto& repeat
= value
.AsTrackRepeat();
1561 if (!repeat
.count
.IsNumber()) {
1562 const auto repeatNames
= repeat
.line_names
.AsSpan();
1563 // If the repeat was truncated due to more than kMaxLine tracks, then
1564 // the repeat will no longer be set on mRepeatAutoStart).
1565 MOZ_ASSERT(!mHasRepeatAuto
||
1566 mRepeatAutoStart
== mExpandedLineNames
.Length());
1567 MOZ_ASSERT(repeatNames
.Length() >= 2);
1568 for (const auto j
: IntegerRange(repeatNames
.Length() - 1)) {
1569 names
.AppendElement(&repeatNames
[j
]);
1570 mExpandedLineNames
.AppendElement(std::move(names
));
1572 nameListToMerge
= &repeatNames
[repeatNames
.Length() - 1];
1575 for (auto j
: IntegerRange(repeat
.count
.AsNumber())) {
1577 if (nameListToMerge
) {
1578 names
.AppendElement(nameListToMerge
);
1579 nameListToMerge
= nullptr;
1581 size_t trackSizesCount
= repeat
.track_sizes
.Length();
1582 auto repeatLineNames
= repeat
.line_names
.AsSpan();
1583 MOZ_ASSERT(repeatLineNames
.Length() == trackSizesCount
||
1584 repeatLineNames
.Length() == trackSizesCount
+ 1);
1585 for (auto k
: IntegerRange(trackSizesCount
)) {
1586 names
.AppendElement(&repeatLineNames
[k
]);
1587 mExpandedLineNames
.AppendElement(std::move(names
));
1589 if (repeatLineNames
.Length() == trackSizesCount
+ 1) {
1590 nameListToMerge
= &repeatLineNames
[trackSizesCount
];
1595 if (MOZ_UNLIKELY(mExpandedLineNames
.Length() > uint32_t(mClampMaxLine
))) {
1596 mExpandedLineNames
.TruncateLength(mClampMaxLine
);
1598 if (MOZ_UNLIKELY(mHasRepeatAuto
&& aIsSubgrid
)) {
1599 mHasRepeatAuto
= false; // we've expanded all subgrid auto-fill lines
1604 * Find the aNth occurrence of aName, searching forward if aNth is positive,
1605 * and in reverse if aNth is negative (aNth == 0 is invalid), starting from
1606 * aFromIndex (not inclusive), and return a 1-based line number.
1607 * Also take into account there is an unconditional match at the lines in
1609 * Return zero if aNth occurrences can't be found. In that case, aNth has
1610 * been decremented with the number of occurrences that were found (if any).
1612 * E.g. to search for "A 2" forward from the start of the grid: aName is "A"
1613 * aNth is 2 and aFromIndex is zero. To search for "A -2", aNth is -2 and
1614 * aFromIndex is ExplicitGridEnd + 1 (which is the line "before" the last
1615 * line when we're searching in reverse). For "span A 2", aNth is 2 when
1616 * used on a grid-[row|column]-end property and -2 for a *-start property,
1617 * and aFromIndex is the line (which we should skip) on the opposite property.
1619 uint32_t FindNamedLine(nsAtom
* aName
, int32_t* aNth
, uint32_t aFromIndex
,
1620 const nsTArray
<uint32_t>& aImplicitLines
) const {
1622 MOZ_ASSERT(!aName
->IsEmpty());
1623 MOZ_ASSERT(aNth
&& *aNth
!= 0);
1625 return FindLine(aName
, aNth
, aFromIndex
, aImplicitLines
);
1627 int32_t nth
= -*aNth
;
1628 int32_t line
= RFindLine(aName
, &nth
, aFromIndex
, aImplicitLines
);
1634 * Return a set of lines in aImplicitLines which matches the area name aName
1635 * on aSide. For example, for aName "a" and aSide being an end side, it
1636 * returns the line numbers which would match "a-end" in the relevant axis.
1637 * For subgrids it includes searching the relevant axis in all ancestor
1638 * grids too (within this subgrid's spanned area). If an ancestor has
1639 * opposite direction, we switch aSide to the opposite logical side so we
1640 * match on the same physical side as the original subgrid we're resolving
1643 void FindNamedAreas(nsAtom
* aName
, LogicalSide aSide
,
1644 nsTArray
<uint32_t>& aImplicitLines
) const {
1645 // True if we're currently in a map that has the same direction as 'this'.
1646 bool sameDirectionAsThis
= true;
1647 uint32_t min
= !mParentLineNameMap
? 1 : mClampMinLine
;
1648 uint32_t max
= mClampMaxLine
;
1649 for (auto* map
= this; true;) {
1650 uint32_t line
= map
->FindNamedArea(aName
, aSide
, min
, max
);
1652 if (MOZ_LIKELY(sameDirectionAsThis
)) {
1655 line
= max
- line
+ 1;
1657 aImplicitLines
.AppendElement(line
);
1659 auto* parent
= map
->mParentLineNameMap
;
1661 if (MOZ_UNLIKELY(aImplicitLines
.Length() > 1)) {
1662 // Remove duplicates and sort in ascending order.
1663 aImplicitLines
.Sort();
1664 for (size_t i
= 0; i
< aImplicitLines
.Length(); ++i
) {
1665 uint32_t prev
= aImplicitLines
[i
];
1667 const auto start
= j
;
1668 while (j
< aImplicitLines
.Length() && aImplicitLines
[j
] == prev
) {
1672 aImplicitLines
.RemoveElementsAt(start
, j
- start
);
1678 if (MOZ_UNLIKELY(!map
->mIsSameDirection
)) {
1679 aSide
= GetOppositeSide(aSide
);
1680 sameDirectionAsThis
= !sameDirectionAsThis
;
1682 min
= map
->TranslateToParentMap(min
);
1683 max
= map
->TranslateToParentMap(max
);
1685 MOZ_ASSERT(!map
->mIsSameDirection
);
1686 std::swap(min
, max
);
1693 * Return true if any implicit named areas match aName, in this map or
1694 * in any of our ancestor maps.
1696 bool HasImplicitNamedArea(nsAtom
* aName
) const {
1697 const auto* map
= this;
1699 if (map
->mAreas
&& map
->mAreas
->has(aName
)) {
1702 map
= map
->mParentLineNameMap
;
1707 // For generating line name data for devtools.
1708 nsTArray
<nsTArray
<StyleCustomIdent
>>
1709 GetResolvedLineNamesForComputedGridTrackInfo() const {
1710 nsTArray
<nsTArray
<StyleCustomIdent
>> result
;
1711 for (auto& expandedLine
: mExpandedLineNames
) {
1712 nsTArray
<StyleCustomIdent
> line
;
1713 for (auto* chunk
: expandedLine
) {
1714 for (auto& name
: chunk
->AsSpan()) {
1715 line
.AppendElement(name
);
1718 result
.AppendElement(std::move(line
));
1723 nsTArray
<RefPtr
<nsAtom
>> GetExplicitLineNamesAtIndex(uint32_t aIndex
) const {
1724 nsTArray
<RefPtr
<nsAtom
>> lineNames
;
1725 if (aIndex
< mTemplateLinesEnd
) {
1726 const auto nameLists
= GetLineNamesAt(aIndex
);
1727 for (const NameList
* nameList
: nameLists
) {
1728 for (const auto& name
: nameList
->AsSpan()) {
1729 lineNames
.AppendElement(name
.AsAtom());
1736 const nsTArray
<SmallPointerArray
<const NameList
>>& ExpandedLineNames() const {
1737 return mExpandedLineNames
;
1739 const Span
<const StyleOwnedSlice
<StyleCustomIdent
>>&
1740 TrackAutoRepeatLineNames() const {
1741 return mTrackAutoRepeatLineNames
;
1743 bool HasRepeatAuto() const { return mHasRepeatAuto
; }
1744 uint32_t NumRepeatTracks() const { return mRepeatAutoEnd
- mRepeatAutoStart
; }
1745 uint32_t RepeatAutoStart() const { return mRepeatAutoStart
; }
1747 // The min/max line number (1-based) for clamping.
1748 int32_t mClampMinLine
;
1749 int32_t mClampMaxLine
;
1752 // Return true if this map represents a subgridded axis.
1753 bool IsSubgridded() const { return mParentLineNameMap
!= nullptr; }
1756 * @see FindNamedLine, this function searches forward.
1758 uint32_t FindLine(nsAtom
* aName
, int32_t* aNth
, uint32_t aFromIndex
,
1759 const nsTArray
<uint32_t>& aImplicitLines
) const {
1760 MOZ_ASSERT(aNth
&& *aNth
> 0);
1761 int32_t nth
= *aNth
;
1762 // For a subgrid we need to search to the end of the grid rather than
1763 // the end of the local name list, since ancestors might match.
1764 const uint32_t end
= IsSubgridded() ? mClampMaxLine
: mTemplateLinesEnd
;
1766 uint32_t i
= aFromIndex
;
1767 for (; i
< end
; i
= line
) {
1769 if (Contains(i
, aName
) || aImplicitLines
.Contains(line
)) {
1775 for (auto implicitLine
: aImplicitLines
) {
1776 if (implicitLine
> i
) {
1777 // implicitLine is after the lines we searched above so it's last.
1778 // (grid-template-areas has more tracks than
1779 // grid-template-[rows|columns])
1781 return implicitLine
;
1785 MOZ_ASSERT(nth
> 0, "should have returned a valid line above already");
1791 * @see FindNamedLine, this function searches in reverse.
1793 uint32_t RFindLine(nsAtom
* aName
, int32_t* aNth
, uint32_t aFromIndex
,
1794 const nsTArray
<uint32_t>& aImplicitLines
) const {
1795 MOZ_ASSERT(aNth
&& *aNth
> 0);
1796 if (MOZ_UNLIKELY(aFromIndex
== 0)) {
1797 return 0; // There are no named lines beyond the start of the explicit
1800 --aFromIndex
; // (shift aFromIndex so we can treat it as inclusive)
1801 int32_t nth
= *aNth
;
1802 // Implicit lines may be beyond the explicit grid so we match those
1803 // first if it's within the mTemplateLinesEnd..aFromIndex range.
1804 // aImplicitLines is presumed sorted.
1805 // For a subgrid we need to search to the end of the grid rather than
1806 // the end of the local name list, since ancestors might match.
1807 const uint32_t end
= IsSubgridded() ? mClampMaxLine
: mTemplateLinesEnd
;
1808 for (auto implicitLine
: Reversed(aImplicitLines
)) {
1809 if (implicitLine
<= end
) {
1812 if (implicitLine
< aFromIndex
) {
1814 return implicitLine
;
1818 for (uint32_t i
= std::min(aFromIndex
, end
); i
; --i
) {
1819 if (Contains(i
- 1, aName
) || aImplicitLines
.Contains(i
)) {
1825 MOZ_ASSERT(nth
> 0, "should have returned a valid line above already");
1830 // Return true if aName exists at aIndex in this map or any parent map.
1831 bool Contains(uint32_t aIndex
, nsAtom
* aName
) const {
1832 const auto* map
= this;
1834 if (aIndex
< map
->mTemplateLinesEnd
&& map
->HasNameAt(aIndex
, aName
)) {
1837 auto* parent
= map
->mParentLineNameMap
;
1841 uint32_t line
= map
->TranslateToParentMap(aIndex
+ 1);
1842 MOZ_ASSERT(line
>= 1, "expected a 1-based line number");
1846 MOZ_ASSERT_UNREACHABLE("we always return from inside the loop above");
1849 static bool Contains(Span
<const StyleCustomIdent
> aNames
, nsAtom
* aName
) {
1850 for (auto& name
: aNames
) {
1851 if (name
.AsAtom() == aName
) {
1858 // Return true if aName exists at aIndex in this map.
1859 bool HasNameAt(const uint32_t aIndex
, nsAtom
* const aName
) const {
1860 const auto nameLists
= GetLineNamesAt(aIndex
);
1861 for (const NameList
* nameList
: nameLists
) {
1862 if (Contains(nameList
->AsSpan(), aName
)) {
1869 // Get the line names at an index.
1870 // This accounts for auto repeat. The results may be spread over multiple name
1871 // lists returned in the array, which is done to avoid unneccessarily copying
1872 // the arrays to concatenate them.
1873 SmallPointerArray
<const NameList
> GetLineNamesAt(
1874 const uint32_t aIndex
) const {
1875 SmallPointerArray
<const NameList
> names
;
1876 // The index into mExpandedLineNames to use, if aIndex doesn't point to a
1877 // name inside of a auto repeat.
1878 uint32_t repeatAdjustedIndex
= aIndex
;
1879 if (mHasRepeatAuto
) {
1880 // If the index is inside of the auto repeat, use the repeat line
1881 // names. Otherwise, if the index is past the end of the repeat it must
1882 // be adjusted to acount for the repeat tracks.
1883 // mExpandedLineNames has the first and last line name lists from the
1884 // repeat in it already, so we can just ignore aIndex == mRepeatAutoStart
1885 // and treat when aIndex == mRepeatAutoEnd the same as any line after the
1887 const uint32_t maxRepeatLine
= mTrackAutoRepeatLineNames
.Length() - 1;
1888 if (aIndex
> mRepeatAutoStart
&& aIndex
< mRepeatAutoEnd
) {
1889 // The index is inside the auto repeat. Calculate the lines to use,
1890 // including the previous repetitions final names when we roll over
1891 // from one repetition to the next.
1892 const uint32_t repeatIndex
=
1893 (aIndex
- mRepeatAutoStart
) % maxRepeatLine
;
1894 if (repeatIndex
== 0) {
1895 // The index is at the start of a new repetition. The start of the
1896 // first repetition is intentionally ignored above, so this will
1897 // consider both the end of the previous repetition and the start
1898 // the one that contains aIndex.
1899 names
.AppendElement(&mTrackAutoRepeatLineNames
[maxRepeatLine
]);
1901 names
.AppendElement(&mTrackAutoRepeatLineNames
[repeatIndex
]);
1904 if (aIndex
!= mRepeatAutoStart
&& aIndex
>= mRepeatAutoEnd
) {
1905 // Adjust the index to account for the line names of the repeat.
1906 repeatAdjustedIndex
-= mRepeatEndDelta
;
1907 repeatAdjustedIndex
+= mTrackAutoRepeatLineNames
.Length() - 2;
1910 MOZ_ASSERT(names
.IsEmpty());
1911 // The index is not inside the repeat tracks, or no repeat tracks exist.
1912 const auto& nameLists
= mExpandedLineNames
[repeatAdjustedIndex
];
1913 for (const NameList
* nameList
: nameLists
) {
1914 names
.AppendElement(nameList
);
1919 // Translate a subgrid line (1-based) to a parent line (1-based).
1920 uint32_t TranslateToParentMap(uint32_t aLine
) const {
1921 if (MOZ_LIKELY(mIsSameDirection
)) {
1922 return aLine
+ mRange
->mStart
;
1924 MOZ_ASSERT(mRange
->mEnd
+ 1 >= aLine
);
1925 return mRange
->mEnd
- (aLine
- 1) + 1;
1929 * Return the 1-based line that match aName in 'grid-template-areas'
1930 * on the side aSide. Clamp the result to aMin..aMax but require
1931 * that some part of the area is inside for it to match.
1932 * Return zero if there is no match.
1934 uint32_t FindNamedArea(nsAtom
* aName
, LogicalSide aSide
, int32_t aMin
,
1935 int32_t aMax
) const {
1936 if (const NamedArea
* area
= FindNamedArea(aName
)) {
1937 int32_t start
= IsBlock(aSide
) ? area
->rows
.start
: area
->columns
.start
;
1938 int32_t end
= IsBlock(aSide
) ? area
->rows
.end
: area
->columns
.end
;
1939 if (IsStart(aSide
)) {
1940 if (start
>= aMin
) {
1941 if (start
<= aMax
) {
1944 } else if (end
>= aMin
) {
1952 } else if (start
<= aMax
) {
1957 return 0; // no match
1961 * A convenience method to lookup a name in 'grid-template-areas'.
1962 * @return null if not found
1964 const NamedArea
* FindNamedArea(nsAtom
* aName
) const {
1965 if (mStylePosition
->mGridTemplateAreas
.IsNone()) {
1968 const auto areas
= mStylePosition
->mGridTemplateAreas
.AsAreas();
1969 for (const NamedArea
& area
: areas
->areas
.AsSpan()) {
1970 if (area
.name
.AsAtom() == aName
) {
1977 // Some style data references, for easy access.
1978 const nsStylePosition
* mStylePosition
;
1979 const ImplicitNamedAreas
* mAreas
;
1980 // The expanded list of line-names. Each entry is usually a single NameList,
1981 // but can be multiple in the case where repeat() expands to something that
1982 // has a line name list at the end.
1983 nsTArray
<SmallPointerArray
<const NameList
>> mExpandedLineNames
;
1984 // The repeat(auto-fill/fit) track value, if any. (always empty for subgrid)
1985 Span
<const StyleOwnedSlice
<StyleCustomIdent
>> mTrackAutoRepeatLineNames
;
1986 // The index of the repeat(auto-fill/fit) track, or zero if there is none.
1987 uint32_t mRepeatAutoStart
;
1988 // The index one past the end of the repeat(auto-fill/fit) tracks. Equal to
1989 // mRepeatAutoStart if there are no repeat(auto-fill/fit) tracks.
1990 uint32_t mRepeatAutoEnd
;
1991 // The total number of repeat tracks minus 1.
1992 int32_t mRepeatEndDelta
;
1993 // The end of the line name lists with repeat(auto-fill/fit) tracks accounted
1995 uint32_t mTemplateLinesEnd
;
1997 // The parent line map, or null if this map isn't for a subgrid.
1998 const LineNameMap
* mParentLineNameMap
;
1999 // The subgrid's range, or null if this map isn't for a subgrid.
2000 const LineRange
* mRange
;
2001 // True if the subgrid/parent axes progresses in the same direction.
2002 const bool mIsSameDirection
;
2004 // True if there is a specified repeat(auto-fill/fit) track.
2005 bool mHasRepeatAuto
;
2009 * State for the tracks in one dimension.
2011 struct nsGridContainerFrame::Tracks
{
2012 explicit Tracks(LogicalAxis aAxis
)
2013 : mContentBoxSize(NS_UNCONSTRAINEDSIZE
),
2014 mGridGap(NS_UNCONSTRAINEDSIZE
),
2015 mStateUnion(TrackSize::StateBits
{0}),
2017 mCanResolveLineRangeSize(false),
2019 mBaselineSubtreeAlign
[BaselineSharingGroup::First
] = StyleAlignFlags::AUTO
;
2020 mBaselineSubtreeAlign
[BaselineSharingGroup::Last
] = StyleAlignFlags::AUTO
;
2021 mBaseline
[BaselineSharingGroup::First
] = NS_INTRINSIC_ISIZE_UNKNOWN
;
2022 mBaseline
[BaselineSharingGroup::Last
] = NS_INTRINSIC_ISIZE_UNKNOWN
;
2025 void Initialize(const TrackSizingFunctions
& aFunctions
,
2026 const NonNegativeLengthPercentageOrNormal
& aGridGap
,
2027 uint32_t aNumTracks
, nscoord aContentBoxSize
);
2030 * Return the union of the state bits for the tracks in aRange.
2032 TrackSize::StateBits
StateBitsForRange(const LineRange
& aRange
) const;
2034 // Some data we collect for aligning baseline-aligned items.
2035 struct ItemBaselineData
{
2036 uint32_t mBaselineTrack
;
2039 GridItemInfo
* mGridItem
;
2040 static bool IsBaselineTrackLessThan(const ItemBaselineData
& a
,
2041 const ItemBaselineData
& b
) {
2042 return a
.mBaselineTrack
< b
.mBaselineTrack
;
2047 * Calculate baseline offsets for the given set of items.
2048 * Helper for InitialzeItemBaselines.
2050 void CalculateItemBaselines(nsTArray
<ItemBaselineData
>& aBaselineItems
,
2051 BaselineSharingGroup aBaselineGroup
);
2054 * Initialize grid item baseline state and offsets.
2056 void InitializeItemBaselines(GridReflowInput
& aState
,
2057 nsTArray
<GridItemInfo
>& aGridItems
);
2060 * A masonry axis has four baseline alignment sets and each set can have
2061 * a first- and last-baseline alignment group, for a total of eight possible
2062 * baseline alignment groups, as follows:
2063 * set 1: the first item in each `start` or `stretch` grid track
2064 * set 2: the last item in each `start` grid track
2065 * set 3: the last item in each `end` or `stretch` grid track
2066 * set 4: the first item in each `end` grid track
2067 * (`start`/`end`/`stretch` refers to the relevant `align/justify-tracks`
2068 * value of the (grid-axis) start track for the item) Baseline-alignment for
2069 * set 1 and 2 always adjusts the item's padding or margin on the start side,
2070 * and set 3 and 4 on the end side, for both first- and last-baseline groups
2071 * in the set. (This is similar to regular grid which always adjusts
2072 * first-baseline groups on the start side and last-baseline groups on the
2073 * end-side. The crux is that those groups are always aligned to the track's
2074 * start/end side respectively.)
2076 struct BaselineAlignmentSet
{
2077 bool MatchTrackAlignment(StyleAlignFlags aTrackAlignment
) const {
2078 if (mTrackAlignmentSet
== BaselineAlignmentSet::StartStretch
) {
2079 return aTrackAlignment
== StyleAlignFlags::START
||
2080 (aTrackAlignment
== StyleAlignFlags::STRETCH
&&
2081 mItemSet
== BaselineAlignmentSet::FirstItems
);
2083 return aTrackAlignment
== StyleAlignFlags::END
||
2084 (aTrackAlignment
== StyleAlignFlags::STRETCH
&&
2085 mItemSet
== BaselineAlignmentSet::LastItems
);
2088 enum ItemSet
{ FirstItems
, LastItems
};
2089 ItemSet mItemSet
= FirstItems
;
2090 enum TrackAlignmentSet
{ StartStretch
, EndStretch
};
2091 TrackAlignmentSet mTrackAlignmentSet
= StartStretch
;
2093 void InitializeItemBaselinesInMasonryAxis(
2094 GridReflowInput
& aState
, nsTArray
<GridItemInfo
>& aGridItems
,
2095 BaselineAlignmentSet aSet
, const nsSize
& aContainerSize
,
2096 nsTArray
<nscoord
>& aTrackSizes
,
2097 nsTArray
<ItemBaselineData
>& aFirstBaselineItems
,
2098 nsTArray
<ItemBaselineData
>& aLastBaselineItems
);
2101 * Apply the additional alignment needed to align the baseline-aligned subtree
2102 * the item belongs to within its baseline track.
2104 void AlignBaselineSubtree(const GridItemInfo
& aGridItem
) const;
2106 enum class TrackSizingPhase
{
2108 ContentBasedMinimums
,
2114 // Some data we collect on each item that spans more than one track for step 3
2115 // and 4 of the Track Sizing Algorithm in ResolveIntrinsicSize below.
2116 // https://w3c.github.io/csswg-drafts/css-grid-1/#algo-spanning-items
2117 struct SpanningItemData final
{
2119 TrackSize::StateBits mState
;
2120 LineRange mLineRange
;
2122 nscoord mMinContentContribution
;
2123 nscoord mMaxContentContribution
;
2126 static bool IsSpanLessThan(const SpanningItemData
& a
,
2127 const SpanningItemData
& b
) {
2128 return a
.mSpan
< b
.mSpan
;
2131 template <TrackSizingPhase phase
>
2132 nscoord
SizeContributionForPhase() const {
2134 case TrackSizingPhase::IntrinsicMinimums
:
2136 case TrackSizingPhase::ContentBasedMinimums
:
2137 case TrackSizingPhase::IntrinsicMaximums
:
2138 return mMinContentContribution
;
2139 case TrackSizingPhase::MaxContentMinimums
:
2140 case TrackSizingPhase::MaxContentMaximums
:
2141 return mMaxContentContribution
;
2143 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
2149 "SpanningItemData { mSpan: %d, mState: %d, mLineRange: (%d, %d), "
2150 "mMinSize: %d, mMinContentContribution: %d, mMaxContentContribution: "
2152 mSpan
, mState
, mLineRange
.mStart
, mLineRange
.mEnd
, mMinSize
,
2153 mMinContentContribution
, mMaxContentContribution
, mFrame
);
2158 using FitContentClamper
=
2159 std::function
<bool(uint32_t aTrack
, nscoord aMinSize
, nscoord
* aSize
)>;
2161 // Helper method for ResolveIntrinsicSize.
2162 template <TrackSizingPhase phase
>
2163 bool GrowSizeForSpanningItems(
2164 nsTArray
<SpanningItemData
>::iterator aIter
,
2165 nsTArray
<SpanningItemData
>::iterator aIterEnd
,
2166 nsTArray
<uint32_t>& aTracks
, nsTArray
<TrackSize
>& aPlan
,
2167 nsTArray
<TrackSize
>& aItemPlan
, TrackSize::StateBits aSelector
,
2168 const FitContentClamper
& aFitContentClamper
= nullptr,
2169 bool aNeedInfinitelyGrowableFlag
= false);
2171 * Resolve Intrinsic Track Sizes.
2172 * http://dev.w3.org/csswg/css-grid/#algo-content
2174 void ResolveIntrinsicSize(GridReflowInput
& aState
,
2175 nsTArray
<GridItemInfo
>& aGridItems
,
2176 const TrackSizingFunctions
& aFunctions
,
2177 LineRange
GridArea::*aRange
,
2178 nscoord aPercentageBasis
,
2179 SizingConstraint aConstraint
);
2182 * Helper for ResolveIntrinsicSize. It implements step 1 "size tracks to fit
2183 * non-spanning items" in the spec. Return true if the track has a <flex>
2184 * max-sizing function, false otherwise.
2186 bool ResolveIntrinsicSizeForNonSpanningItems(
2187 GridReflowInput
& aState
, const TrackSizingFunctions
& aFunctions
,
2188 nscoord aPercentageBasis
, SizingConstraint aConstraint
,
2189 const LineRange
& aRange
, const GridItemInfo
& aGridItem
);
2191 // Helper method that returns the track size to use in §11.5.1.2
2192 // https://drafts.csswg.org/css-grid/#extra-space
2193 template <TrackSizingPhase phase
>
2194 static nscoord
StartSizeInDistribution(const TrackSize
& aSize
) {
2196 case TrackSizingPhase::IntrinsicMinimums
:
2197 case TrackSizingPhase::ContentBasedMinimums
:
2198 case TrackSizingPhase::MaxContentMinimums
:
2200 case TrackSizingPhase::IntrinsicMaximums
:
2201 case TrackSizingPhase::MaxContentMaximums
:
2202 if (aSize
.mLimit
== NS_UNCONSTRAINEDSIZE
) {
2205 return aSize
.mLimit
;
2207 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
2211 * Collect the tracks which are growable (matching aSelector) into
2212 * aGrowableTracks, and return the amount of space that can be used
2213 * to grow those tracks. This method implements CSS Grid §11.5.1.2.
2214 * https://drafts.csswg.org/css-grid/#extra-space
2216 template <TrackSizingPhase phase
>
2217 nscoord
CollectGrowable(nscoord aAvailableSpace
, const LineRange
& aRange
,
2218 TrackSize::StateBits aSelector
,
2219 nsTArray
<uint32_t>& aGrowableTracks
) const {
2220 MOZ_ASSERT(aAvailableSpace
> 0, "why call me?");
2221 nscoord space
= aAvailableSpace
- mGridGap
* (aRange
.Extent() - 1);
2222 for (auto i
: aRange
.Range()) {
2223 const TrackSize
& sz
= mSizes
[i
];
2224 space
-= StartSizeInDistribution
<phase
>(sz
);
2228 if (sz
.mState
& aSelector
) {
2229 aGrowableTracks
.AppendElement(i
);
2232 return aGrowableTracks
.IsEmpty() ? 0 : space
;
2235 template <TrackSizingPhase phase
>
2236 void InitializeItemPlan(nsTArray
<TrackSize
>& aItemPlan
,
2237 const nsTArray
<uint32_t>& aTracks
) const {
2238 for (uint32_t track
: aTracks
) {
2239 auto& plan
= aItemPlan
[track
];
2240 const TrackSize
& sz
= mSizes
[track
];
2241 plan
.mBase
= StartSizeInDistribution
<phase
>(sz
);
2242 bool unlimited
= sz
.mState
& TrackSize::eInfinitelyGrowable
;
2243 plan
.mLimit
= unlimited
? NS_UNCONSTRAINEDSIZE
: sz
.mLimit
;
2244 plan
.mState
= sz
.mState
;
2248 template <TrackSizingPhase phase
>
2249 void InitializePlan(nsTArray
<TrackSize
>& aPlan
) const {
2250 for (size_t i
= 0, len
= aPlan
.Length(); i
< len
; ++i
) {
2251 auto& plan
= aPlan
[i
];
2252 const auto& sz
= mSizes
[i
];
2253 plan
.mBase
= StartSizeInDistribution
<phase
>(sz
);
2254 MOZ_ASSERT(phase
== TrackSizingPhase::MaxContentMaximums
||
2255 !(sz
.mState
& TrackSize::eInfinitelyGrowable
),
2256 "forgot to reset the eInfinitelyGrowable bit?");
2257 plan
.mState
= sz
.mState
;
2261 template <TrackSizingPhase phase
>
2262 void CopyPlanToSize(const nsTArray
<TrackSize
>& aPlan
,
2263 bool aNeedInfinitelyGrowableFlag
= false) {
2264 for (size_t i
= 0, len
= mSizes
.Length(); i
< len
; ++i
) {
2265 const auto& plan
= aPlan
[i
];
2266 MOZ_ASSERT(plan
.mBase
>= 0);
2267 auto& sz
= mSizes
[i
];
2269 case TrackSizingPhase::IntrinsicMinimums
:
2270 case TrackSizingPhase::ContentBasedMinimums
:
2271 case TrackSizingPhase::MaxContentMinimums
:
2272 sz
.mBase
= plan
.mBase
;
2274 case TrackSizingPhase::IntrinsicMaximums
:
2275 if (plan
.mState
& TrackSize::eModified
) {
2276 if (sz
.mLimit
== NS_UNCONSTRAINEDSIZE
&&
2277 aNeedInfinitelyGrowableFlag
) {
2278 sz
.mState
|= TrackSize::eInfinitelyGrowable
;
2280 sz
.mLimit
= plan
.mBase
;
2283 case TrackSizingPhase::MaxContentMaximums
:
2284 if (plan
.mState
& TrackSize::eModified
) {
2285 sz
.mLimit
= plan
.mBase
;
2287 sz
.mState
&= ~TrackSize::eInfinitelyGrowable
;
2294 * Grow the planned size for tracks in aGrowableTracks up to their limit
2295 * and then freeze them (all aGrowableTracks must be unfrozen on entry).
2296 * Subtract the space added from aAvailableSpace and return that.
2298 nscoord
GrowTracksToLimit(nscoord aAvailableSpace
, nsTArray
<TrackSize
>& aPlan
,
2299 const nsTArray
<uint32_t>& aGrowableTracks
,
2300 const FitContentClamper
& aFitContentClamper
) const {
2301 MOZ_ASSERT(aAvailableSpace
> 0 && aGrowableTracks
.Length() > 0);
2302 nscoord space
= aAvailableSpace
;
2303 uint32_t numGrowable
= aGrowableTracks
.Length();
2305 nscoord spacePerTrack
= std::max
<nscoord
>(space
/ numGrowable
, 1);
2306 for (uint32_t track
: aGrowableTracks
) {
2307 TrackSize
& sz
= aPlan
[track
];
2308 if (sz
.IsFrozen()) {
2311 nscoord newBase
= sz
.mBase
+ spacePerTrack
;
2312 nscoord limit
= sz
.mLimit
;
2313 if (MOZ_UNLIKELY((sz
.mState
& TrackSize::eFitContent
) &&
2314 aFitContentClamper
)) {
2315 // Clamp the limit to the fit-content() size, for §12.5.2 step 5/6.
2316 aFitContentClamper(track
, sz
.mBase
, &limit
);
2318 if (newBase
> limit
) {
2319 nscoord consumed
= limit
- sz
.mBase
;
2324 sz
.mState
|= TrackSize::eFrozen
;
2325 if (--numGrowable
== 0) {
2330 space
-= spacePerTrack
;
2332 MOZ_ASSERT(space
>= 0);
2338 MOZ_ASSERT_UNREACHABLE("we don't exit the loop above except by return");
2343 * Helper for GrowSelectedTracksUnlimited. For the set of tracks (S) that
2344 * match aMinSizingSelector: if a track in S doesn't match aMaxSizingSelector
2345 * then mark it with aSkipFlag. If all tracks in S were marked then unmark
2346 * them. Return aNumGrowable minus the number of tracks marked. It is
2347 * assumed that aPlan have no aSkipFlag set for tracks in aGrowableTracks
2348 * on entry to this method.
2350 static uint32_t MarkExcludedTracks(nsTArray
<TrackSize
>& aPlan
,
2351 uint32_t aNumGrowable
,
2352 const nsTArray
<uint32_t>& aGrowableTracks
,
2353 TrackSize::StateBits aMinSizingSelector
,
2354 TrackSize::StateBits aMaxSizingSelector
,
2355 TrackSize::StateBits aSkipFlag
) {
2356 bool foundOneSelected
= false;
2357 bool foundOneGrowable
= false;
2358 uint32_t numGrowable
= aNumGrowable
;
2359 for (uint32_t track
: aGrowableTracks
) {
2360 TrackSize
& sz
= aPlan
[track
];
2361 const auto state
= sz
.mState
;
2362 if (state
& aMinSizingSelector
) {
2363 foundOneSelected
= true;
2364 if (state
& aMaxSizingSelector
) {
2365 foundOneGrowable
= true;
2368 sz
.mState
|= aSkipFlag
;
2369 MOZ_ASSERT(numGrowable
!= 0);
2373 // 12.5 "if there are no such tracks, then all affected tracks"
2374 if (foundOneSelected
&& !foundOneGrowable
) {
2375 for (uint32_t track
: aGrowableTracks
) {
2376 aPlan
[track
].mState
&= ~aSkipFlag
;
2378 numGrowable
= aNumGrowable
;
2384 * Mark all tracks in aGrowableTracks with an eSkipGrowUnlimited bit if
2385 * they *shouldn't* grow unlimited in §11.5.1.2.3 "Distribute space beyond
2386 * growth limits" https://drafts.csswg.org/css-grid/#extra-space
2387 * Return the number of tracks that are still growable.
2389 template <TrackSizingPhase phase
>
2390 static uint32_t MarkExcludedTracks(nsTArray
<TrackSize
>& aPlan
,
2391 const nsTArray
<uint32_t>& aGrowableTracks
,
2392 TrackSize::StateBits aSelector
) {
2393 uint32_t numGrowable
= aGrowableTracks
.Length();
2394 if (phase
== TrackSizingPhase::IntrinsicMaximums
||
2395 phase
== TrackSizingPhase::MaxContentMaximums
) {
2396 // "when handling any intrinsic growth limit: all affected tracks"
2399 MOZ_ASSERT(aSelector
== (aSelector
& TrackSize::eIntrinsicMinSizing
) &&
2400 (aSelector
& TrackSize::eMaxContentMinSizing
),
2401 "Should only get here for track sizing steps 2.1 to 2.3");
2402 // Note that eMaxContentMinSizing is always included. We do those first:
2403 numGrowable
= MarkExcludedTracks(
2404 aPlan
, numGrowable
, aGrowableTracks
, TrackSize::eMaxContentMinSizing
,
2405 TrackSize::eMaxContentMaxSizing
, TrackSize::eSkipGrowUnlimited1
);
2406 // Now mark min-content/auto min-sizing tracks if requested.
2407 auto minOrAutoSelector
= aSelector
& ~TrackSize::eMaxContentMinSizing
;
2408 if (minOrAutoSelector
) {
2409 numGrowable
= MarkExcludedTracks(
2410 aPlan
, numGrowable
, aGrowableTracks
, minOrAutoSelector
,
2411 TrackSize::eIntrinsicMaxSizing
, TrackSize::eSkipGrowUnlimited2
);
2417 * Increase the planned size for tracks in aGrowableTracks that aren't
2418 * marked with a eSkipGrowUnlimited flag beyond their limit.
2419 * This implements the "Distribute space beyond growth limits" step in
2420 * https://drafts.csswg.org/css-grid/#distribute-extra-space
2422 void GrowSelectedTracksUnlimited(
2423 nscoord aAvailableSpace
, nsTArray
<TrackSize
>& aPlan
,
2424 const nsTArray
<uint32_t>& aGrowableTracks
, uint32_t aNumGrowable
,
2425 const FitContentClamper
& aFitContentClamper
) const {
2426 MOZ_ASSERT(aAvailableSpace
> 0 && aGrowableTracks
.Length() > 0 &&
2427 aNumGrowable
<= aGrowableTracks
.Length());
2428 nscoord space
= aAvailableSpace
;
2429 DebugOnly
<bool> didClamp
= false;
2430 while (aNumGrowable
) {
2431 nscoord spacePerTrack
= std::max
<nscoord
>(space
/ aNumGrowable
, 1);
2432 for (uint32_t track
: aGrowableTracks
) {
2433 TrackSize
& sz
= aPlan
[track
];
2434 if (sz
.mState
& TrackSize::eSkipGrowUnlimited
) {
2435 continue; // an excluded track
2437 nscoord delta
= spacePerTrack
;
2438 nscoord newBase
= sz
.mBase
+ delta
;
2439 if (MOZ_UNLIKELY((sz
.mState
& TrackSize::eFitContent
) &&
2440 aFitContentClamper
)) {
2441 // Clamp newBase to the fit-content() size, for §12.5.2 step 5/6.
2442 if (aFitContentClamper(track
, sz
.mBase
, &newBase
)) {
2444 delta
= newBase
- sz
.mBase
;
2445 MOZ_ASSERT(delta
>= 0, "track size shouldn't shrink");
2446 sz
.mState
|= TrackSize::eSkipGrowUnlimited1
;
2452 MOZ_ASSERT(space
>= 0);
2458 MOZ_ASSERT(didClamp
,
2459 "we don't exit the loop above except by return, "
2460 "unless we clamped some track's size");
2464 * Distribute aAvailableSpace to the planned base size for aGrowableTracks
2465 * up to their limits, then distribute the remaining space beyond the limits.
2467 template <TrackSizingPhase phase
>
2468 void DistributeToTrackSizes(nscoord aAvailableSpace
,
2469 nsTArray
<TrackSize
>& aPlan
,
2470 nsTArray
<TrackSize
>& aItemPlan
,
2471 nsTArray
<uint32_t>& aGrowableTracks
,
2472 TrackSize::StateBits aSelector
,
2473 const FitContentClamper
& aFitContentClamper
) {
2474 InitializeItemPlan
<phase
>(aItemPlan
, aGrowableTracks
);
2475 nscoord space
= GrowTracksToLimit(aAvailableSpace
, aItemPlan
,
2476 aGrowableTracks
, aFitContentClamper
);
2478 uint32_t numGrowable
=
2479 MarkExcludedTracks
<phase
>(aItemPlan
, aGrowableTracks
, aSelector
);
2480 GrowSelectedTracksUnlimited(space
, aItemPlan
, aGrowableTracks
,
2481 numGrowable
, aFitContentClamper
);
2483 for (uint32_t track
: aGrowableTracks
) {
2484 nscoord
& plannedSize
= aPlan
[track
].mBase
;
2485 nscoord itemIncurredSize
= aItemPlan
[track
].mBase
;
2486 if (plannedSize
< itemIncurredSize
) {
2487 plannedSize
= itemIncurredSize
;
2493 * Distribute aAvailableSize to the tracks. This implements 12.6 at:
2494 * http://dev.w3.org/csswg/css-grid/#algo-grow-tracks
2496 void DistributeFreeSpace(nscoord aAvailableSize
) {
2497 const uint32_t numTracks
= mSizes
.Length();
2498 if (MOZ_UNLIKELY(numTracks
== 0 || aAvailableSize
<= 0)) {
2501 if (aAvailableSize
== NS_UNCONSTRAINEDSIZE
) {
2502 for (TrackSize
& sz
: mSizes
) {
2503 sz
.mBase
= sz
.mLimit
;
2506 // Compute free space and count growable tracks.
2507 nscoord space
= aAvailableSize
;
2508 uint32_t numGrowable
= numTracks
;
2509 for (const TrackSize
& sz
: mSizes
) {
2511 MOZ_ASSERT(sz
.mBase
<= sz
.mLimit
);
2512 if (sz
.mBase
== sz
.mLimit
) {
2516 // Distribute the free space evenly to the growable tracks. If not exactly
2517 // divisable the remainder is added to the leading tracks.
2518 while (space
> 0 && numGrowable
) {
2519 nscoord spacePerTrack
= std::max
<nscoord
>(space
/ numGrowable
, 1);
2520 for (uint32_t i
= 0; i
< numTracks
&& space
> 0; ++i
) {
2521 TrackSize
& sz
= mSizes
[i
];
2522 if (sz
.mBase
== sz
.mLimit
) {
2525 nscoord newBase
= sz
.mBase
+ spacePerTrack
;
2526 if (newBase
>= sz
.mLimit
) {
2527 space
-= sz
.mLimit
- sz
.mBase
;
2528 sz
.mBase
= sz
.mLimit
;
2531 space
-= spacePerTrack
;
2540 * Implements "12.7.1. Find the Size of an 'fr'".
2541 * http://dev.w3.org/csswg/css-grid/#algo-find-fr-size
2542 * (The returned value is a 'nscoord' divided by a factor - a floating type
2543 * is used to avoid intermediary rounding errors.)
2545 float FindFrUnitSize(const LineRange
& aRange
,
2546 const nsTArray
<uint32_t>& aFlexTracks
,
2547 const TrackSizingFunctions
& aFunctions
,
2548 nscoord aSpaceToFill
) const;
2551 * Implements the "find the used flex fraction" part of StretchFlexibleTracks.
2552 * (The returned value is a 'nscoord' divided by a factor - a floating type
2553 * is used to avoid intermediary rounding errors.)
2555 float FindUsedFlexFraction(GridReflowInput
& aState
,
2556 nsTArray
<GridItemInfo
>& aGridItems
,
2557 const nsTArray
<uint32_t>& aFlexTracks
,
2558 const TrackSizingFunctions
& aFunctions
,
2559 nscoord aAvailableSize
) const;
2562 * Implements "12.7. Stretch Flexible Tracks"
2563 * http://dev.w3.org/csswg/css-grid/#algo-flex-tracks
2565 void StretchFlexibleTracks(GridReflowInput
& aState
,
2566 nsTArray
<GridItemInfo
>& aGridItems
,
2567 const TrackSizingFunctions
& aFunctions
,
2568 nscoord aAvailableSize
);
2571 * Implements "12.3. Track Sizing Algorithm"
2572 * http://dev.w3.org/csswg/css-grid/#algo-track-sizing
2574 void CalculateSizes(GridReflowInput
& aState
,
2575 nsTArray
<GridItemInfo
>& aGridItems
,
2576 const TrackSizingFunctions
& aFunctions
,
2577 nscoord aContentBoxSize
, LineRange
GridArea::*aRange
,
2578 SizingConstraint aConstraint
);
2581 * Apply 'align/justify-content', whichever is relevant for this axis.
2582 * https://drafts.csswg.org/css-align-3/#propdef-align-content
2584 void AlignJustifyContent(const nsStylePosition
* aStyle
,
2585 StyleContentDistribution aAligmentStyleValue
,
2586 WritingMode aWM
, nscoord aContentBoxSize
,
2587 bool aIsSubgridded
);
2589 nscoord
GridLineEdge(uint32_t aLine
, GridLineSide aSide
) const {
2590 if (MOZ_UNLIKELY(mSizes
.IsEmpty())) {
2591 // https://drafts.csswg.org/css-grid/#grid-definition
2592 // "... the explicit grid still contains one grid line in each axis."
2593 MOZ_ASSERT(aLine
== 0, "We should only resolve line 1 in an empty grid");
2596 MOZ_ASSERT(aLine
<= mSizes
.Length(), "mSizes is too small");
2597 if (aSide
== GridLineSide::BeforeGridGap
) {
2601 const TrackSize
& sz
= mSizes
[aLine
- 1];
2602 return sz
.mPosition
+ sz
.mBase
;
2604 if (aLine
== mSizes
.Length()) {
2605 return mContentBoxSize
;
2607 return mSizes
[aLine
].mPosition
;
2610 nscoord
SumOfGridTracksAndGaps() {
2611 return SumOfGridTracks() + SumOfGridGaps();
2614 nscoord
SumOfGridTracks() const {
2616 for (const TrackSize
& size
: mSizes
) {
2617 result
+= size
.mBase
;
2622 nscoord
SumOfGridGaps() const {
2623 auto len
= mSizes
.Length();
2624 return MOZ_LIKELY(len
> 1) ? (len
- 1) * mGridGap
: 0;
2628 * Break before aRow, i.e. set the eBreakBefore flag on aRow and set the grid
2629 * gap before aRow to zero (and shift all rows after it by the removed gap).
2631 void BreakBeforeRow(uint32_t aRow
) {
2632 MOZ_ASSERT(mAxis
== eLogicalAxisBlock
,
2633 "Should only be fragmenting in the block axis (between rows)");
2634 nscoord prevRowEndPos
= 0;
2636 auto& prevSz
= mSizes
[aRow
- 1];
2637 prevRowEndPos
= prevSz
.mPosition
+ prevSz
.mBase
;
2639 auto& sz
= mSizes
[aRow
];
2640 const nscoord gap
= sz
.mPosition
- prevRowEndPos
;
2641 sz
.mState
|= TrackSize::eBreakBefore
;
2643 for (uint32_t i
= aRow
, len
= mSizes
.Length(); i
< len
; ++i
) {
2644 mSizes
[i
].mPosition
-= gap
;
2650 * Set the size of aRow to aSize and adjust the position of all rows after it.
2652 void ResizeRow(uint32_t aRow
, nscoord aNewSize
) {
2653 MOZ_ASSERT(mAxis
== eLogicalAxisBlock
,
2654 "Should only be fragmenting in the block axis (between rows)");
2655 MOZ_ASSERT(aNewSize
>= 0);
2656 auto& sz
= mSizes
[aRow
];
2657 nscoord delta
= aNewSize
- sz
.mBase
;
2658 NS_WARNING_ASSERTION(delta
!= nscoord(0), "Useless call to ResizeRow");
2659 sz
.mBase
= aNewSize
;
2660 const uint32_t numRows
= mSizes
.Length();
2661 for (uint32_t r
= aRow
+ 1; r
< numRows
; ++r
) {
2662 mSizes
[r
].mPosition
+= delta
;
2666 nscoord
ResolveSize(const LineRange
& aRange
) const {
2667 MOZ_ASSERT(mCanResolveLineRangeSize
);
2668 MOZ_ASSERT(aRange
.Extent() > 0, "grid items cover at least one track");
2670 aRange
.ToPositionAndLength(mSizes
, &pos
, &size
);
2678 CopyableAutoTArray
<TrackSize
, 32> mSizes
;
2679 nscoord mContentBoxSize
;
2681 // The first(last)-baseline for the first(last) track in this axis.
2682 PerBaseline
<nscoord
> mBaseline
;
2683 // The union of the track min/max-sizing state bits in this axis.
2684 TrackSize::StateBits mStateUnion
;
2686 // Used for aligning a baseline-aligned subtree of items. The only possible
2687 // values are StyleAlignFlags::{START,END,CENTER,AUTO}. AUTO means there are
2688 // no baseline-aligned items in any track in that axis.
2689 // There is one alignment value for each BaselineSharingGroup.
2690 PerBaseline
<StyleAlignFlags
> mBaselineSubtreeAlign
;
2691 // True if track positions and sizes are final in this axis.
2692 bool mCanResolveLineRangeSize
;
2693 // True if this axis has masonry layout.
2698 void nsGridContainerFrame::Tracks::Dump() const {
2699 printf("%zu %s %s ", mSizes
.Length(), mIsMasonry
? "masonry" : "grid",
2700 mAxis
== eLogicalAxisBlock
? "rows" : "columns");
2701 TrackSize::DumpStateBits(mStateUnion
);
2703 for (uint32_t i
= 0, len
= mSizes
.Length(); i
< len
; ++i
) {
2708 double px
= AppUnitsPerCSSPixel();
2709 printf("Baselines: %.2fpx %2fpx\n",
2710 mBaseline
[BaselineSharingGroup::First
] / px
,
2711 mBaseline
[BaselineSharingGroup::Last
] / px
);
2712 printf("Gap: %.2fpx\n", mGridGap
/ px
);
2713 printf("ContentBoxSize: %.2fpx\n", mContentBoxSize
/ px
);
2718 * Grid data shared by all continuations, owned by the first-in-flow.
2719 * The data is initialized from the first-in-flow's GridReflowInput at
2720 * the end of its reflow. Fragmentation will modify mRows.mSizes -
2721 * the mPosition to remove the row gap at the break boundary, the mState
2722 * by setting the eBreakBefore flag, and mBase is modified when we decide
2723 * to grow a row. mOriginalRowData is setup by the first-in-flow and
2724 * not modified after that. It's used for undoing the changes to mRows.
2725 * mCols, mGridItems, mAbsPosItems are used for initializing the grid
2726 * reflow input for continuations, see GridReflowInput::Initialize below.
2728 struct nsGridContainerFrame::SharedGridData
{
2730 : mCols(eLogicalAxisInline
),
2731 mRows(eLogicalAxisBlock
),
2732 mGenerateComputedGridInfo(false) {}
2736 nscoord mBase
; // the original track size
2737 nscoord mGap
; // the original gap before a track
2739 nsTArray
<RowData
> mOriginalRowData
;
2740 nsTArray
<GridItemInfo
> mGridItems
;
2741 nsTArray
<GridItemInfo
> mAbsPosItems
;
2742 bool mGenerateComputedGridInfo
;
2745 * Only set on the first-in-flow. Continuations will Initialize() their
2746 * GridReflowInput from it.
2748 NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop
, SharedGridData
)
2751 struct MOZ_STACK_CLASS
nsGridContainerFrame::GridReflowInput
{
2752 GridReflowInput(nsGridContainerFrame
* aFrame
, const ReflowInput
& aRI
)
2753 : GridReflowInput(aFrame
, *aRI
.mRenderingContext
, &aRI
,
2754 aRI
.mStylePosition
, aRI
.GetWritingMode()) {}
2755 GridReflowInput(nsGridContainerFrame
* aFrame
, gfxContext
& aRC
)
2756 : GridReflowInput(aFrame
, aRC
, nullptr, aFrame
->StylePosition(),
2757 aFrame
->GetWritingMode()) {}
2760 * Initialize our track sizes and grid item info using the shared
2761 * state from aGridContainerFrame first-in-flow.
2763 void InitializeForContinuation(nsGridContainerFrame
* aGridContainerFrame
,
2764 nscoord aConsumedBSize
) {
2765 MOZ_ASSERT(aGridContainerFrame
->GetPrevInFlow(),
2766 "don't call this on the first-in-flow");
2767 MOZ_ASSERT(mGridItems
.IsEmpty() && mAbsPosItems
.IsEmpty(),
2768 "shouldn't have any item data yet");
2770 // Get the SharedGridData from the first-in-flow. Also calculate the number
2771 // of fragments before this so that we can figure out our start row below.
2772 uint32_t fragment
= 0;
2773 nsIFrame
* firstInFlow
= aGridContainerFrame
;
2774 for (auto pif
= aGridContainerFrame
->GetPrevInFlow(); pif
;
2775 pif
= pif
->GetPrevInFlow()) {
2779 mSharedGridData
= firstInFlow
->GetProperty(SharedGridData::Prop());
2780 MOZ_ASSERT(mSharedGridData
, "first-in-flow must have SharedGridData");
2782 // Find the start row for this fragment and undo breaks after that row
2783 // since the breaks might be different from the last reflow.
2784 auto& rowSizes
= mSharedGridData
->mRows
.mSizes
;
2785 const uint32_t numRows
= rowSizes
.Length();
2786 mStartRow
= numRows
;
2787 for (uint32_t row
= 0, breakCount
= 0; row
< numRows
; ++row
) {
2788 if (rowSizes
[row
].mState
& TrackSize::eBreakBefore
) {
2789 if (fragment
== ++breakCount
) {
2791 mFragBStart
= rowSizes
[row
].mPosition
;
2792 // Restore the original size for |row| and grid gaps / state after it.
2793 const auto& origRowData
= mSharedGridData
->mOriginalRowData
;
2794 rowSizes
[row
].mBase
= origRowData
[row
].mBase
;
2795 nscoord prevEndPos
= rowSizes
[row
].mPosition
+ rowSizes
[row
].mBase
;
2796 while (++row
< numRows
) {
2797 auto& sz
= rowSizes
[row
];
2798 const auto& orig
= origRowData
[row
];
2799 sz
.mPosition
= prevEndPos
+ orig
.mGap
;
2800 sz
.mBase
= orig
.mBase
;
2801 sz
.mState
&= ~TrackSize::eBreakBefore
;
2802 prevEndPos
= sz
.mPosition
+ sz
.mBase
;
2808 if (mStartRow
== numRows
||
2809 aGridContainerFrame
->IsMasonry(eLogicalAxisBlock
)) {
2810 // All of the grid's rows fit inside of previous grid-container fragments,
2811 // or it's a masonry axis.
2812 mFragBStart
= aConsumedBSize
;
2815 // Copy the shared track state.
2816 // XXX consider temporarily swapping the array elements instead and swapping
2817 // XXX them back after we're done reflowing, for better performance.
2818 // XXX (bug 1252002)
2819 mCols
= mSharedGridData
->mCols
;
2820 mRows
= mSharedGridData
->mRows
;
2822 if (firstInFlow
->GetProperty(UsedTrackSizes::Prop())) {
2823 auto* prop
= aGridContainerFrame
->GetProperty(UsedTrackSizes::Prop());
2825 prop
= new UsedTrackSizes();
2826 aGridContainerFrame
->SetProperty(UsedTrackSizes::Prop(), prop
);
2828 prop
->mCanResolveLineRangeSize
= {true, true};
2829 prop
->mSizes
[eLogicalAxisInline
].Assign(mCols
.mSizes
);
2830 prop
->mSizes
[eLogicalAxisBlock
].Assign(mRows
.mSizes
);
2833 // Copy item data from each child's first-in-flow data in mSharedGridData.
2834 // XXX NOTE: This is O(n^2) in the number of items. (bug 1252186)
2836 for (; !mIter
.AtEnd(); mIter
.Next()) {
2837 nsIFrame
* child
= *mIter
;
2838 nsIFrame
* childFirstInFlow
= child
->FirstInFlow();
2839 DebugOnly
<size_t> len
= mGridItems
.Length();
2840 for (auto& itemInfo
: mSharedGridData
->mGridItems
) {
2841 if (itemInfo
.mFrame
== childFirstInFlow
) {
2843 mGridItems
.AppendElement(GridItemInfo(child
, itemInfo
.mArea
));
2844 // Copy the item's baseline data so that the item's last fragment can
2845 // do 'last baseline' alignment if necessary.
2846 item
->mState
[0] |= itemInfo
.mState
[0] & ItemState::eAllBaselineBits
;
2847 item
->mState
[1] |= itemInfo
.mState
[1] & ItemState::eAllBaselineBits
;
2848 item
->mBaselineOffset
[0] = itemInfo
.mBaselineOffset
[0];
2849 item
->mBaselineOffset
[1] = itemInfo
.mBaselineOffset
[1];
2850 item
->mState
[0] |= itemInfo
.mState
[0] & ItemState::eAutoPlacement
;
2851 item
->mState
[1] |= itemInfo
.mState
[1] & ItemState::eAutoPlacement
;
2855 MOZ_ASSERT(mGridItems
.Length() == len
+ 1, "can't find GridItemInfo");
2858 // XXX NOTE: This is O(n^2) in the number of abs.pos. items. (bug 1252186)
2859 const nsFrameList
& absPosChildren
= aGridContainerFrame
->GetChildList(
2860 aGridContainerFrame
->GetAbsoluteListID());
2861 for (auto f
: absPosChildren
) {
2862 nsIFrame
* childFirstInFlow
= f
->FirstInFlow();
2863 DebugOnly
<size_t> len
= mAbsPosItems
.Length();
2864 for (auto& itemInfo
: mSharedGridData
->mAbsPosItems
) {
2865 if (itemInfo
.mFrame
== childFirstInFlow
) {
2866 mAbsPosItems
.AppendElement(GridItemInfo(f
, itemInfo
.mArea
));
2870 MOZ_ASSERT(mAbsPosItems
.Length() == len
+ 1, "can't find GridItemInfo");
2873 // Copy in the computed grid info state bit
2874 if (mSharedGridData
->mGenerateComputedGridInfo
) {
2875 aGridContainerFrame
->SetShouldGenerateComputedInfo(true);
2880 * Calculate our track sizes in the given axis.
2882 void CalculateTrackSizesForAxis(LogicalAxis aAxis
, const Grid
& aGrid
,
2884 SizingConstraint aConstraint
);
2887 * Calculate our track sizes.
2889 void CalculateTrackSizes(const Grid
& aGrid
, const LogicalSize
& aContentBox
,
2890 SizingConstraint aConstraint
);
2893 * Return the percentage basis for a grid item in its writing-mode.
2894 * If aAxis is eLogicalAxisInline then we return NS_UNCONSTRAINEDSIZE in
2895 * both axes since we know all track sizes are indefinite at this point
2896 * (we calculate column sizes before row sizes). Otherwise, assert that
2897 * column sizes are known and calculate the size for aGridItem.mArea.mCols
2898 * and use NS_UNCONSTRAINEDSIZE in the other axis.
2899 * @param aAxis the axis we're currently calculating track sizes for
2901 LogicalSize
PercentageBasisFor(LogicalAxis aAxis
,
2902 const GridItemInfo
& aGridItem
) const;
2905 * Return the containing block for a grid item occupying aArea.
2907 LogicalRect
ContainingBlockFor(const GridArea
& aArea
) const;
2910 * Return the containing block for an abs.pos. grid item occupying aArea.
2911 * Any 'auto' lines in the grid area will be aligned with grid container
2912 * containing block on that side.
2913 * @param aGridOrigin the origin of the grid
2914 * @param aGridCB the grid container containing block (its padding area)
2916 LogicalRect
ContainingBlockForAbsPos(const GridArea
& aArea
,
2917 const LogicalPoint
& aGridOrigin
,
2918 const LogicalRect
& aGridCB
) const;
2921 * Apply `align/justify-content` alignment in our masonry axis.
2922 * This aligns the "masonry box" within our content box size.
2924 void AlignJustifyContentInMasonryAxis(nscoord aMasonryBoxSize
,
2925 nscoord aContentBoxSize
);
2927 * Apply `align/justify-tracks` alignment in our masonry axis.
2929 void AlignJustifyTracksInMasonryAxis(const LogicalSize
& aContentSize
,
2930 const nsSize
& aContainerSize
);
2932 // Helper for CollectSubgridItemsForAxis.
2933 static void CollectSubgridForAxis(LogicalAxis aAxis
, WritingMode aContainerWM
,
2934 const LineRange
& aRangeInAxis
,
2935 const LineRange
& aRangeInOppositeAxis
,
2936 const GridItemInfo
& aItem
,
2937 const nsTArray
<GridItemInfo
>& aItems
,
2938 nsTArray
<GridItemInfo
>& aResult
) {
2939 const auto oppositeAxis
= GetOrthogonalAxis(aAxis
);
2940 bool itemIsSubgridInOppositeAxis
= aItem
.IsSubgrid(oppositeAxis
);
2941 auto subgridWM
= aItem
.mFrame
->GetWritingMode();
2942 bool isOrthogonal
= subgridWM
.IsOrthogonalTo(aContainerWM
);
2943 bool isSameDirInAxis
=
2944 subgridWM
.ParallelAxisStartsOnSameSide(aAxis
, aContainerWM
);
2945 bool isSameDirInOppositeAxis
=
2946 subgridWM
.ParallelAxisStartsOnSameSide(oppositeAxis
, aContainerWM
);
2948 // We'll Transpose the area below so these needs to be transposed as well.
2949 std::swap(isSameDirInAxis
, isSameDirInOppositeAxis
);
2951 uint32_t offsetInAxis
= aRangeInAxis
.mStart
;
2952 uint32_t gridEndInAxis
= aRangeInAxis
.Extent();
2953 uint32_t offsetInOppositeAxis
= aRangeInOppositeAxis
.mStart
;
2954 uint32_t gridEndInOppositeAxis
= aRangeInOppositeAxis
.Extent();
2955 for (const auto& subgridItem
: aItems
) {
2956 auto newItem
= aResult
.AppendElement(
2957 isOrthogonal
? subgridItem
.Transpose() : subgridItem
);
2958 if (MOZ_UNLIKELY(!isSameDirInAxis
)) {
2959 newItem
->ReverseDirection(aAxis
, gridEndInAxis
);
2961 newItem
->mArea
.LineRangeForAxis(aAxis
).Translate(offsetInAxis
);
2962 if (itemIsSubgridInOppositeAxis
) {
2963 if (MOZ_UNLIKELY(!isSameDirInOppositeAxis
)) {
2964 newItem
->ReverseDirection(oppositeAxis
, gridEndInOppositeAxis
);
2966 LineRange
& range
= newItem
->mArea
.LineRangeForAxis(oppositeAxis
);
2967 range
.Translate(offsetInOppositeAxis
);
2969 if (newItem
->IsSubgrid(aAxis
)) {
2971 subgridItem
.SubgridFrame()->GetProperty(Subgrid::Prop());
2972 CollectSubgridForAxis(aAxis
, aContainerWM
,
2973 newItem
->mArea
.LineRangeForAxis(aAxis
),
2974 newItem
->mArea
.LineRangeForAxis(oppositeAxis
),
2975 *newItem
, subgrid
->mGridItems
, aResult
);
2980 // Copy all descendant items from all our subgrid children that are subgridded
2981 // in aAxis recursively into aResult. All item grid area's and state are
2982 // translated to our coordinates.
2983 void CollectSubgridItemsForAxis(LogicalAxis aAxis
,
2984 nsTArray
<GridItemInfo
>& aResult
) const {
2985 for (const auto& item
: mGridItems
) {
2986 if (item
.IsSubgrid(aAxis
)) {
2987 const auto oppositeAxis
= GetOrthogonalAxis(aAxis
);
2988 auto* subgrid
= item
.SubgridFrame()->GetProperty(Subgrid::Prop());
2989 CollectSubgridForAxis(aAxis
, mWM
, item
.mArea
.LineRangeForAxis(aAxis
),
2990 item
.mArea
.LineRangeForAxis(oppositeAxis
), item
,
2991 subgrid
->mGridItems
, aResult
);
2996 Tracks
& TracksFor(LogicalAxis aAxis
) {
2997 return aAxis
== eLogicalAxisBlock
? mRows
: mCols
;
2999 const Tracks
& TracksFor(LogicalAxis aAxis
) const {
3000 return aAxis
== eLogicalAxisBlock
? mRows
: mCols
;
3003 CSSOrderAwareFrameIterator mIter
;
3004 const nsStylePosition
* const mGridStyle
;
3007 TrackSizingFunctions mColFunctions
;
3008 TrackSizingFunctions mRowFunctions
;
3010 * Info about each (normal flow) grid item.
3012 nsTArray
<GridItemInfo
> mGridItems
;
3014 * Info about each grid-aligned abs.pos. child.
3016 nsTArray
<GridItemInfo
> mAbsPosItems
;
3019 * @note mReflowInput may be null when using the 2nd ctor above. In this case
3020 * we'll construct a dummy parent reflow input if we need it to calculate
3021 * min/max-content contributions when sizing tracks.
3023 const ReflowInput
* const mReflowInput
;
3024 gfxContext
& mRenderingContext
;
3025 nsGridContainerFrame
* const mFrame
;
3026 SharedGridData
* mSharedGridData
; // [weak] owned by mFrame's first-in-flow.
3027 /** Computed border+padding with mSkipSides applied. */
3028 LogicalMargin mBorderPadding
;
3030 * BStart of this fragment in "grid space" (i.e. the concatenation of content
3031 * areas of all fragments). Equal to mRows.mSizes[mStartRow].mPosition,
3032 * or, if this fragment starts after the last row, the ConsumedBSize().
3034 nscoord mFragBStart
;
3035 /** The start row for this fragment. */
3038 * The start row for the next fragment, if any. If mNextFragmentStartRow ==
3039 * mStartRow then there are no rows in this fragment.
3041 uint32_t mNextFragmentStartRow
;
3042 /** Our tentative ApplySkipSides bits. */
3043 LogicalSides mSkipSides
;
3044 const WritingMode mWM
;
3045 /** Initialized lazily, when we find the fragmentainer. */
3046 bool mInFragmentainer
;
3049 GridReflowInput(nsGridContainerFrame
* aFrame
, gfxContext
& aRenderingContext
,
3050 const ReflowInput
* aReflowInput
,
3051 const nsStylePosition
* aGridStyle
, const WritingMode
& aWM
)
3052 : mIter(aFrame
, FrameChildListID::Principal
),
3053 mGridStyle(aGridStyle
),
3054 mCols(eLogicalAxisInline
),
3055 mRows(eLogicalAxisBlock
),
3056 mColFunctions(mGridStyle
->mGridTemplateColumns
,
3057 mGridStyle
->mGridAutoColumns
,
3058 aFrame
->IsSubgrid(eLogicalAxisInline
)),
3059 mRowFunctions(mGridStyle
->mGridTemplateRows
, mGridStyle
->mGridAutoRows
,
3060 aFrame
->IsSubgrid(eLogicalAxisBlock
)),
3061 mReflowInput(aReflowInput
),
3062 mRenderingContext(aRenderingContext
),
3064 mSharedGridData(nullptr),
3065 mBorderPadding(aWM
),
3068 mNextFragmentStartRow(0),
3069 mSkipSides(aFrame
->GetWritingMode()),
3071 mInFragmentainer(false) {
3072 MOZ_ASSERT(!aReflowInput
|| aReflowInput
->mFrame
== mFrame
);
3074 mBorderPadding
= aReflowInput
->ComputedLogicalBorderPadding(mWM
);
3075 mSkipSides
= aFrame
->PreReflowBlockLevelLogicalSkipSides();
3076 mBorderPadding
.ApplySkipSides(mSkipSides
);
3078 mCols
.mIsMasonry
= aFrame
->IsMasonry(eLogicalAxisInline
);
3079 mRows
.mIsMasonry
= aFrame
->IsMasonry(eLogicalAxisBlock
);
3080 MOZ_ASSERT(!(mCols
.mIsMasonry
&& mRows
.mIsMasonry
),
3081 "can't have masonry layout in both axes");
3085 using GridReflowInput
= nsGridContainerFrame::GridReflowInput
;
3088 * The Grid implements grid item placement and the state of the grid -
3089 * the size of the explicit/implicit grid, which cells are occupied etc.
3091 struct MOZ_STACK_CLASS
nsGridContainerFrame::Grid
{
3092 explicit Grid(const Grid
* aParentGrid
= nullptr) : mParentGrid(aParentGrid
) {}
3095 * Place all child frames into the grid and expand the (implicit) grid as
3096 * needed. The allocated GridAreas are stored in the GridAreaProperty
3097 * frame property on the child frame.
3098 * @param aRepeatSizing the container's [min-|max-]*size - used to determine
3099 * the number of repeat(auto-fill/fit) tracks.
3101 void PlaceGridItems(GridReflowInput
& aState
,
3102 const RepeatTrackSizingInput
& aRepeatSizing
);
3104 void SubgridPlaceGridItems(GridReflowInput
& aParentState
, Grid
* aParentGrid
,
3105 const GridItemInfo
& aGridItem
);
3108 * As above but for an abs.pos. child. Any 'auto' lines will be represented
3109 * by kAutoLine in the LineRange result.
3110 * @param aGridStart the first line in the final, but untranslated grid
3111 * @param aGridEnd the last line in the final, but untranslated grid
3113 LineRange
ResolveAbsPosLineRange(const StyleGridLine
& aStart
,
3114 const StyleGridLine
& aEnd
,
3115 const LineNameMap
& aNameMap
,
3116 LogicalAxis aAxis
, uint32_t aExplicitGridEnd
,
3117 int32_t aGridStart
, int32_t aGridEnd
,
3118 const nsStylePosition
* aStyle
);
3121 * Return a GridArea for abs.pos. item with non-auto lines placed at
3122 * a definite line (1-based) with placement errors resolved. One or both
3123 * positions may still be 'auto'.
3124 * @param aChild the abs.pos. grid item to place
3125 * @param aStyle the StylePosition() for the grid container
3127 GridArea
PlaceAbsPos(nsIFrame
* aChild
, const LineNameMap
& aColLineNameMap
,
3128 const LineNameMap
& aRowLineNameMap
,
3129 const nsStylePosition
* aStyle
);
3132 * Find the first column in row aLockedRow starting at aStartCol where aArea
3133 * could be placed without overlapping other items. The returned column may
3134 * cause aArea to overflow the current implicit grid bounds if placed there.
3136 uint32_t FindAutoCol(uint32_t aStartCol
, uint32_t aLockedRow
,
3137 const GridArea
* aArea
) const;
3140 * Place aArea in the first column (in row aArea->mRows.mStart) starting at
3141 * aStartCol without overlapping other items. The resulting aArea may
3142 * overflow the current implicit grid bounds.
3143 * @param aClampMaxColLine the maximum allowed column line number (zero-based)
3144 * Pre-condition: aArea->mRows.IsDefinite() is true.
3145 * Post-condition: aArea->IsDefinite() is true.
3147 void PlaceAutoCol(uint32_t aStartCol
, GridArea
* aArea
,
3148 uint32_t aClampMaxColLine
) const;
3151 * Find the first row in column aLockedCol starting at aStartRow where aArea
3152 * could be placed without overlapping other items. The returned row may
3153 * cause aArea to overflow the current implicit grid bounds if placed there.
3155 uint32_t FindAutoRow(uint32_t aLockedCol
, uint32_t aStartRow
,
3156 const GridArea
* aArea
) const;
3159 * Place aArea in the first row (in column aArea->mCols.mStart) starting at
3160 * aStartRow without overlapping other items. The resulting aArea may
3161 * overflow the current implicit grid bounds.
3162 * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
3163 * Pre-condition: aArea->mCols.IsDefinite() is true.
3164 * Post-condition: aArea->IsDefinite() is true.
3166 void PlaceAutoRow(uint32_t aStartRow
, GridArea
* aArea
,
3167 uint32_t aClampMaxRowLine
) const;
3170 * Place aArea in the first column starting at aStartCol,aStartRow without
3171 * causing it to overlap other items or overflow mGridColEnd.
3172 * If there's no such column in aStartRow, continue in position 1,aStartRow+1.
3173 * @param aClampMaxColLine the maximum allowed column line number (zero-based)
3174 * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
3175 * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
3176 * Post-condition: aArea->IsDefinite() is true.
3178 void PlaceAutoAutoInRowOrder(uint32_t aStartCol
, uint32_t aStartRow
,
3179 GridArea
* aArea
, uint32_t aClampMaxColLine
,
3180 uint32_t aClampMaxRowLine
) const;
3183 * Place aArea in the first row starting at aStartCol,aStartRow without
3184 * causing it to overlap other items or overflow mGridRowEnd.
3185 * If there's no such row in aStartCol, continue in position aStartCol+1,1.
3186 * @param aClampMaxColLine the maximum allowed column line number (zero-based)
3187 * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
3188 * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
3189 * Post-condition: aArea->IsDefinite() is true.
3191 void PlaceAutoAutoInColOrder(uint32_t aStartCol
, uint32_t aStartRow
,
3192 GridArea
* aArea
, uint32_t aClampMaxColLine
,
3193 uint32_t aClampMaxRowLine
) const;
3196 * Return aLine if it's inside the aMin..aMax range (inclusive),
3197 * otherwise return kAutoLine.
3199 static int32_t AutoIfOutside(int32_t aLine
, int32_t aMin
, int32_t aMax
) {
3200 MOZ_ASSERT(aMin
<= aMax
);
3201 if (aLine
< aMin
|| aLine
> aMax
) {
3208 * Inflate the implicit grid to include aArea.
3209 * @param aArea may be definite or auto
3211 void InflateGridFor(const GridArea
& aArea
) {
3212 mGridColEnd
= std::max(mGridColEnd
, aArea
.mCols
.HypotheticalEnd());
3213 mGridRowEnd
= std::max(mGridRowEnd
, aArea
.mRows
.HypotheticalEnd());
3214 MOZ_ASSERT(mGridColEnd
<= kTranslatedMaxLine
&&
3215 mGridRowEnd
<= kTranslatedMaxLine
);
3219 * Calculates the empty tracks in a repeat(auto-fit).
3220 * @param aOutNumEmptyLines Outputs the number of tracks which are empty.
3221 * @param aSizingFunctions Sizing functions for the relevant axis.
3222 * @param aNumGridLines Number of grid lines for the relevant axis.
3223 * @param aIsEmptyFunc Functor to check if a cell is empty. This should be
3224 * mCellMap.IsColEmpty or mCellMap.IsRowEmpty, depending on the axis.
3226 template <typename IsEmptyFuncT
>
3227 static Maybe
<nsTArray
<uint32_t>> CalculateAdjustForAutoFitElements(
3228 uint32_t* aOutNumEmptyTracks
, TrackSizingFunctions
& aSizingFunctions
,
3229 uint32_t aNumGridLines
, IsEmptyFuncT aIsEmptyFunc
);
3232 * Return a line number for (non-auto) aLine, per:
3233 * http://dev.w3.org/csswg/css-grid/#line-placement
3234 * @param aLine style data for the line (must be non-auto)
3235 * @param aNth a number of lines to find from aFromIndex, negative if the
3236 * search should be in reverse order. In the case aLine has
3237 * a specified line name, it's permitted to pass in zero which
3238 * will be treated as one.
3239 * @param aFromIndex the zero-based index to start counting from
3240 * @param aLineNameList the explicit named lines
3241 * @param aSide the axis+edge we're resolving names for (e.g. if we're
3242 resolving a grid-row-start line, pass eLogicalSideBStart)
3243 * @param aExplicitGridEnd the last line in the explicit grid
3244 * @param aStyle the StylePosition() for the grid container
3245 * @return a definite line (1-based), clamped to
3246 * the mClampMinLine..mClampMaxLine range
3248 int32_t ResolveLine(const StyleGridLine
& aLine
, int32_t aNth
,
3249 uint32_t aFromIndex
, const LineNameMap
& aNameMap
,
3250 LogicalSide aSide
, uint32_t aExplicitGridEnd
,
3251 const nsStylePosition
* aStyle
);
3254 * Helper method for ResolveLineRange.
3255 * @see ResolveLineRange
3256 * @return a pair (start,end) of lines
3258 typedef std::pair
<int32_t, int32_t> LinePair
;
3259 LinePair
ResolveLineRangeHelper(const StyleGridLine
& aStart
,
3260 const StyleGridLine
& aEnd
,
3261 const LineNameMap
& aNameMap
,
3262 LogicalAxis aAxis
, uint32_t aExplicitGridEnd
,
3263 const nsStylePosition
* aStyle
);
3266 * Return a LineRange based on the given style data. Non-auto lines
3267 * are resolved to a definite line number (1-based) per:
3268 * http://dev.w3.org/csswg/css-grid/#line-placement
3269 * with placement errors corrected per:
3270 * http://dev.w3.org/csswg/css-grid/#grid-placement-errors
3271 * @param aStyle the StylePosition() for the grid container
3272 * @param aStart style data for the start line
3273 * @param aEnd style data for the end line
3274 * @param aLineNameList the explicit named lines
3275 * @param aAxis the axis we're resolving names in
3276 * @param aExplicitGridEnd the last line in the explicit grid
3277 * @param aStyle the StylePosition() for the grid container
3279 LineRange
ResolveLineRange(const StyleGridLine
& aStart
,
3280 const StyleGridLine
& aEnd
,
3281 const LineNameMap
& aNameMap
, LogicalAxis aAxis
,
3282 uint32_t aExplicitGridEnd
,
3283 const nsStylePosition
* aStyle
);
3286 * Return a GridArea with non-auto lines placed at a definite line (1-based)
3287 * with placement errors resolved. One or both positions may still
3289 * @param aChild the grid item
3290 * @param aStyle the StylePosition() for the grid container
3292 GridArea
PlaceDefinite(nsIFrame
* aChild
, const LineNameMap
& aColLineNameMap
,
3293 const LineNameMap
& aRowLineNameMap
,
3294 const nsStylePosition
* aStyle
);
3296 bool HasImplicitNamedArea(nsAtom
* aName
) const {
3297 return mAreas
&& mAreas
->has(aName
);
3300 // Return true if aString ends in aSuffix and has at least one character
3301 // before the suffix. Assign aIndex to where the suffix starts.
3302 static bool IsNameWithSuffix(nsAtom
* aString
, const nsString
& aSuffix
,
3304 if (StringEndsWith(nsDependentAtomString(aString
), aSuffix
)) {
3305 *aIndex
= aString
->GetLength() - aSuffix
.Length();
3306 return *aIndex
!= 0;
3311 static bool IsNameWithEndSuffix(nsAtom
* aString
, uint32_t* aIndex
) {
3312 return IsNameWithSuffix(aString
, u
"-end"_ns
, aIndex
);
3315 static bool IsNameWithStartSuffix(nsAtom
* aString
, uint32_t* aIndex
) {
3316 return IsNameWithSuffix(aString
, u
"-start"_ns
, aIndex
);
3319 // Return the relevant parent LineNameMap for the given subgrid axis aAxis.
3320 const LineNameMap
* ParentLineMapForAxis(bool aIsOrthogonal
,
3321 LogicalAxis aAxis
) const {
3325 bool isRows
= aIsOrthogonal
== (aAxis
== eLogicalAxisInline
);
3326 return isRows
? mParentGrid
->mRowNameMap
: mParentGrid
->mColNameMap
;
3329 void SetLineMaps(const LineNameMap
* aColNameMap
,
3330 const LineNameMap
* aRowNameMap
) {
3331 mColNameMap
= aColNameMap
;
3332 mRowNameMap
= aRowNameMap
;
3336 * A CellMap holds state for each cell in the grid.
3337 * It's row major. It's sparse in the sense that it only has enough rows to
3338 * cover the last row that has a grid item. Each row only has enough entries
3339 * to cover columns that are occupied *on that row*, i.e. it's not a full
3340 * matrix covering the entire implicit grid. An absent Cell means that it's
3341 * unoccupied by any grid item.
3345 constexpr Cell() : mIsOccupied(false) {}
3346 bool mIsOccupied
: 1;
3349 void Fill(const GridArea
& aGridArea
) {
3350 MOZ_ASSERT(aGridArea
.IsDefinite());
3351 MOZ_ASSERT(aGridArea
.mRows
.mStart
< aGridArea
.mRows
.mEnd
);
3352 MOZ_ASSERT(aGridArea
.mCols
.mStart
< aGridArea
.mCols
.mEnd
);
3353 const auto numRows
= aGridArea
.mRows
.mEnd
;
3354 const auto numCols
= aGridArea
.mCols
.mEnd
;
3355 mCells
.EnsureLengthAtLeast(numRows
);
3356 for (auto i
= aGridArea
.mRows
.mStart
; i
< numRows
; ++i
) {
3357 nsTArray
<Cell
>& cellsInRow
= mCells
[i
];
3358 cellsInRow
.EnsureLengthAtLeast(numCols
);
3359 for (auto j
= aGridArea
.mCols
.mStart
; j
< numCols
; ++j
) {
3360 cellsInRow
[j
].mIsOccupied
= true;
3365 uint32_t IsEmptyCol(uint32_t aCol
) const {
3366 for (auto& row
: mCells
) {
3367 if (aCol
< row
.Length() && row
[aCol
].mIsOccupied
) {
3373 uint32_t IsEmptyRow(uint32_t aRow
) const {
3374 if (aRow
>= mCells
.Length()) {
3377 for (const Cell
& cell
: mCells
[aRow
]) {
3378 if (cell
.mIsOccupied
) {
3386 const size_t numRows
= mCells
.Length();
3387 for (size_t i
= 0; i
< numRows
; ++i
) {
3388 const nsTArray
<Cell
>& cellsInRow
= mCells
[i
];
3389 const size_t numCols
= cellsInRow
.Length();
3390 printf("%lu:\t", (unsigned long)i
+ 1);
3391 for (size_t j
= 0; j
< numCols
; ++j
) {
3392 printf(cellsInRow
[j
].mIsOccupied
? "X " : ". ");
3399 nsTArray
<nsTArray
<Cell
>> mCells
;
3403 * State for each cell in the grid.
3407 * @see HasImplicitNamedArea.
3409 ImplicitNamedAreas
* mAreas
;
3411 * The last column grid line (1-based) in the explicit grid.
3412 * (i.e. the number of explicit columns + 1)
3414 uint32_t mExplicitGridColEnd
;
3416 * The last row grid line (1-based) in the explicit grid.
3417 * (i.e. the number of explicit rows + 1)
3419 uint32_t mExplicitGridRowEnd
;
3420 // Same for the implicit grid, except these become zero-based after
3421 // resolving definite lines.
3422 uint32_t mGridColEnd
;
3423 uint32_t mGridRowEnd
;
3426 * Offsets from the start of the implicit grid to the start of the translated
3427 * explicit grid. They are zero if there are no implicit lines before 1,1.
3428 * e.g. "grid-column: span 3 / 1" makes mExplicitGridOffsetCol = 3 and the
3429 * corresponding GridArea::mCols will be 0 / 3 in the zero-based translated
3432 uint32_t mExplicitGridOffsetCol
;
3433 uint32_t mExplicitGridOffsetRow
;
3436 * Our parent grid if any.
3438 const Grid
* mParentGrid
;
3443 const LineNameMap
* mColNameMap
;
3444 const LineNameMap
* mRowNameMap
;
3448 * Compute margin+border+padding for aGridItem.mFrame (a subgrid) and store it
3449 * on its Subgrid property (and return that property).
3450 * aPercentageBasis is in the grid item's writing-mode.
3452 static Subgrid
* SubgridComputeMarginBorderPadding(
3453 const GridItemInfo
& aGridItem
, const LogicalSize
& aPercentageBasis
) {
3454 auto* subgridFrame
= aGridItem
.SubgridFrame();
3455 auto cbWM
= aGridItem
.mFrame
->GetParent()->GetWritingMode();
3456 auto* subgrid
= subgridFrame
->GetProperty(Subgrid::Prop());
3457 auto wm
= subgridFrame
->GetWritingMode();
3458 auto pmPercentageBasis
= cbWM
.IsOrthogonalTo(wm
) ? aPercentageBasis
.BSize(wm
)
3459 : aPercentageBasis
.ISize(wm
);
3460 SizeComputationInput
sz(subgridFrame
, nullptr, cbWM
, pmPercentageBasis
);
3461 subgrid
->mMarginBorderPadding
=
3462 sz
.ComputedLogicalMargin(cbWM
) + sz
.ComputedLogicalBorderPadding(cbWM
);
3464 if (aGridItem
.mFrame
!= subgridFrame
) {
3465 nsIScrollableFrame
* scrollFrame
= aGridItem
.mFrame
->GetScrollTargetFrame();
3468 sz
.ComputedLogicalMargin(cbWM
) == LogicalMargin(cbWM
) &&
3469 sz
.ComputedLogicalBorder(cbWM
) == LogicalMargin(cbWM
),
3470 "A scrolled inner frame should not have any margin or border!");
3472 // Add the margin and border from the (outer) scroll frame.
3473 SizeComputationInput
szScrollFrame(aGridItem
.mFrame
, nullptr, cbWM
,
3475 subgrid
->mMarginBorderPadding
+=
3476 szScrollFrame
.ComputedLogicalMargin(cbWM
) +
3477 szScrollFrame
.ComputedLogicalBorder(cbWM
);
3479 nsMargin ssz
= scrollFrame
->GetActualScrollbarSizes();
3480 subgrid
->mMarginBorderPadding
+= LogicalMargin(cbWM
, ssz
);
3483 if (aGridItem
.mFrame
->IsFieldSetFrame()) {
3484 const auto* f
= static_cast<nsFieldSetFrame
*>(aGridItem
.mFrame
);
3485 const auto* inner
= f
->GetInner();
3486 auto wm
= inner
->GetWritingMode();
3487 LogicalPoint pos
= inner
->GetLogicalPosition(aGridItem
.mFrame
->GetSize());
3488 // The legend is always on the BStart side and it inflates the fieldset's
3489 // "border area" size. The inner frame's b-start pos equals that size.
3490 LogicalMargin
offsets(wm
, pos
.B(wm
), 0, 0, 0);
3491 subgrid
->mMarginBorderPadding
+= offsets
.ConvertTo(cbWM
, wm
);
3497 static void CopyUsedTrackSizes(nsTArray
<TrackSize
>& aResult
,
3498 const nsGridContainerFrame
* aUsedTrackSizesFrame
,
3499 const UsedTrackSizes
* aUsedTrackSizes
,
3500 const nsGridContainerFrame
* aSubgridFrame
,
3501 const Subgrid
* aSubgrid
,
3502 LogicalAxis aSubgridAxis
) {
3503 MOZ_ASSERT(aSubgridFrame
->ParentGridContainerForSubgrid() ==
3504 aUsedTrackSizesFrame
);
3505 aResult
.SetLength(aSubgridAxis
== eLogicalAxisInline
? aSubgrid
->mGridColEnd
3506 : aSubgrid
->mGridRowEnd
);
3508 aSubgrid
->mIsOrthogonal
? GetOrthogonalAxis(aSubgridAxis
) : aSubgridAxis
;
3509 const auto& parentSizes
= aUsedTrackSizes
->mSizes
[parentAxis
];
3510 MOZ_ASSERT(aUsedTrackSizes
->mCanResolveLineRangeSize
[parentAxis
]);
3511 if (parentSizes
.IsEmpty()) {
3514 const auto& range
= aSubgrid
->mArea
.LineRangeForAxis(parentAxis
);
3515 const auto cbwm
= aUsedTrackSizesFrame
->GetWritingMode();
3516 const auto wm
= aSubgridFrame
->GetWritingMode();
3517 // Recompute the MBP to resolve percentages against the resolved track sizes.
3518 if (parentAxis
== eLogicalAxisInline
) {
3519 // Find the subgrid's grid item frame in its parent grid container. This
3520 // is usually the same as aSubgridFrame but it may also have a ScrollFrame,
3521 // FieldSetFrame etc. We just loop until we see the first ancestor
3522 // GridContainerFrame and pick the last frame we saw before that.
3523 // Note that all subgrids are inside a parent (sub)grid container.
3524 const nsIFrame
* outerGridItemFrame
= aSubgridFrame
;
3525 for (nsIFrame
* parent
= aSubgridFrame
->GetParent();
3526 parent
!= aUsedTrackSizesFrame
; parent
= parent
->GetParent()) {
3527 MOZ_ASSERT(!parent
->IsGridContainerFrame());
3528 outerGridItemFrame
= parent
;
3530 auto sizeInAxis
= range
.ToLength(aUsedTrackSizes
->mSizes
[parentAxis
]);
3531 LogicalSize pmPercentageBasis
=
3532 aSubgrid
->mIsOrthogonal
? LogicalSize(wm
, nscoord(0), sizeInAxis
)
3533 : LogicalSize(wm
, sizeInAxis
, nscoord(0));
3534 GridItemInfo
info(const_cast<nsIFrame
*>(outerGridItemFrame
),
3536 SubgridComputeMarginBorderPadding(info
, pmPercentageBasis
);
3538 const LogicalMargin
& mbp
= aSubgrid
->mMarginBorderPadding
;
3541 if (MOZ_LIKELY(cbwm
.ParallelAxisStartsOnSameSide(parentAxis
, wm
))) {
3542 startMBP
= mbp
.Start(parentAxis
, cbwm
);
3543 endMBP
= mbp
.End(parentAxis
, cbwm
);
3544 uint32_t i
= range
.mStart
;
3545 nscoord startPos
= parentSizes
[i
].mPosition
+ startMBP
;
3546 for (auto& sz
: aResult
) {
3547 sz
= parentSizes
[i
++];
3548 sz
.mPosition
-= startPos
;
3551 startMBP
= mbp
.End(parentAxis
, cbwm
);
3552 endMBP
= mbp
.Start(parentAxis
, cbwm
);
3553 uint32_t i
= range
.mEnd
- 1;
3554 const auto& parentEnd
= parentSizes
[i
];
3555 nscoord parentEndPos
= parentEnd
.mPosition
+ parentEnd
.mBase
- startMBP
;
3556 for (auto& sz
: aResult
) {
3557 sz
= parentSizes
[i
--];
3558 sz
.mPosition
= parentEndPos
- (sz
.mPosition
+ sz
.mBase
);
3561 auto& startTrack
= aResult
[0];
3562 startTrack
.mPosition
= 0;
3563 startTrack
.mBase
-= startMBP
;
3564 if (MOZ_UNLIKELY(startTrack
.mBase
< nscoord(0))) {
3565 // Our MBP doesn't fit in the start track. Adjust the track position
3566 // to maintain track alignment with our parent.
3567 startTrack
.mPosition
= startTrack
.mBase
;
3568 startTrack
.mBase
= nscoord(0);
3570 auto& endTrack
= aResult
.LastElement();
3571 endTrack
.mBase
-= endMBP
;
3572 if (MOZ_UNLIKELY(endTrack
.mBase
< nscoord(0))) {
3573 endTrack
.mBase
= nscoord(0);
3577 void nsGridContainerFrame::UsedTrackSizes::ResolveTrackSizesForAxis(
3578 nsGridContainerFrame
* aFrame
, LogicalAxis aAxis
, gfxContext
& aRC
) {
3579 if (mCanResolveLineRangeSize
[aAxis
]) {
3582 if (!aFrame
->IsSubgrid()) {
3583 // We can't resolve sizes in this axis at this point. aFrame is the top grid
3584 // container, which will store its final track sizes later once they're
3585 // resolved in this axis (in GridReflowInput::CalculateTrackSizesForAxis).
3586 // The single caller of this method only needs track sizes for
3587 // calculating a CB size and it will treat it as indefinite when
3591 auto* parent
= aFrame
->ParentGridContainerForSubgrid();
3592 auto* parentSizes
= parent
->GetUsedTrackSizes();
3594 parentSizes
= new UsedTrackSizes();
3595 parent
->SetProperty(UsedTrackSizes::Prop(), parentSizes
);
3597 auto* subgrid
= aFrame
->GetProperty(Subgrid::Prop());
3598 const auto parentAxis
=
3599 subgrid
->mIsOrthogonal
? GetOrthogonalAxis(aAxis
) : aAxis
;
3600 parentSizes
->ResolveTrackSizesForAxis(parent
, parentAxis
, aRC
);
3601 if (!parentSizes
->mCanResolveLineRangeSize
[parentAxis
]) {
3602 if (aFrame
->IsSubgrid(aAxis
)) {
3603 ResolveSubgridTrackSizesForAxis(aFrame
, aAxis
, subgrid
, aRC
,
3604 NS_UNCONSTRAINEDSIZE
);
3608 if (aFrame
->IsSubgrid(aAxis
)) {
3609 CopyUsedTrackSizes(mSizes
[aAxis
], parent
, parentSizes
, aFrame
, subgrid
,
3611 mCanResolveLineRangeSize
[aAxis
] = true;
3613 const auto& range
= subgrid
->mArea
.LineRangeForAxis(parentAxis
);
3614 nscoord contentBoxSize
= range
.ToLength(parentSizes
->mSizes
[parentAxis
]);
3615 auto parentWM
= aFrame
->GetParent()->GetWritingMode();
3617 subgrid
->mMarginBorderPadding
.StartEnd(parentAxis
, parentWM
);
3618 contentBoxSize
= std::max(nscoord(0), contentBoxSize
);
3619 ResolveSubgridTrackSizesForAxis(aFrame
, aAxis
, subgrid
, aRC
,
3624 void nsGridContainerFrame::UsedTrackSizes::ResolveSubgridTrackSizesForAxis(
3625 nsGridContainerFrame
* aFrame
, LogicalAxis aAxis
, Subgrid
* aSubgrid
,
3626 gfxContext
& aRC
, nscoord aContentBoxSize
) {
3627 GridReflowInput
state(aFrame
, aRC
);
3628 state
.mGridItems
= aSubgrid
->mGridItems
.Clone();
3630 grid
.mGridColEnd
= aSubgrid
->mGridColEnd
;
3631 grid
.mGridRowEnd
= aSubgrid
->mGridRowEnd
;
3632 state
.CalculateTrackSizesForAxis(aAxis
, grid
, aContentBoxSize
,
3633 SizingConstraint::NoConstraint
);
3634 const auto& tracks
= aAxis
== eLogicalAxisInline
? state
.mCols
: state
.mRows
;
3635 mSizes
[aAxis
].Assign(tracks
.mSizes
);
3636 mCanResolveLineRangeSize
[aAxis
] = tracks
.mCanResolveLineRangeSize
;
3637 MOZ_ASSERT(mCanResolveLineRangeSize
[aAxis
]);
3640 void nsGridContainerFrame::GridReflowInput::CalculateTrackSizesForAxis(
3641 LogicalAxis aAxis
, const Grid
& aGrid
, nscoord aContentBoxSize
,
3642 SizingConstraint aConstraint
) {
3643 auto& tracks
= aAxis
== eLogicalAxisInline
? mCols
: mRows
;
3644 const auto& sizingFunctions
=
3645 aAxis
== eLogicalAxisInline
? mColFunctions
: mRowFunctions
;
3646 const auto& gapStyle
= aAxis
== eLogicalAxisInline
? mGridStyle
->mColumnGap
3647 : mGridStyle
->mRowGap
;
3648 if (tracks
.mIsMasonry
) {
3649 // See comment on nsGridContainerFrame::MasonryLayout().
3650 tracks
.Initialize(sizingFunctions
, gapStyle
, 2, aContentBoxSize
);
3651 tracks
.mCanResolveLineRangeSize
= true;
3655 aAxis
== eLogicalAxisInline
? aGrid
.mGridColEnd
: aGrid
.mGridRowEnd
;
3656 Maybe
<TrackSizingFunctions
> fallbackTrackSizing
;
3658 bool useParentGaps
= false;
3659 const bool isSubgriddedAxis
= mFrame
->IsSubgrid(aAxis
);
3660 if (MOZ_LIKELY(!isSubgriddedAxis
)) {
3661 tracks
.Initialize(sizingFunctions
, gapStyle
, gridEnd
, aContentBoxSize
);
3664 nsLayoutUtils::ResolveGapToLength(gapStyle
, aContentBoxSize
);
3665 tracks
.mContentBoxSize
= aContentBoxSize
;
3666 const auto* subgrid
= mFrame
->GetProperty(Subgrid::Prop());
3667 tracks
.mSizes
.SetLength(gridEnd
);
3668 auto* parent
= mFrame
->ParentGridContainerForSubgrid();
3669 auto parentAxis
= subgrid
->mIsOrthogonal
? GetOrthogonalAxis(aAxis
) : aAxis
;
3670 const auto* parentSizes
= parent
->GetUsedTrackSizes();
3671 if (parentSizes
&& parentSizes
->mCanResolveLineRangeSize
[parentAxis
]) {
3672 CopyUsedTrackSizes(tracks
.mSizes
, parent
, parentSizes
, mFrame
, subgrid
,
3674 useParentGaps
= gapStyle
.IsNormal();
3676 fallbackTrackSizing
.emplace(TrackSizingFunctions::ForSubgridFallback(
3677 mFrame
, subgrid
, parent
, parentAxis
));
3678 tracks
.Initialize(*fallbackTrackSizing
, gapStyle
, gridEnd
,
3683 // We run the Track Sizing Algorithm in non-subgridded axes, and in some
3684 // cases in a subgridded axis when our parent track sizes aren't resolved yet.
3685 if (MOZ_LIKELY(!isSubgriddedAxis
) || fallbackTrackSizing
.isSome()) {
3686 const size_t origGridItemCount
= mGridItems
.Length();
3687 if (mFrame
->HasSubgridItems(aAxis
)) {
3688 CollectSubgridItemsForAxis(aAxis
, mGridItems
);
3690 tracks
.CalculateSizes(
3692 fallbackTrackSizing
? *fallbackTrackSizing
: sizingFunctions
,
3694 aAxis
== eLogicalAxisInline
? &GridArea::mCols
: &GridArea::mRows
,
3696 // XXXmats we're losing the baseline state of subgrid descendants that
3697 // CollectSubgridItemsForAxis added here. We need to propagate that
3698 // state into the subgrid's Reflow somehow...
3699 mGridItems
.TruncateLength(origGridItemCount
);
3702 if (aContentBoxSize
!= NS_UNCONSTRAINEDSIZE
) {
3703 auto alignment
= mGridStyle
->UsedContentAlignment(tracks
.mAxis
);
3704 tracks
.AlignJustifyContent(mGridStyle
, alignment
, mWM
, aContentBoxSize
,
3706 } else if (!useParentGaps
) {
3707 const nscoord gridGap
= tracks
.mGridGap
;
3709 for (TrackSize
& sz
: tracks
.mSizes
) {
3711 pos
+= sz
.mBase
+ gridGap
;
3715 if (aConstraint
== SizingConstraint::NoConstraint
&&
3716 (mFrame
->HasSubgridItems() || mFrame
->IsSubgrid())) {
3717 mFrame
->StoreUsedTrackSizes(aAxis
, tracks
.mSizes
);
3720 // positions and sizes are now final
3721 tracks
.mCanResolveLineRangeSize
= true;
3724 void nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
3725 const Grid
& aGrid
, const LogicalSize
& aContentBox
,
3726 SizingConstraint aConstraint
) {
3727 CalculateTrackSizesForAxis(eLogicalAxisInline
, aGrid
, aContentBox
.ISize(mWM
),
3729 CalculateTrackSizesForAxis(eLogicalAxisBlock
, aGrid
, aContentBox
.BSize(mWM
),
3733 // Align an item's margin box in its aAxis inside aCBSize.
3734 static void AlignJustifySelf(StyleAlignFlags aAlignment
, LogicalAxis aAxis
,
3735 AlignJustifyFlags aFlags
, nscoord aBaselineAdjust
,
3736 nscoord aCBSize
, const ReflowInput
& aRI
,
3737 const LogicalSize
& aChildSize
,
3738 LogicalPoint
* aPos
) {
3739 MOZ_ASSERT(aAlignment
!= StyleAlignFlags::AUTO
,
3740 "unexpected 'auto' "
3741 "computed value for normal flow grid item");
3743 // NOTE: this is the resulting frame offset (border box).
3744 nscoord offset
= CSSAlignUtils::AlignJustifySelf(
3745 aAlignment
, aAxis
, aFlags
, aBaselineAdjust
, aCBSize
, aRI
, aChildSize
);
3747 // Set the position (aPos) for the requested alignment.
3749 WritingMode wm
= aRI
.GetWritingMode();
3750 nscoord
& pos
= aAxis
== eLogicalAxisBlock
? aPos
->B(wm
) : aPos
->I(wm
);
3751 pos
+= MOZ_LIKELY(aFlags
& AlignJustifyFlags::SameSide
) ? offset
: -offset
;
3755 static void AlignSelf(const nsGridContainerFrame::GridItemInfo
& aGridItem
,
3756 StyleAlignFlags aAlignSelf
, nscoord aCBSize
,
3757 const WritingMode aCBWM
, const ReflowInput
& aRI
,
3758 const LogicalSize
& aSize
, AlignJustifyFlags aFlags
,
3759 LogicalPoint
* aPos
) {
3760 AlignJustifyFlags flags
= aFlags
;
3761 if (aAlignSelf
& StyleAlignFlags::SAFE
) {
3762 flags
|= AlignJustifyFlags::OverflowSafe
;
3764 aAlignSelf
&= ~StyleAlignFlags::FLAG_BITS
;
3766 WritingMode childWM
= aRI
.GetWritingMode();
3767 if (aCBWM
.ParallelAxisStartsOnSameSide(eLogicalAxisBlock
, childWM
)) {
3768 flags
|= AlignJustifyFlags::SameSide
;
3771 // Grid's 'align-self' axis is never parallel to the container's inline axis.
3772 if (aAlignSelf
== StyleAlignFlags::LEFT
||
3773 aAlignSelf
== StyleAlignFlags::RIGHT
) {
3774 aAlignSelf
= StyleAlignFlags::START
;
3776 if (MOZ_LIKELY(aAlignSelf
== StyleAlignFlags::NORMAL
)) {
3777 aAlignSelf
= StyleAlignFlags::STRETCH
;
3780 nscoord baselineAdjust
= 0;
3781 if (aAlignSelf
== StyleAlignFlags::BASELINE
||
3782 aAlignSelf
== StyleAlignFlags::LAST_BASELINE
) {
3783 aAlignSelf
= aGridItem
.GetSelfBaseline(aAlignSelf
, eLogicalAxisBlock
,
3785 // Adjust the baseline alignment value if the baseline affects the opposite
3786 // side of what AlignJustifySelf expects.
3787 auto state
= aGridItem
.mState
[eLogicalAxisBlock
];
3788 if (aAlignSelf
== StyleAlignFlags::LAST_BASELINE
&&
3789 !GridItemInfo::BaselineAlignmentAffectsEndSide(state
)) {
3790 aAlignSelf
= StyleAlignFlags::BASELINE
;
3791 } else if (aAlignSelf
== StyleAlignFlags::BASELINE
&&
3792 GridItemInfo::BaselineAlignmentAffectsEndSide(state
)) {
3793 aAlignSelf
= StyleAlignFlags::LAST_BASELINE
;
3797 bool isOrthogonal
= aCBWM
.IsOrthogonalTo(childWM
);
3798 LogicalAxis axis
= isOrthogonal
? eLogicalAxisInline
: eLogicalAxisBlock
;
3799 AlignJustifySelf(aAlignSelf
, axis
, flags
, baselineAdjust
, aCBSize
, aRI
, aSize
,
3803 static void JustifySelf(const nsGridContainerFrame::GridItemInfo
& aGridItem
,
3804 StyleAlignFlags aJustifySelf
, nscoord aCBSize
,
3805 const WritingMode aCBWM
, const ReflowInput
& aRI
,
3806 const LogicalSize
& aSize
, AlignJustifyFlags aFlags
,
3807 LogicalPoint
* aPos
) {
3808 AlignJustifyFlags flags
= aFlags
;
3809 if (aJustifySelf
& StyleAlignFlags::SAFE
) {
3810 flags
|= AlignJustifyFlags::OverflowSafe
;
3812 aJustifySelf
&= ~StyleAlignFlags::FLAG_BITS
;
3814 WritingMode childWM
= aRI
.GetWritingMode();
3815 if (aCBWM
.ParallelAxisStartsOnSameSide(eLogicalAxisInline
, childWM
)) {
3816 flags
|= AlignJustifyFlags::SameSide
;
3819 if (MOZ_LIKELY(aJustifySelf
== StyleAlignFlags::NORMAL
)) {
3820 aJustifySelf
= StyleAlignFlags::STRETCH
;
3823 nscoord baselineAdjust
= 0;
3824 // Grid's 'justify-self' axis is always parallel to the container's inline
3825 // axis, so justify-self:left|right always applies.
3826 if (aJustifySelf
== StyleAlignFlags::LEFT
) {
3828 aCBWM
.IsBidiLTR() ? StyleAlignFlags::START
: StyleAlignFlags::END
;
3829 } else if (aJustifySelf
== StyleAlignFlags::RIGHT
) {
3831 aCBWM
.IsBidiLTR() ? StyleAlignFlags::END
: StyleAlignFlags::START
;
3832 } else if (aJustifySelf
== StyleAlignFlags::BASELINE
||
3833 aJustifySelf
== StyleAlignFlags::LAST_BASELINE
) {
3834 aJustifySelf
= aGridItem
.GetSelfBaseline(aJustifySelf
, eLogicalAxisInline
,
3836 // Adjust the baseline alignment value if the baseline affects the opposite
3837 // side of what AlignJustifySelf expects.
3838 auto state
= aGridItem
.mState
[eLogicalAxisInline
];
3839 if (aJustifySelf
== StyleAlignFlags::LAST_BASELINE
&&
3840 !GridItemInfo::BaselineAlignmentAffectsEndSide(state
)) {
3841 aJustifySelf
= StyleAlignFlags::BASELINE
;
3842 } else if (aJustifySelf
== StyleAlignFlags::BASELINE
&&
3843 GridItemInfo::BaselineAlignmentAffectsEndSide(state
)) {
3844 aJustifySelf
= StyleAlignFlags::LAST_BASELINE
;
3848 bool isOrthogonal
= aCBWM
.IsOrthogonalTo(childWM
);
3849 LogicalAxis axis
= isOrthogonal
? eLogicalAxisBlock
: eLogicalAxisInline
;
3850 AlignJustifySelf(aJustifySelf
, axis
, flags
, baselineAdjust
, aCBSize
, aRI
,
3854 static StyleAlignFlags
GetAlignJustifyValue(StyleAlignFlags aAlignment
,
3855 const WritingMode aWM
,
3856 const bool aIsAlign
,
3857 bool* aOverflowSafe
) {
3858 *aOverflowSafe
= bool(aAlignment
& StyleAlignFlags::SAFE
);
3859 aAlignment
&= ~StyleAlignFlags::FLAG_BITS
;
3861 // Map some alignment values to 'start' / 'end'.
3862 if (aAlignment
== StyleAlignFlags::LEFT
||
3863 aAlignment
== StyleAlignFlags::RIGHT
) {
3865 // Grid's 'align-content' axis is never parallel to the inline axis.
3866 return StyleAlignFlags::START
;
3868 bool isStart
= aWM
.IsBidiLTR() == (aAlignment
== StyleAlignFlags::LEFT
);
3869 return isStart
? StyleAlignFlags::START
: StyleAlignFlags::END
;
3871 if (aAlignment
== StyleAlignFlags::FLEX_START
) {
3872 return StyleAlignFlags::START
; // same as 'start' for Grid
3874 if (aAlignment
== StyleAlignFlags::FLEX_END
) {
3875 return StyleAlignFlags::END
; // same as 'end' for Grid
3880 static Maybe
<StyleAlignFlags
> GetAlignJustifyFallbackIfAny(
3881 const StyleContentDistribution
& aDistribution
, const WritingMode aWM
,
3882 const bool aIsAlign
, bool* aOverflowSafe
) {
3883 // TODO: Eventually this should look at aDistribution's fallback alignment,
3884 // see https://github.com/w3c/csswg-drafts/issues/1002.
3885 if (aDistribution
.primary
== StyleAlignFlags::STRETCH
||
3886 aDistribution
.primary
== StyleAlignFlags::SPACE_BETWEEN
) {
3887 return Some(StyleAlignFlags::START
);
3889 if (aDistribution
.primary
== StyleAlignFlags::SPACE_AROUND
||
3890 aDistribution
.primary
== StyleAlignFlags::SPACE_EVENLY
) {
3891 return Some(StyleAlignFlags::CENTER
);
3896 //----------------------------------------------------------------------
3898 // Frame class boilerplate
3899 // =======================
3901 NS_QUERYFRAME_HEAD(nsGridContainerFrame
)
3902 NS_QUERYFRAME_ENTRY(nsGridContainerFrame
)
3903 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
3905 NS_IMPL_FRAMEARENA_HELPERS(nsGridContainerFrame
)
3907 nsContainerFrame
* NS_NewGridContainerFrame(PresShell
* aPresShell
,
3908 ComputedStyle
* aStyle
) {
3909 return new (aPresShell
)
3910 nsGridContainerFrame(aStyle
, aPresShell
->GetPresContext());
3913 //----------------------------------------------------------------------
3915 // nsGridContainerFrame Method Implementations
3916 // ===========================================
3918 /*static*/ const nsRect
& nsGridContainerFrame::GridItemCB(nsIFrame
* aChild
) {
3919 MOZ_ASSERT(aChild
->IsAbsolutelyPositioned());
3920 nsRect
* cb
= aChild
->GetProperty(GridItemContainingBlockRect());
3922 "this method must only be called on grid items, and the grid "
3923 "container should've reflowed this item by now and set up cb");
3927 void nsGridContainerFrame::AddImplicitNamedAreas(
3928 Span
<LineNameList
> aLineNameLists
) {
3929 // http://dev.w3.org/csswg/css-grid/#implicit-named-areas
3930 // Note: recording these names for fast lookup later is just an optimization.
3931 const uint32_t len
= std::min(aLineNameLists
.Length(), size_t(kMaxLine
));
3932 nsTHashSet
<nsString
> currentStarts
;
3933 ImplicitNamedAreas
* areas
= GetImplicitNamedAreas();
3934 for (uint32_t i
= 0; i
< len
; ++i
) {
3935 for (const auto& nameIdent
: aLineNameLists
[i
].AsSpan()) {
3936 nsAtom
* name
= nameIdent
.AsAtom();
3937 uint32_t indexOfSuffix
;
3938 if (Grid::IsNameWithStartSuffix(name
, &indexOfSuffix
) ||
3939 Grid::IsNameWithEndSuffix(name
, &indexOfSuffix
)) {
3940 // Extract the name that was found earlier.
3941 nsDependentSubstring
areaName(nsDependentAtomString(name
), 0,
3944 // Lazily create the ImplicitNamedAreas.
3946 areas
= new ImplicitNamedAreas
;
3947 SetProperty(ImplicitNamedAreasProperty(), areas
);
3950 RefPtr
<nsAtom
> name
= NS_Atomize(areaName
);
3951 auto addPtr
= areas
->lookupForAdd(name
);
3955 NamedArea
{StyleAtom(do_AddRef(name
)), {0, 0}, {0, 0}})) {
3956 MOZ_CRASH("OOM while adding grid name lists");
3964 void nsGridContainerFrame::InitImplicitNamedAreas(
3965 const nsStylePosition
* aStyle
) {
3966 ImplicitNamedAreas
* areas
= GetImplicitNamedAreas();
3968 // Clear it, but reuse the hashtable itself for now. We'll remove it
3969 // below if it isn't needed anymore.
3972 auto Add
= [&](const GridTemplate
& aTemplate
, bool aIsSubgrid
) {
3973 AddImplicitNamedAreas(aTemplate
.LineNameLists(aIsSubgrid
));
3974 for (auto& value
: aTemplate
.TrackListValues()) {
3975 if (value
.IsTrackRepeat()) {
3976 AddImplicitNamedAreas(value
.AsTrackRepeat().line_names
.AsSpan());
3980 Add(aStyle
->mGridTemplateColumns
, IsSubgrid(eLogicalAxisInline
));
3981 Add(aStyle
->mGridTemplateRows
, IsSubgrid(eLogicalAxisBlock
));
3982 if (areas
&& areas
->count() == 0) {
3983 RemoveProperty(ImplicitNamedAreasProperty());
3987 int32_t nsGridContainerFrame::Grid::ResolveLine(
3988 const StyleGridLine
& aLine
, int32_t aNth
, uint32_t aFromIndex
,
3989 const LineNameMap
& aNameMap
, LogicalSide aSide
, uint32_t aExplicitGridEnd
,
3990 const nsStylePosition
* aStyle
) {
3991 MOZ_ASSERT(!aLine
.IsAuto());
3993 if (aLine
.LineName()->IsEmpty()) {
3994 MOZ_ASSERT(aNth
!= 0, "css-grid 9.2: <integer> must not be zero.");
3995 line
= int32_t(aFromIndex
) + aNth
;
3998 // <integer> was omitted; treat it as 1.
4001 bool isNameOnly
= !aLine
.is_span
&& aLine
.line_num
== 0;
4003 AutoTArray
<uint32_t, 16> implicitLines
;
4004 aNameMap
.FindNamedAreas(aLine
.ident
.AsAtom(), aSide
, implicitLines
);
4005 if (!implicitLines
.IsEmpty() ||
4006 aNameMap
.HasImplicitNamedArea(aLine
.LineName())) {
4007 // aName is a named area - look for explicit lines named
4008 // <name>-start/-end depending on which side we're resolving.
4009 // http://dev.w3.org/csswg/css-grid/#grid-placement-slot
4010 nsAutoString
lineName(nsDependentAtomString(aLine
.LineName()));
4011 if (IsStart(aSide
)) {
4012 lineName
.AppendLiteral("-start");
4014 lineName
.AppendLiteral("-end");
4016 RefPtr
<nsAtom
> name
= NS_Atomize(lineName
);
4017 line
= aNameMap
.FindNamedLine(name
, &aNth
, aFromIndex
, implicitLines
);
4022 // If LineName() ends in -start/-end, try the prefix as a named area.
4023 AutoTArray
<uint32_t, 16> implicitLines
;
4025 bool useStart
= IsNameWithStartSuffix(aLine
.LineName(), &index
);
4026 if (useStart
|| IsNameWithEndSuffix(aLine
.LineName(), &index
)) {
4027 auto side
= MakeLogicalSide(
4028 GetAxis(aSide
), useStart
? eLogicalEdgeStart
: eLogicalEdgeEnd
);
4029 RefPtr
<nsAtom
> name
= NS_Atomize(nsDependentSubstring(
4030 nsDependentAtomString(aLine
.LineName()), 0, index
));
4031 aNameMap
.FindNamedAreas(name
, side
, implicitLines
);
4033 line
= aNameMap
.FindNamedLine(aLine
.LineName(), &aNth
, aFromIndex
,
4038 MOZ_ASSERT(aNth
!= 0, "we found all N named lines but 'line' is zero!");
4040 if (aLine
.is_span
) {
4041 // http://dev.w3.org/csswg/css-grid/#grid-placement-span-int
4042 // 'span <custom-ident> N'
4043 edgeLine
= IsStart(aSide
) ? 1 : aExplicitGridEnd
;
4045 // http://dev.w3.org/csswg/css-grid/#grid-placement-int
4046 // '<custom-ident> N'
4047 edgeLine
= aNth
< 0 ? 1 : aExplicitGridEnd
;
4049 // "If not enough lines with that name exist, all lines in the implicit
4050 // grid are assumed to have that name..."
4051 line
= edgeLine
+ aNth
;
4054 return clamped(line
, aNameMap
.mClampMinLine
, aNameMap
.mClampMaxLine
);
4057 nsGridContainerFrame::Grid::LinePair
4058 nsGridContainerFrame::Grid::ResolveLineRangeHelper(
4059 const StyleGridLine
& aStart
, const StyleGridLine
& aEnd
,
4060 const LineNameMap
& aNameMap
, LogicalAxis aAxis
, uint32_t aExplicitGridEnd
,
4061 const nsStylePosition
* aStyle
) {
4062 MOZ_ASSERT(int32_t(kAutoLine
) > kMaxLine
);
4064 if (aStart
.is_span
) {
4065 if (aEnd
.is_span
|| aEnd
.IsAuto()) {
4066 // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
4067 if (aStart
.LineName()->IsEmpty()) {
4068 // span <integer> / span *
4069 // span <integer> / auto
4070 return LinePair(kAutoLine
, aStart
.line_num
);
4072 // span <custom-ident> / span *
4073 // span <custom-ident> / auto
4074 return LinePair(kAutoLine
, 1); // XXX subgrid explicit size instead of 1?
4077 uint32_t from
= aEnd
.line_num
< 0 ? aExplicitGridEnd
+ 1 : 0;
4078 auto end
= ResolveLine(aEnd
, aEnd
.line_num
, from
, aNameMap
,
4079 MakeLogicalSide(aAxis
, eLogicalEdgeEnd
),
4080 aExplicitGridEnd
, aStyle
);
4081 int32_t span
= aStart
.line_num
== 0 ? 1 : aStart
.line_num
;
4083 // The end is at or before the first explicit line, thus all lines before
4084 // it match <custom-ident> since they're implicit.
4085 int32_t start
= std::max(end
- span
, aNameMap
.mClampMinLine
);
4086 return LinePair(start
, end
);
4088 auto start
= ResolveLine(aStart
, -span
, end
, aNameMap
,
4089 MakeLogicalSide(aAxis
, eLogicalEdgeStart
),
4090 aExplicitGridEnd
, aStyle
);
4091 return LinePair(start
, end
);
4094 int32_t start
= kAutoLine
;
4095 if (aStart
.IsAuto()) {
4096 if (aEnd
.IsAuto()) {
4098 return LinePair(start
, 1); // XXX subgrid explicit size instead of 1?
4101 if (aEnd
.LineName()->IsEmpty()) {
4102 // auto / span <integer>
4103 MOZ_ASSERT(aEnd
.line_num
!= 0);
4104 return LinePair(start
, aEnd
.line_num
);
4106 // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
4107 // auto / span <custom-ident>
4108 return LinePair(start
, 1); // XXX subgrid explicit size instead of 1?
4111 uint32_t from
= aStart
.line_num
< 0 ? aExplicitGridEnd
+ 1 : 0;
4112 start
= ResolveLine(aStart
, aStart
.line_num
, from
, aNameMap
,
4113 MakeLogicalSide(aAxis
, eLogicalEdgeStart
),
4114 aExplicitGridEnd
, aStyle
);
4115 if (aEnd
.IsAuto()) {
4116 // A "definite line / auto" should resolve the auto to 'span 1'.
4117 // The error handling in ResolveLineRange will make that happen and also
4118 // clamp the end line correctly if we return "start / start".
4119 return LinePair(start
, start
);
4124 int32_t nth
= aEnd
.line_num
== 0 ? 1 : aEnd
.line_num
;
4126 if (MOZ_UNLIKELY(start
< 0)) {
4127 if (aEnd
.LineName()->IsEmpty()) {
4128 return LinePair(start
, start
+ nth
);
4132 if (start
>= int32_t(aExplicitGridEnd
)) {
4133 // The start is at or after the last explicit line, thus all lines
4134 // after it match <custom-ident> since they're implicit.
4135 return LinePair(start
, std::min(start
+ nth
, aNameMap
.mClampMaxLine
));
4140 from
= aEnd
.line_num
< 0 ? aExplicitGridEnd
+ 1 : 0;
4142 auto end
= ResolveLine(aEnd
, nth
, from
, aNameMap
,
4143 MakeLogicalSide(aAxis
, eLogicalEdgeEnd
),
4144 aExplicitGridEnd
, aStyle
);
4145 if (start
== int32_t(kAutoLine
)) {
4146 // auto / definite line
4147 start
= std::max(aNameMap
.mClampMinLine
, end
- 1);
4149 return LinePair(start
, end
);
4152 nsGridContainerFrame::LineRange
nsGridContainerFrame::Grid::ResolveLineRange(
4153 const StyleGridLine
& aStart
, const StyleGridLine
& aEnd
,
4154 const LineNameMap
& aNameMap
, LogicalAxis aAxis
, uint32_t aExplicitGridEnd
,
4155 const nsStylePosition
* aStyle
) {
4156 LinePair r
= ResolveLineRangeHelper(aStart
, aEnd
, aNameMap
, aAxis
,
4157 aExplicitGridEnd
, aStyle
);
4158 MOZ_ASSERT(r
.second
!= int32_t(kAutoLine
));
4160 if (r
.first
== int32_t(kAutoLine
)) {
4161 // r.second is a span, clamp it to aNameMap.mClampMaxLine - 1 so that
4162 // the returned range has a HypotheticalEnd <= aNameMap.mClampMaxLine.
4163 // http://dev.w3.org/csswg/css-grid/#overlarge-grids
4164 r
.second
= std::min(r
.second
, aNameMap
.mClampMaxLine
- 1);
4166 // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
4167 if (r
.first
> r
.second
) {
4168 std::swap(r
.first
, r
.second
);
4169 } else if (r
.first
== r
.second
) {
4170 if (MOZ_UNLIKELY(r
.first
== aNameMap
.mClampMaxLine
)) {
4171 r
.first
= aNameMap
.mClampMaxLine
- 1;
4173 r
.second
= r
.first
+ 1; // XXX subgrid explicit size instead of 1?
4176 return LineRange(r
.first
, r
.second
);
4179 nsGridContainerFrame::GridArea
nsGridContainerFrame::Grid::PlaceDefinite(
4180 nsIFrame
* aChild
, const LineNameMap
& aColLineNameMap
,
4181 const LineNameMap
& aRowLineNameMap
, const nsStylePosition
* aStyle
) {
4182 const nsStylePosition
* itemStyle
= aChild
->StylePosition();
4184 ResolveLineRange(itemStyle
->mGridColumnStart
, itemStyle
->mGridColumnEnd
,
4185 aColLineNameMap
, eLogicalAxisInline
, mExplicitGridColEnd
,
4187 ResolveLineRange(itemStyle
->mGridRowStart
, itemStyle
->mGridRowEnd
,
4188 aRowLineNameMap
, eLogicalAxisBlock
, mExplicitGridRowEnd
,
4192 nsGridContainerFrame::LineRange
4193 nsGridContainerFrame::Grid::ResolveAbsPosLineRange(
4194 const StyleGridLine
& aStart
, const StyleGridLine
& aEnd
,
4195 const LineNameMap
& aNameMap
, LogicalAxis aAxis
, uint32_t aExplicitGridEnd
,
4196 int32_t aGridStart
, int32_t aGridEnd
, const nsStylePosition
* aStyle
) {
4197 if (aStart
.IsAuto()) {
4198 if (aEnd
.IsAuto()) {
4199 return LineRange(kAutoLine
, kAutoLine
);
4201 uint32_t from
= aEnd
.line_num
< 0 ? aExplicitGridEnd
+ 1 : 0;
4202 int32_t end
= ResolveLine(aEnd
, aEnd
.line_num
, from
, aNameMap
,
4203 MakeLogicalSide(aAxis
, eLogicalEdgeEnd
),
4204 aExplicitGridEnd
, aStyle
);
4208 // A line outside the existing grid is treated as 'auto' for abs.pos (10.1).
4209 end
= AutoIfOutside(end
, aGridStart
, aGridEnd
);
4210 return LineRange(kAutoLine
, end
);
4213 if (aEnd
.IsAuto()) {
4214 uint32_t from
= aStart
.line_num
< 0 ? aExplicitGridEnd
+ 1 : 0;
4215 int32_t start
= ResolveLine(aStart
, aStart
.line_num
, from
, aNameMap
,
4216 MakeLogicalSide(aAxis
, eLogicalEdgeStart
),
4217 aExplicitGridEnd
, aStyle
);
4218 if (aStart
.is_span
) {
4219 start
= std::max(aGridEnd
- start
, aGridStart
);
4221 start
= AutoIfOutside(start
, aGridStart
, aGridEnd
);
4222 return LineRange(start
, kAutoLine
);
4226 ResolveLineRange(aStart
, aEnd
, aNameMap
, aAxis
, aExplicitGridEnd
, aStyle
);
4228 MOZ_ASSERT(aStart
.is_span
&& aEnd
.is_span
,
4229 "span / span is the only case "
4230 "leading to IsAuto here -- we dealt with the other cases above");
4231 // The second span was ignored per 9.2.1. For abs.pos., 10.1 says that this
4232 // case should result in "auto / auto" unlike normal flow grid items.
4233 return LineRange(kAutoLine
, kAutoLine
);
4236 return LineRange(AutoIfOutside(r
.mUntranslatedStart
, aGridStart
, aGridEnd
),
4237 AutoIfOutside(r
.mUntranslatedEnd
, aGridStart
, aGridEnd
));
4240 nsGridContainerFrame::GridArea
nsGridContainerFrame::Grid::PlaceAbsPos(
4241 nsIFrame
* aChild
, const LineNameMap
& aColLineNameMap
,
4242 const LineNameMap
& aRowLineNameMap
, const nsStylePosition
* aStyle
) {
4243 const nsStylePosition
* itemStyle
= aChild
->StylePosition();
4244 int32_t gridColStart
= 1 - mExplicitGridOffsetCol
;
4245 int32_t gridRowStart
= 1 - mExplicitGridOffsetRow
;
4246 return GridArea(ResolveAbsPosLineRange(
4247 itemStyle
->mGridColumnStart
, itemStyle
->mGridColumnEnd
,
4248 aColLineNameMap
, eLogicalAxisInline
, mExplicitGridColEnd
,
4249 gridColStart
, mGridColEnd
, aStyle
),
4250 ResolveAbsPosLineRange(
4251 itemStyle
->mGridRowStart
, itemStyle
->mGridRowEnd
,
4252 aRowLineNameMap
, eLogicalAxisBlock
, mExplicitGridRowEnd
,
4253 gridRowStart
, mGridRowEnd
, aStyle
));
4256 uint32_t nsGridContainerFrame::Grid::FindAutoCol(uint32_t aStartCol
,
4257 uint32_t aLockedRow
,
4258 const GridArea
* aArea
) const {
4259 const uint32_t extent
= aArea
->mCols
.Extent();
4260 const uint32_t iStart
= aLockedRow
;
4261 const uint32_t iEnd
= iStart
+ aArea
->mRows
.Extent();
4262 uint32_t candidate
= aStartCol
;
4263 for (uint32_t i
= iStart
; i
< iEnd
;) {
4264 if (i
>= mCellMap
.mCells
.Length()) {
4267 const nsTArray
<CellMap::Cell
>& cellsInRow
= mCellMap
.mCells
[i
];
4268 const uint32_t len
= cellsInRow
.Length();
4269 const uint32_t lastCandidate
= candidate
;
4270 // Find the first gap in the current row that's at least 'extent' wide.
4271 // ('gap' tracks how wide the current column gap is.)
4272 for (uint32_t j
= candidate
, gap
= 0; j
< len
&& gap
< extent
; ++j
) {
4273 if (!cellsInRow
[j
].mIsOccupied
) {
4280 if (lastCandidate
< candidate
&& i
!= iStart
) {
4281 // Couldn't fit 'extent' tracks at 'lastCandidate' here so we must
4282 // restart from the beginning with the new 'candidate'.
4291 void nsGridContainerFrame::Grid::PlaceAutoCol(uint32_t aStartCol
,
4293 uint32_t aClampMaxColLine
) const {
4294 MOZ_ASSERT(aArea
->mRows
.IsDefinite() && aArea
->mCols
.IsAuto());
4295 uint32_t col
= FindAutoCol(aStartCol
, aArea
->mRows
.mStart
, aArea
);
4296 aArea
->mCols
.ResolveAutoPosition(col
, aClampMaxColLine
);
4297 MOZ_ASSERT(aArea
->IsDefinite());
4300 uint32_t nsGridContainerFrame::Grid::FindAutoRow(uint32_t aLockedCol
,
4302 const GridArea
* aArea
) const {
4303 const uint32_t extent
= aArea
->mRows
.Extent();
4304 const uint32_t jStart
= aLockedCol
;
4305 const uint32_t jEnd
= jStart
+ aArea
->mCols
.Extent();
4306 const uint32_t iEnd
= mCellMap
.mCells
.Length();
4307 uint32_t candidate
= aStartRow
;
4308 // Find the first gap in the rows that's at least 'extent' tall.
4309 // ('gap' tracks how tall the current row gap is.)
4310 for (uint32_t i
= candidate
, gap
= 0; i
< iEnd
&& gap
< extent
; ++i
) {
4311 ++gap
; // tentative, but we may reset it below if a column is occupied
4312 const nsTArray
<CellMap::Cell
>& cellsInRow
= mCellMap
.mCells
[i
];
4313 const uint32_t clampedJEnd
= std::min
<uint32_t>(jEnd
, cellsInRow
.Length());
4314 // Check if the current row is unoccupied from jStart to jEnd.
4315 for (uint32_t j
= jStart
; j
< clampedJEnd
; ++j
) {
4316 if (cellsInRow
[j
].mIsOccupied
) {
4317 // Couldn't fit 'extent' rows at 'candidate' here; we hit something
4318 // at row 'i'. So, try the row after 'i' as our next candidate.
4328 void nsGridContainerFrame::Grid::PlaceAutoRow(uint32_t aStartRow
,
4330 uint32_t aClampMaxRowLine
) const {
4331 MOZ_ASSERT(aArea
->mCols
.IsDefinite() && aArea
->mRows
.IsAuto());
4332 uint32_t row
= FindAutoRow(aArea
->mCols
.mStart
, aStartRow
, aArea
);
4333 aArea
->mRows
.ResolveAutoPosition(row
, aClampMaxRowLine
);
4334 MOZ_ASSERT(aArea
->IsDefinite());
4337 void nsGridContainerFrame::Grid::PlaceAutoAutoInRowOrder(
4338 uint32_t aStartCol
, uint32_t aStartRow
, GridArea
* aArea
,
4339 uint32_t aClampMaxColLine
, uint32_t aClampMaxRowLine
) const {
4340 MOZ_ASSERT(aArea
->mCols
.IsAuto() && aArea
->mRows
.IsAuto());
4341 const uint32_t colExtent
= aArea
->mCols
.Extent();
4342 const uint32_t gridRowEnd
= mGridRowEnd
;
4343 const uint32_t gridColEnd
= mGridColEnd
;
4344 uint32_t col
= aStartCol
;
4345 uint32_t row
= aStartRow
;
4346 for (; row
< gridRowEnd
; ++row
) {
4347 col
= FindAutoCol(col
, row
, aArea
);
4348 if (col
+ colExtent
<= gridColEnd
) {
4353 MOZ_ASSERT(row
< gridRowEnd
|| col
== 0,
4354 "expected column 0 for placing in a new row");
4355 aArea
->mCols
.ResolveAutoPosition(col
, aClampMaxColLine
);
4356 aArea
->mRows
.ResolveAutoPosition(row
, aClampMaxRowLine
);
4357 MOZ_ASSERT(aArea
->IsDefinite());
4360 void nsGridContainerFrame::Grid::PlaceAutoAutoInColOrder(
4361 uint32_t aStartCol
, uint32_t aStartRow
, GridArea
* aArea
,
4362 uint32_t aClampMaxColLine
, uint32_t aClampMaxRowLine
) const {
4363 MOZ_ASSERT(aArea
->mCols
.IsAuto() && aArea
->mRows
.IsAuto());
4364 const uint32_t rowExtent
= aArea
->mRows
.Extent();
4365 const uint32_t gridRowEnd
= mGridRowEnd
;
4366 const uint32_t gridColEnd
= mGridColEnd
;
4367 uint32_t col
= aStartCol
;
4368 uint32_t row
= aStartRow
;
4369 for (; col
< gridColEnd
; ++col
) {
4370 row
= FindAutoRow(col
, row
, aArea
);
4371 if (row
+ rowExtent
<= gridRowEnd
) {
4376 MOZ_ASSERT(col
< gridColEnd
|| row
== 0,
4377 "expected row 0 for placing in a new column");
4378 aArea
->mCols
.ResolveAutoPosition(col
, aClampMaxColLine
);
4379 aArea
->mRows
.ResolveAutoPosition(row
, aClampMaxRowLine
);
4380 MOZ_ASSERT(aArea
->IsDefinite());
4383 template <typename IsEmptyFuncT
>
4384 Maybe
<nsTArray
<uint32_t>>
4385 nsGridContainerFrame::Grid::CalculateAdjustForAutoFitElements(
4386 uint32_t* const aOutNumEmptyLines
, TrackSizingFunctions
& aSizingFunctions
,
4387 uint32_t aNumGridLines
, IsEmptyFuncT aIsEmptyFunc
) {
4388 Maybe
<nsTArray
<uint32_t>> trackAdjust
;
4389 uint32_t& numEmptyLines
= *aOutNumEmptyLines
;
4391 if (aSizingFunctions
.NumRepeatTracks() > 0) {
4392 MOZ_ASSERT(aSizingFunctions
.mHasRepeatAuto
);
4393 // Since this loop is concerned with just the repeat tracks, we
4394 // iterate from 0..NumRepeatTracks() which is the natural range of
4395 // mRemoveRepeatTracks. This means we have to add
4396 // (mExplicitGridOffset + mRepeatAutoStart) to get a zero-based
4397 // index for arrays like mCellMap/aIsEmptyFunc and trackAdjust. We'll then
4398 // fill out the trackAdjust array for all the remaining lines.
4399 const uint32_t repeatStart
= (aSizingFunctions
.mExplicitGridOffset
+
4400 aSizingFunctions
.mRepeatAutoStart
);
4401 const uint32_t numRepeats
= aSizingFunctions
.NumRepeatTracks();
4402 for (uint32_t i
= 0; i
< numRepeats
; ++i
) {
4403 if (numEmptyLines
) {
4404 MOZ_ASSERT(trackAdjust
.isSome());
4405 (*trackAdjust
)[repeatStart
+ i
] = numEmptyLines
;
4407 if (aIsEmptyFunc(repeatStart
+ i
)) {
4409 if (trackAdjust
.isNothing()) {
4410 trackAdjust
.emplace(aNumGridLines
);
4411 trackAdjust
->SetLength(aNumGridLines
);
4412 PodZero(trackAdjust
->Elements(), trackAdjust
->Length());
4415 aSizingFunctions
.mRemovedRepeatTracks
[i
] = true;
4418 // Fill out the trackAdjust array for all the tracks after the repeats.
4419 if (numEmptyLines
) {
4420 for (uint32_t line
= repeatStart
+ numRepeats
; line
< aNumGridLines
;
4422 (*trackAdjust
)[line
] = numEmptyLines
;
4430 void nsGridContainerFrame::Grid::SubgridPlaceGridItems(
4431 GridReflowInput
& aParentState
, Grid
* aParentGrid
,
4432 const GridItemInfo
& aGridItem
) {
4433 MOZ_ASSERT(aGridItem
.mArea
.IsDefinite() ||
4434 aGridItem
.mFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
),
4435 "the subgrid's lines should be resolved by now");
4436 if (aGridItem
.IsSubgrid(eLogicalAxisInline
)) {
4437 aParentState
.mFrame
->AddStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM
);
4439 if (aGridItem
.IsSubgrid(eLogicalAxisBlock
)) {
4440 aParentState
.mFrame
->AddStateBits(NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM
);
4442 auto* childGrid
= aGridItem
.SubgridFrame();
4443 const auto* pos
= childGrid
->StylePosition();
4444 childGrid
->NormalizeChildLists();
4445 GridReflowInput
state(childGrid
, aParentState
.mRenderingContext
);
4446 childGrid
->InitImplicitNamedAreas(pos
);
4448 const bool isOrthogonal
= aParentState
.mWM
.IsOrthogonalTo(state
.mWM
);
4449 // Record the subgrid's GridArea in a frame property.
4450 auto* subgrid
= childGrid
->GetProperty(Subgrid::Prop());
4452 subgrid
= new Subgrid(aGridItem
.mArea
, isOrthogonal
, aParentState
.mWM
);
4453 childGrid
->SetProperty(Subgrid::Prop(), subgrid
);
4455 subgrid
->mArea
= aGridItem
.mArea
;
4456 subgrid
->mIsOrthogonal
= isOrthogonal
;
4457 subgrid
->mGridItems
.Clear();
4458 subgrid
->mAbsPosItems
.Clear();
4461 // Abs.pos. subgrids may have kAutoLine in their area. Map those to the edge
4462 // line in the parent's grid (zero-based line numbers).
4463 if (MOZ_UNLIKELY(subgrid
->mArea
.mCols
.mStart
== kAutoLine
)) {
4464 subgrid
->mArea
.mCols
.mStart
= 0;
4466 if (MOZ_UNLIKELY(subgrid
->mArea
.mCols
.mEnd
== kAutoLine
)) {
4467 subgrid
->mArea
.mCols
.mEnd
= aParentGrid
->mGridColEnd
- 1;
4469 if (MOZ_UNLIKELY(subgrid
->mArea
.mRows
.mStart
== kAutoLine
)) {
4470 subgrid
->mArea
.mRows
.mStart
= 0;
4472 if (MOZ_UNLIKELY(subgrid
->mArea
.mRows
.mEnd
== kAutoLine
)) {
4473 subgrid
->mArea
.mRows
.mEnd
= aParentGrid
->mGridRowEnd
- 1;
4476 MOZ_ASSERT((subgrid
->mArea
.mCols
.Extent() > 0 &&
4477 subgrid
->mArea
.mRows
.Extent() > 0) ||
4478 state
.mGridItems
.IsEmpty(),
4479 "subgrid needs at least one track for its items");
4481 // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
4482 // https://drafts.csswg.org/css-grid/#auto-repeat
4483 // They're only used for auto-repeat in a non-subgridded axis so we skip
4484 // computing them otherwise.
4485 RepeatTrackSizingInput
repeatSizing(state
.mWM
);
4486 if (!childGrid
->IsColSubgrid() && state
.mColFunctions
.mHasRepeatAuto
) {
4487 repeatSizing
.InitFromStyle(eLogicalAxisInline
, state
.mWM
,
4488 state
.mFrame
->Style());
4490 if (!childGrid
->IsRowSubgrid() && state
.mRowFunctions
.mHasRepeatAuto
) {
4491 repeatSizing
.InitFromStyle(eLogicalAxisBlock
, state
.mWM
,
4492 state
.mFrame
->Style());
4495 PlaceGridItems(state
, repeatSizing
);
4497 subgrid
->mGridItems
= std::move(state
.mGridItems
);
4498 subgrid
->mAbsPosItems
= std::move(state
.mAbsPosItems
);
4499 subgrid
->mGridColEnd
= mGridColEnd
;
4500 subgrid
->mGridRowEnd
= mGridRowEnd
;
4503 void nsGridContainerFrame::Grid::PlaceGridItems(
4504 GridReflowInput
& aState
, const RepeatTrackSizingInput
& aSizes
) {
4505 MOZ_ASSERT(mCellMap
.mCells
.IsEmpty(), "unexpected entries in cell map");
4507 mAreas
= aState
.mFrame
->GetImplicitNamedAreas();
4509 if (aState
.mFrame
->HasSubgridItems() || aState
.mFrame
->IsSubgrid()) {
4510 if (auto* uts
= aState
.mFrame
->GetUsedTrackSizes()) {
4511 uts
->mCanResolveLineRangeSize
= {false, false};
4512 uts
->mSizes
[eLogicalAxisInline
].ClearAndRetainStorage();
4513 uts
->mSizes
[eLogicalAxisBlock
].ClearAndRetainStorage();
4517 // SubgridPlaceGridItems will set these if we find any subgrid items.
4518 aState
.mFrame
->RemoveStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM
|
4519 NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM
);
4521 // http://dev.w3.org/csswg/css-grid/#grid-definition
4522 // Initialize the end lines of the Explicit Grid (mExplicitGridCol[Row]End).
4523 // This is determined by the larger of the number of rows/columns defined
4524 // by 'grid-template-areas' and the 'grid-template-rows'/'-columns', plus one.
4525 // Also initialize the Implicit Grid (mGridCol[Row]End) to the same values.
4526 // Note that this is for a grid with a 1,1 origin. We'll change that
4527 // to a 0,0 based grid after placing definite lines.
4528 const nsStylePosition
* const gridStyle
= aState
.mGridStyle
;
4529 const auto* areas
= gridStyle
->mGridTemplateAreas
.IsNone()
4531 : &*gridStyle
->mGridTemplateAreas
.AsAreas();
4532 const LineNameMap
* parentLineNameMap
= nullptr;
4533 const LineRange
* subgridRange
= nullptr;
4534 bool subgridAxisIsSameDirection
= true;
4535 if (!aState
.mFrame
->IsColSubgrid()) {
4536 aState
.mColFunctions
.InitRepeatTracks(
4537 gridStyle
->mColumnGap
, aSizes
.mMin
.ISize(aState
.mWM
),
4538 aSizes
.mSize
.ISize(aState
.mWM
), aSizes
.mMax
.ISize(aState
.mWM
));
4539 uint32_t areaCols
= areas
? areas
->width
+ 1 : 1;
4540 mExplicitGridColEnd
= aState
.mColFunctions
.ComputeExplicitGridEnd(areaCols
);
4542 const auto* subgrid
= aState
.mFrame
->GetProperty(Subgrid::Prop());
4543 subgridRange
= &subgrid
->SubgridCols();
4544 uint32_t extent
= subgridRange
->Extent();
4545 mExplicitGridColEnd
= extent
+ 1; // the grid is 1-based at this point
4547 ParentLineMapForAxis(subgrid
->mIsOrthogonal
, eLogicalAxisInline
);
4549 aState
.mFrame
->ParentGridContainerForSubgrid()->GetWritingMode();
4550 subgridAxisIsSameDirection
=
4551 aState
.mWM
.ParallelAxisStartsOnSameSide(eLogicalAxisInline
, parentWM
);
4553 mGridColEnd
= mExplicitGridColEnd
;
4554 LineNameMap
colLineNameMap(gridStyle
, mAreas
, aState
.mColFunctions
,
4555 parentLineNameMap
, subgridRange
,
4556 subgridAxisIsSameDirection
);
4558 if (!aState
.mFrame
->IsRowSubgrid()) {
4559 const Maybe
<nscoord
> containBSize
= aState
.mFrame
->ContainIntrinsicBSize();
4560 const nscoord repeatTrackSizingBSize
= [&] {
4561 // This clamping only applies to auto sizes.
4563 aSizes
.mSize
.BSize(aState
.mWM
) == NS_UNCONSTRAINEDSIZE
) {
4564 return NS_CSS_MINMAX(*containBSize
, aSizes
.mMin
.BSize(aState
.mWM
),
4565 aSizes
.mMax
.BSize(aState
.mWM
));
4567 return aSizes
.mSize
.BSize(aState
.mWM
);
4569 aState
.mRowFunctions
.InitRepeatTracks(
4570 gridStyle
->mRowGap
, aSizes
.mMin
.BSize(aState
.mWM
),
4571 repeatTrackSizingBSize
, aSizes
.mMax
.BSize(aState
.mWM
));
4572 uint32_t areaRows
= areas
? areas
->strings
.Length() + 1 : 1;
4573 mExplicitGridRowEnd
= aState
.mRowFunctions
.ComputeExplicitGridEnd(areaRows
);
4574 parentLineNameMap
= nullptr;
4575 subgridRange
= nullptr;
4577 const auto* subgrid
= aState
.mFrame
->GetProperty(Subgrid::Prop());
4578 subgridRange
= &subgrid
->SubgridRows();
4579 uint32_t extent
= subgridRange
->Extent();
4580 mExplicitGridRowEnd
= extent
+ 1; // the grid is 1-based at this point
4582 ParentLineMapForAxis(subgrid
->mIsOrthogonal
, eLogicalAxisBlock
);
4584 aState
.mFrame
->ParentGridContainerForSubgrid()->GetWritingMode();
4585 subgridAxisIsSameDirection
=
4586 aState
.mWM
.ParallelAxisStartsOnSameSide(eLogicalAxisBlock
, parentWM
);
4588 mGridRowEnd
= mExplicitGridRowEnd
;
4589 LineNameMap
rowLineNameMap(gridStyle
, mAreas
, aState
.mRowFunctions
,
4590 parentLineNameMap
, subgridRange
,
4591 subgridAxisIsSameDirection
);
4593 const bool isSubgridOrItemInSubgrid
=
4594 aState
.mFrame
->IsSubgrid() || !!mParentGrid
;
4595 auto SetSubgridChildEdgeBits
=
4596 [this, isSubgridOrItemInSubgrid
](GridItemInfo
& aItem
) -> void {
4597 if (isSubgridOrItemInSubgrid
) {
4598 const auto& area
= aItem
.mArea
;
4599 if (area
.mCols
.mStart
== 0) {
4600 aItem
.mState
[eLogicalAxisInline
] |= ItemState::eStartEdge
;
4602 if (area
.mCols
.mEnd
== mGridColEnd
) {
4603 aItem
.mState
[eLogicalAxisInline
] |= ItemState::eEndEdge
;
4605 if (area
.mRows
.mStart
== 0) {
4606 aItem
.mState
[eLogicalAxisBlock
] |= ItemState::eStartEdge
;
4608 if (area
.mRows
.mEnd
== mGridRowEnd
) {
4609 aItem
.mState
[eLogicalAxisBlock
] |= ItemState::eEndEdge
;
4614 SetLineMaps(&colLineNameMap
, &rowLineNameMap
);
4616 // http://dev.w3.org/csswg/css-grid/#line-placement
4617 // Resolve definite positions per spec chap 9.2.
4620 aState
.mGridItems
.ClearAndRetainStorage();
4621 aState
.mIter
.Reset();
4622 for (; !aState
.mIter
.AtEnd(); aState
.mIter
.Next()) {
4623 nsIFrame
* child
= *aState
.mIter
;
4624 GridItemInfo
* info
= aState
.mGridItems
.AppendElement(GridItemInfo(
4626 PlaceDefinite(child
, colLineNameMap
, rowLineNameMap
, gridStyle
)));
4627 MOZ_ASSERT(aState
.mIter
.ItemIndex() == aState
.mGridItems
.Length() - 1,
4628 "ItemIndex() is broken");
4629 GridArea
& area
= info
->mArea
;
4630 if (area
.mCols
.IsDefinite()) {
4631 minCol
= std::min(minCol
, area
.mCols
.mUntranslatedStart
);
4633 if (area
.mRows
.IsDefinite()) {
4634 minRow
= std::min(minRow
, area
.mRows
.mUntranslatedStart
);
4638 // Translate the whole grid so that the top-/left-most area is at 0,0.
4639 mExplicitGridOffsetCol
= 1 - minCol
; // minCol/Row is always <= 1, see above
4640 mExplicitGridOffsetRow
= 1 - minRow
;
4641 aState
.mColFunctions
.mExplicitGridOffset
= mExplicitGridOffsetCol
;
4642 aState
.mRowFunctions
.mExplicitGridOffset
= mExplicitGridOffsetRow
;
4643 const int32_t offsetToColZero
= int32_t(mExplicitGridOffsetCol
) - 1;
4644 const int32_t offsetToRowZero
= int32_t(mExplicitGridOffsetRow
) - 1;
4645 const bool isRowMasonry
= aState
.mFrame
->IsMasonry(eLogicalAxisBlock
);
4646 const bool isColMasonry
= aState
.mFrame
->IsMasonry(eLogicalAxisInline
);
4647 const bool isMasonry
= isColMasonry
|| isRowMasonry
;
4648 mGridColEnd
+= offsetToColZero
;
4649 mGridRowEnd
+= offsetToRowZero
;
4650 const uint32_t gridAxisTrackCount
= isRowMasonry
? mGridColEnd
: mGridRowEnd
;
4651 aState
.mIter
.Reset();
4652 for (; !aState
.mIter
.AtEnd(); aState
.mIter
.Next()) {
4653 auto& item
= aState
.mGridItems
[aState
.mIter
.ItemIndex()];
4654 GridArea
& area
= item
.mArea
;
4655 if (area
.mCols
.IsDefinite()) {
4656 area
.mCols
.mStart
= area
.mCols
.mUntranslatedStart
+ offsetToColZero
;
4657 area
.mCols
.mEnd
= area
.mCols
.mUntranslatedEnd
+ offsetToColZero
;
4659 if (area
.mRows
.IsDefinite()) {
4660 area
.mRows
.mStart
= area
.mRows
.mUntranslatedStart
+ offsetToRowZero
;
4661 area
.mRows
.mEnd
= area
.mRows
.mUntranslatedEnd
+ offsetToRowZero
;
4663 if (area
.IsDefinite()) {
4665 item
.MaybeInhibitSubgridInMasonry(aState
.mFrame
, gridAxisTrackCount
);
4667 if (item
.IsSubgrid()) {
4669 grid
.SubgridPlaceGridItems(aState
, this, item
);
4671 mCellMap
.Fill(area
);
4672 InflateGridFor(area
);
4673 SetSubgridChildEdgeBits(item
);
4677 // http://dev.w3.org/csswg/css-grid/#auto-placement-algo
4678 // Step 1, place 'auto' items that have one definite position -
4679 // definite row (column) for grid-auto-flow:row (column).
4680 auto flowStyle
= gridStyle
->mGridAutoFlow
;
4681 const bool isRowOrder
=
4682 isMasonry
? isRowMasonry
: !!(flowStyle
& StyleGridAutoFlow::ROW
);
4683 const bool isSparse
= !(flowStyle
& StyleGridAutoFlow::DENSE
);
4684 uint32_t clampMaxColLine
= colLineNameMap
.mClampMaxLine
+ offsetToColZero
;
4685 uint32_t clampMaxRowLine
= rowLineNameMap
.mClampMaxLine
+ offsetToRowZero
;
4686 // We need 1 cursor per row (or column) if placement is sparse.
4688 Maybe
<nsTHashMap
<nsUint32HashKey
, uint32_t>> cursors
;
4692 auto placeAutoMinorFunc
=
4693 isRowOrder
? &Grid::PlaceAutoCol
: &Grid::PlaceAutoRow
;
4694 uint32_t clampMaxLine
= isRowOrder
? clampMaxColLine
: clampMaxRowLine
;
4695 aState
.mIter
.Reset();
4696 for (; !aState
.mIter
.AtEnd(); aState
.mIter
.Next()) {
4697 auto& item
= aState
.mGridItems
[aState
.mIter
.ItemIndex()];
4698 GridArea
& area
= item
.mArea
;
4699 LineRange
& major
= isRowOrder
? area
.mRows
: area
.mCols
;
4700 LineRange
& minor
= isRowOrder
? area
.mCols
: area
.mRows
;
4701 if (major
.IsDefinite() && minor
.IsAuto()) {
4702 // Items with 'auto' in the minor dimension only.
4703 const uint32_t cursor
= isSparse
? cursors
->Get(major
.mStart
) : 0;
4704 (this->*placeAutoMinorFunc
)(cursor
, &area
, clampMaxLine
);
4706 item
.MaybeInhibitSubgridInMasonry(aState
.mFrame
, gridAxisTrackCount
);
4708 if (item
.IsSubgrid()) {
4710 grid
.SubgridPlaceGridItems(aState
, this, item
);
4712 mCellMap
.Fill(area
);
4713 SetSubgridChildEdgeBits(item
);
4715 cursors
->InsertOrUpdate(major
.mStart
, minor
.mEnd
);
4718 InflateGridFor(area
); // Step 2, inflating for auto items too
4722 // XXX NOTE possible spec issue.
4723 // XXX It's unclear if the remaining major-dimension auto and
4724 // XXX auto in both dimensions should use the same cursor or not,
4725 // XXX https://www.w3.org/Bugs/Public/show_bug.cgi?id=16044
4726 // XXX seems to indicate it shouldn't.
4727 // XXX http://dev.w3.org/csswg/css-grid/#auto-placement-cursor
4728 // XXX now says it should (but didn't in earlier versions)
4730 // Step 3, place the remaining grid items
4731 uint32_t cursorMajor
= 0; // for 'dense' these two cursors will stay at 0,0
4732 uint32_t cursorMinor
= 0;
4733 auto placeAutoMajorFunc
=
4734 isRowOrder
? &Grid::PlaceAutoRow
: &Grid::PlaceAutoCol
;
4735 uint32_t clampMaxMajorLine
= isRowOrder
? clampMaxRowLine
: clampMaxColLine
;
4736 aState
.mIter
.Reset();
4737 for (; !aState
.mIter
.AtEnd(); aState
.mIter
.Next()) {
4738 auto& item
= aState
.mGridItems
[aState
.mIter
.ItemIndex()];
4739 GridArea
& area
= item
.mArea
;
4740 MOZ_ASSERT(*aState
.mIter
== item
.mFrame
,
4741 "iterator out of sync with aState.mGridItems");
4742 LineRange
& major
= isRowOrder
? area
.mRows
: area
.mCols
;
4743 LineRange
& minor
= isRowOrder
? area
.mCols
: area
.mRows
;
4744 if (major
.IsAuto()) {
4745 if (minor
.IsDefinite()) {
4746 // Items with 'auto' in the major dimension only.
4748 if (minor
.mStart
< cursorMinor
) {
4751 cursorMinor
= minor
.mStart
;
4753 (this->*placeAutoMajorFunc
)(cursorMajor
, &area
, clampMaxMajorLine
);
4755 cursorMajor
= major
.mStart
;
4758 // Items with 'auto' in both dimensions.
4760 PlaceAutoAutoInRowOrder(cursorMinor
, cursorMajor
, &area
,
4761 clampMaxColLine
, clampMaxRowLine
);
4763 PlaceAutoAutoInColOrder(cursorMajor
, cursorMinor
, &area
,
4764 clampMaxColLine
, clampMaxRowLine
);
4767 cursorMajor
= major
.mStart
;
4768 cursorMinor
= minor
.mEnd
;
4770 uint32_t gridMajorEnd
= isRowOrder
? mGridRowEnd
: mGridColEnd
;
4771 uint32_t gridMinorEnd
= isRowOrder
? mGridColEnd
: mGridRowEnd
;
4772 MOZ_ASSERT(cursorMajor
<= gridMajorEnd
,
4773 "we shouldn't need to place items further than 1 track "
4774 "past the current end of the grid, in major dimension");
4775 MOZ_ASSERT(cursorMinor
<= gridMinorEnd
,
4776 "we shouldn't add implicit minor tracks for auto/auto");
4781 item
.MaybeInhibitSubgridInMasonry(aState
.mFrame
, gridAxisTrackCount
);
4783 if (item
.IsSubgrid()) {
4785 grid
.SubgridPlaceGridItems(aState
, this, item
);
4787 mCellMap
.Fill(area
);
4788 InflateGridFor(area
);
4789 SetSubgridChildEdgeBits(item
);
4790 // XXXmats it might be possible to optimize this a bit for masonry layout
4791 // if this item was placed in the 2nd row && !isSparse, or the 1st row
4792 // is full. Still gotta inflate the grid for all items though to make
4793 // the grid large enough...
4797 // Force all items into the 1st/2nd track and have span 1 in the masonry axis.
4798 // (See comment on nsGridContainerFrame::MasonryLayout().)
4800 auto masonryAxis
= isRowMasonry
? eLogicalAxisBlock
: eLogicalAxisInline
;
4801 aState
.mIter
.Reset();
4802 for (; !aState
.mIter
.AtEnd(); aState
.mIter
.Next()) {
4803 auto& item
= aState
.mGridItems
[aState
.mIter
.ItemIndex()];
4804 auto& masonryRange
= item
.mArea
.LineRangeForAxis(masonryAxis
);
4805 masonryRange
.mStart
= std::min(masonryRange
.mStart
, 1U);
4806 masonryRange
.mEnd
= masonryRange
.mStart
+ 1U;
4810 if (aState
.mFrame
->IsAbsoluteContainer()) {
4811 // 9.4 Absolutely-positioned Grid Items
4812 // http://dev.w3.org/csswg/css-grid/#abspos-items
4813 // We only resolve definite lines here; we'll align auto positions to the
4814 // grid container later during reflow.
4815 const nsFrameList
& children
=
4816 aState
.mFrame
->GetChildList(aState
.mFrame
->GetAbsoluteListID());
4817 const int32_t offsetToColZero
= int32_t(mExplicitGridOffsetCol
) - 1;
4818 const int32_t offsetToRowZero
= int32_t(mExplicitGridOffsetRow
) - 1;
4819 // Untranslate the grid again temporarily while resolving abs.pos. lines.
4820 AutoRestore
<uint32_t> zeroOffsetGridColEnd(mGridColEnd
);
4821 AutoRestore
<uint32_t> zeroOffsetGridRowEnd(mGridRowEnd
);
4822 mGridColEnd
-= offsetToColZero
;
4823 mGridRowEnd
-= offsetToRowZero
;
4824 aState
.mAbsPosItems
.ClearAndRetainStorage();
4825 for (nsIFrame
* child
: children
) {
4826 GridItemInfo
* info
= aState
.mAbsPosItems
.AppendElement(GridItemInfo(
4828 PlaceAbsPos(child
, colLineNameMap
, rowLineNameMap
, gridStyle
)));
4829 GridArea
& area
= info
->mArea
;
4830 if (area
.mCols
.mUntranslatedStart
!= int32_t(kAutoLine
)) {
4831 area
.mCols
.mStart
= area
.mCols
.mUntranslatedStart
+ offsetToColZero
;
4833 // XXXmats clamp any non-auto line to 0 or 1. This is intended to
4834 // allow authors to address the start/end of the masonry box.
4835 // This is experimental at this point though and needs author feedback
4836 // and spec work to sort out what is desired and how it should work.
4837 // See https://github.com/w3c/csswg-drafts/issues/4650
4838 area
.mCols
.mStart
= std::min(area
.mCols
.mStart
, 1U);
4841 if (area
.mCols
.mUntranslatedEnd
!= int32_t(kAutoLine
)) {
4842 area
.mCols
.mEnd
= area
.mCols
.mUntranslatedEnd
+ offsetToColZero
;
4845 area
.mCols
.mEnd
= std::min(area
.mCols
.mEnd
, 1U);
4848 if (area
.mRows
.mUntranslatedStart
!= int32_t(kAutoLine
)) {
4849 area
.mRows
.mStart
= area
.mRows
.mUntranslatedStart
+ offsetToRowZero
;
4852 area
.mRows
.mStart
= std::min(area
.mRows
.mStart
, 1U);
4855 if (area
.mRows
.mUntranslatedEnd
!= int32_t(kAutoLine
)) {
4856 area
.mRows
.mEnd
= area
.mRows
.mUntranslatedEnd
+ offsetToRowZero
;
4859 area
.mRows
.mEnd
= std::min(area
.mRows
.mEnd
, 1U);
4863 info
->MaybeInhibitSubgridInMasonry(aState
.mFrame
, gridAxisTrackCount
);
4866 // An abs.pos. subgrid with placement auto/1 or -1/auto technically
4867 // doesn't span any parent tracks. Inhibit subgridding in this case.
4868 if (info
->IsSubgrid(eLogicalAxisInline
)) {
4869 if (info
->mArea
.mCols
.mStart
== zeroOffsetGridColEnd
.SavedValue() ||
4870 info
->mArea
.mCols
.mEnd
== 0) {
4871 info
->InhibitSubgrid(aState
.mFrame
, eLogicalAxisInline
);
4874 if (info
->IsSubgrid(eLogicalAxisBlock
)) {
4875 if (info
->mArea
.mRows
.mStart
== zeroOffsetGridRowEnd
.SavedValue() ||
4876 info
->mArea
.mRows
.mEnd
== 0) {
4877 info
->InhibitSubgrid(aState
.mFrame
, eLogicalAxisBlock
);
4881 if (info
->IsSubgrid()) {
4883 grid
.SubgridPlaceGridItems(aState
, this, *info
);
4888 // Count empty 'auto-fit' tracks in the repeat() range.
4889 // |colAdjust| will have a count for each line in the grid of how many
4890 // tracks were empty between the start of the grid and that line.
4892 Maybe
<nsTArray
<uint32_t>> colAdjust
;
4893 uint32_t numEmptyCols
= 0;
4894 if (aState
.mColFunctions
.mHasRepeatAuto
&&
4895 gridStyle
->mGridTemplateColumns
.GetRepeatAutoValue()->count
.IsAutoFit()) {
4896 const auto& cellMap
= mCellMap
;
4897 colAdjust
= CalculateAdjustForAutoFitElements(
4898 &numEmptyCols
, aState
.mColFunctions
, mGridColEnd
+ 1,
4899 [&cellMap
](uint32_t i
) -> bool { return cellMap
.IsEmptyCol(i
); });
4902 // Do similar work for the row tracks, with the same logic.
4903 Maybe
<nsTArray
<uint32_t>> rowAdjust
;
4904 uint32_t numEmptyRows
= 0;
4905 if (aState
.mRowFunctions
.mHasRepeatAuto
&&
4906 gridStyle
->mGridTemplateRows
.GetRepeatAutoValue()->count
.IsAutoFit()) {
4907 const auto& cellMap
= mCellMap
;
4908 rowAdjust
= CalculateAdjustForAutoFitElements(
4909 &numEmptyRows
, aState
.mRowFunctions
, mGridRowEnd
+ 1,
4910 [&cellMap
](uint32_t i
) -> bool { return cellMap
.IsEmptyRow(i
); });
4912 MOZ_ASSERT((numEmptyCols
> 0) == colAdjust
.isSome());
4913 MOZ_ASSERT((numEmptyRows
> 0) == rowAdjust
.isSome());
4914 // Remove the empty 'auto-fit' tracks we found above, if any.
4915 if (numEmptyCols
|| numEmptyRows
) {
4916 // Adjust the line numbers in the grid areas.
4917 for (auto& item
: aState
.mGridItems
) {
4919 item
.AdjustForRemovedTracks(eLogicalAxisInline
, *colAdjust
);
4922 item
.AdjustForRemovedTracks(eLogicalAxisBlock
, *rowAdjust
);
4925 for (auto& item
: aState
.mAbsPosItems
) {
4927 item
.AdjustForRemovedTracks(eLogicalAxisInline
, *colAdjust
);
4930 item
.AdjustForRemovedTracks(eLogicalAxisBlock
, *rowAdjust
);
4933 // Adjust the grid size.
4934 mGridColEnd
-= numEmptyCols
;
4935 mExplicitGridColEnd
-= numEmptyCols
;
4936 mGridRowEnd
-= numEmptyRows
;
4937 mExplicitGridRowEnd
-= numEmptyRows
;
4938 // Adjust the track mapping to unmap the removed tracks.
4939 auto colRepeatCount
= aState
.mColFunctions
.NumRepeatTracks();
4940 aState
.mColFunctions
.SetNumRepeatTracks(colRepeatCount
- numEmptyCols
);
4941 auto rowRepeatCount
= aState
.mRowFunctions
.NumRepeatTracks();
4942 aState
.mRowFunctions
.SetNumRepeatTracks(rowRepeatCount
- numEmptyRows
);
4945 // Update the line boundaries of the implicit grid areas, if needed.
4946 if (mAreas
&& aState
.mFrame
->ShouldGenerateComputedInfo()) {
4947 for (auto iter
= mAreas
->iter(); !iter
.done(); iter
.next()) {
4948 auto& areaInfo
= iter
.get().value();
4950 // Resolve the lines for the area. We use the name of the area as the
4951 // name of the lines, knowing that the line placement algorithm will
4952 // add the -start and -end suffixes as appropriate for layout.
4953 StyleGridLine lineStartAndEnd
;
4954 lineStartAndEnd
.ident
= areaInfo
.name
;
4956 LineRange columnLines
=
4957 ResolveLineRange(lineStartAndEnd
, lineStartAndEnd
, colLineNameMap
,
4958 eLogicalAxisInline
, mExplicitGridColEnd
, gridStyle
);
4960 LineRange rowLines
=
4961 ResolveLineRange(lineStartAndEnd
, lineStartAndEnd
, rowLineNameMap
,
4962 eLogicalAxisBlock
, mExplicitGridRowEnd
, gridStyle
);
4964 // Put the resolved line indices back into the area structure.
4965 areaInfo
.columns
.start
= columnLines
.mStart
+ mExplicitGridOffsetCol
;
4966 areaInfo
.columns
.end
= columnLines
.mEnd
+ mExplicitGridOffsetCol
;
4967 areaInfo
.rows
.start
= rowLines
.mStart
+ mExplicitGridOffsetRow
;
4968 areaInfo
.rows
.end
= rowLines
.mEnd
+ mExplicitGridOffsetRow
;
4973 void nsGridContainerFrame::Tracks::Initialize(
4974 const TrackSizingFunctions
& aFunctions
,
4975 const NonNegativeLengthPercentageOrNormal
& aGridGap
, uint32_t aNumTracks
,
4976 nscoord aContentBoxSize
) {
4977 mSizes
.SetLength(aNumTracks
);
4978 PodZero(mSizes
.Elements(), mSizes
.Length());
4979 for (uint32_t i
= 0, len
= mSizes
.Length(); i
< len
; ++i
) {
4980 auto& sz
= mSizes
[i
];
4981 mStateUnion
|= sz
.Initialize(aContentBoxSize
, aFunctions
.SizingFor(i
));
4983 sz
.mBase
= aContentBoxSize
;
4984 sz
.mLimit
= aContentBoxSize
;
4987 mGridGap
= nsLayoutUtils::ResolveGapToLength(aGridGap
, aContentBoxSize
);
4988 mContentBoxSize
= aContentBoxSize
;
4992 * Reflow aChild in the given aAvailableSize.
4994 static nscoord
MeasuringReflow(nsIFrame
* aChild
,
4995 const ReflowInput
* aReflowInput
, gfxContext
* aRC
,
4996 const LogicalSize
& aAvailableSize
,
4997 const LogicalSize
& aCBSize
,
4998 nscoord aIMinSizeClamp
= NS_MAXSIZE
,
4999 nscoord aBMinSizeClamp
= NS_MAXSIZE
) {
5000 nsContainerFrame
* parent
= aChild
->GetParent();
5001 nsPresContext
* pc
= aChild
->PresContext();
5002 Maybe
<ReflowInput
> dummyParentState
;
5003 const ReflowInput
* rs
= aReflowInput
;
5004 if (!aReflowInput
) {
5005 MOZ_ASSERT(!parent
->HasAnyStateBits(NS_FRAME_IN_REFLOW
));
5006 dummyParentState
.emplace(
5008 LogicalSize(parent
->GetWritingMode(), 0, NS_UNCONSTRAINEDSIZE
),
5009 ReflowInput::InitFlag::DummyParentReflowInput
);
5010 rs
= dummyParentState
.ptr();
5013 // This will suppress various ABSURD_SIZE warnings for this reflow.
5014 parent
->SetProperty(nsContainerFrame::DebugReflowingWithInfiniteISize(),
5017 auto wm
= aChild
->GetWritingMode();
5018 ComputeSizeFlags csFlags
= ComputeSizeFlag::IsGridMeasuringReflow
;
5019 if (aAvailableSize
.ISize(wm
) == INFINITE_ISIZE_COORD
) {
5020 csFlags
+= ComputeSizeFlag::ShrinkWrap
;
5022 if (aIMinSizeClamp
!= NS_MAXSIZE
) {
5023 csFlags
+= ComputeSizeFlag::IClampMarginBoxMinSize
;
5025 if (aBMinSizeClamp
!= NS_MAXSIZE
) {
5026 csFlags
+= ComputeSizeFlag::BClampMarginBoxMinSize
;
5027 aChild
->SetProperty(nsIFrame::BClampMarginBoxMinSizeProperty(),
5030 aChild
->RemoveProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
5032 ReflowInput
childRI(pc
, *rs
, aChild
, aAvailableSize
, Some(aCBSize
), {}, {},
5035 // FIXME (perf): It would be faster to do this only if the previous reflow of
5036 // the child was not a measuring reflow, and only if the child does some of
5037 // the things that are affected by ComputeSizeFlag::IsGridMeasuringReflow.
5038 childRI
.SetBResize(true);
5039 // Not 100% sure this is needed, but be conservative for now:
5040 childRI
.mFlags
.mIsBResizeForPercentages
= true;
5042 ReflowOutput
childSize(childRI
);
5043 nsReflowStatus childStatus
;
5044 const nsIFrame::ReflowChildFlags flags
=
5045 nsIFrame::ReflowChildFlags::NoMoveFrame
|
5046 nsIFrame::ReflowChildFlags::NoSizeView
|
5047 nsIFrame::ReflowChildFlags::NoDeleteNextInFlowChild
;
5050 GridItemCachedBAxisMeasurement cachedMeasurement
=
5051 aChild
->GetProperty(GridItemCachedBAxisMeasurement::Prop(), &found
);
5052 if (found
&& cachedMeasurement
.IsValidFor(aChild
, aCBSize
)) {
5053 childSize
.BSize(wm
) = cachedMeasurement
.BSize();
5054 childSize
.ISize(wm
) = aChild
->ISize(wm
);
5055 nsContainerFrame::FinishReflowChild(aChild
, pc
, childSize
, &childRI
, wm
,
5056 LogicalPoint(wm
), nsSize(), flags
);
5058 "[perf] MeasuringReflow accepted cached value=%d, child=%p, "
5060 cachedMeasurement
.BSize(), aChild
,
5061 aCBSize
.ISize(aChild
->GetWritingMode()));
5062 return cachedMeasurement
.BSize();
5065 parent
->ReflowChild(aChild
, pc
, childSize
, childRI
, wm
, LogicalPoint(wm
),
5066 nsSize(), flags
, childStatus
);
5067 nsContainerFrame::FinishReflowChild(aChild
, pc
, childSize
, &childRI
, wm
,
5068 LogicalPoint(wm
), nsSize(), flags
);
5070 parent
->RemoveProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
5073 GridItemCachedBAxisMeasurement::CanCacheMeasurement(aChild
, aCBSize
)) {
5074 GridItemCachedBAxisMeasurement
cachedMeasurement(aChild
, aCBSize
,
5075 childSize
.BSize(wm
));
5076 aChild
->SetProperty(GridItemCachedBAxisMeasurement::Prop(),
5079 "[perf] MeasuringReflow created new cached value=%d, child=%p, "
5081 cachedMeasurement
.BSize(), aChild
,
5082 aCBSize
.ISize(aChild
->GetWritingMode()));
5084 if (GridItemCachedBAxisMeasurement::CanCacheMeasurement(aChild
, aCBSize
)) {
5085 cachedMeasurement
.Update(aChild
, aCBSize
, childSize
.BSize(wm
));
5087 "[perf] MeasuringReflow rejected but updated cached value=%d, "
5088 "child=%p, aCBSize.ISize=%d",
5089 cachedMeasurement
.BSize(), aChild
,
5090 aCBSize
.ISize(aChild
->GetWritingMode()));
5091 aChild
->SetProperty(GridItemCachedBAxisMeasurement::Prop(),
5094 aChild
->RemoveProperty(GridItemCachedBAxisMeasurement::Prop());
5096 "[perf] MeasuringReflow rejected and removed cached value, "
5102 return childSize
.BSize(wm
);
5106 * Reflow aChild in the given aAvailableSize, using aNewContentBoxSize as its
5107 * computed size in aChildAxis.
5109 static void PostReflowStretchChild(
5110 nsIFrame
* aChild
, const ReflowInput
& aReflowInput
,
5111 const LogicalSize
& aAvailableSize
, const LogicalSize
& aCBSize
,
5112 LogicalAxis aChildAxis
, const nscoord aNewContentBoxSize
,
5113 nscoord aIMinSizeClamp
= NS_MAXSIZE
, nscoord aBMinSizeClamp
= NS_MAXSIZE
) {
5114 nsPresContext
* pc
= aChild
->PresContext();
5115 ComputeSizeFlags csFlags
;
5116 if (aIMinSizeClamp
!= NS_MAXSIZE
) {
5117 csFlags
+= ComputeSizeFlag::IClampMarginBoxMinSize
;
5119 if (aBMinSizeClamp
!= NS_MAXSIZE
) {
5120 csFlags
+= ComputeSizeFlag::BClampMarginBoxMinSize
;
5121 aChild
->SetProperty(nsIFrame::BClampMarginBoxMinSizeProperty(),
5124 aChild
->RemoveProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
5126 ReflowInput
ri(pc
, aReflowInput
, aChild
, aAvailableSize
, Some(aCBSize
), {},
5128 if (aChildAxis
== eLogicalAxisBlock
) {
5129 ri
.SetComputedBSize(ri
.ApplyMinMaxBSize(aNewContentBoxSize
));
5131 ri
.SetComputedISize(ri
.ApplyMinMaxISize(aNewContentBoxSize
));
5133 ReflowOutput
childSize(ri
);
5134 nsReflowStatus childStatus
;
5135 const nsIFrame::ReflowChildFlags flags
=
5136 nsIFrame::ReflowChildFlags::NoMoveFrame
|
5137 nsIFrame::ReflowChildFlags::NoDeleteNextInFlowChild
;
5138 auto wm
= aChild
->GetWritingMode();
5139 nsContainerFrame
* parent
= aChild
->GetParent();
5140 parent
->ReflowChild(aChild
, pc
, childSize
, ri
, wm
, LogicalPoint(wm
), nsSize(),
5141 flags
, childStatus
);
5142 nsContainerFrame::FinishReflowChild(aChild
, pc
, childSize
, &ri
, wm
,
5143 LogicalPoint(wm
), nsSize(), flags
);
5147 * Return the accumulated margin+border+padding in aAxis for aFrame (a subgrid)
5148 * and its ancestor subgrids.
5150 static LogicalMargin
SubgridAccumulatedMarginBorderPadding(
5151 nsIFrame
* aFrame
, const Subgrid
* aSubgrid
, WritingMode aResultWM
,
5152 LogicalAxis aAxis
) {
5153 MOZ_ASSERT(aFrame
->IsGridContainerFrame());
5154 auto* subgridFrame
= static_cast<nsGridContainerFrame
*>(aFrame
);
5155 LogicalMargin
result(aSubgrid
->mMarginBorderPadding
);
5156 auto* parent
= subgridFrame
->ParentGridContainerForSubgrid();
5157 auto subgridCBWM
= parent
->GetWritingMode();
5158 auto childRange
= aSubgrid
->mArea
.LineRangeForAxis(aAxis
);
5159 bool skipStartSide
= false;
5160 bool skipEndSide
= false;
5161 auto axis
= aSubgrid
->mIsOrthogonal
? GetOrthogonalAxis(aAxis
) : aAxis
;
5162 // If aFrame's parent is also a subgrid, then add its MBP on the edges that
5163 // are adjacent (i.e. start or end in the same track), recursively.
5164 // ("parent" refers to the grid-frame we're currently adding MBP for,
5165 // and "grandParent" its parent, as we walk up the chain.)
5166 while (parent
->IsSubgrid(axis
)) {
5167 auto* parentSubgrid
= parent
->GetProperty(Subgrid::Prop());
5168 auto* grandParent
= parent
->ParentGridContainerForSubgrid();
5169 auto parentCBWM
= grandParent
->GetWritingMode();
5170 if (parentCBWM
.IsOrthogonalTo(subgridCBWM
)) {
5171 axis
= GetOrthogonalAxis(axis
);
5173 const auto& parentRange
= parentSubgrid
->mArea
.LineRangeForAxis(axis
);
5174 bool sameDir
= parentCBWM
.ParallelAxisStartsOnSameSide(axis
, subgridCBWM
);
5176 skipStartSide
|= childRange
.mStart
!= 0;
5177 skipEndSide
|= childRange
.mEnd
!= parentRange
.Extent();
5179 skipEndSide
|= childRange
.mStart
!= 0;
5180 skipStartSide
|= childRange
.mEnd
!= parentRange
.Extent();
5182 if (skipStartSide
&& skipEndSide
) {
5186 parentSubgrid
->mMarginBorderPadding
.ConvertTo(subgridCBWM
, parentCBWM
);
5187 if (skipStartSide
) {
5188 mbp
.Start(aAxis
, subgridCBWM
) = nscoord(0);
5191 mbp
.End(aAxis
, subgridCBWM
) = nscoord(0);
5194 parent
= grandParent
;
5195 childRange
= parentRange
;
5197 return result
.ConvertTo(aResultWM
, subgridCBWM
);
5201 * Return the [min|max]-content contribution of aChild to its parent (i.e.
5202 * the child's margin-box) in aAxis.
5204 static nscoord
ContentContribution(
5205 const GridItemInfo
& aGridItem
, const GridReflowInput
& aState
,
5206 gfxContext
* aRC
, WritingMode aCBWM
, LogicalAxis aAxis
,
5207 const Maybe
<LogicalSize
>& aPercentageBasis
, IntrinsicISizeType aConstraint
,
5208 nscoord aMinSizeClamp
= NS_MAXSIZE
, uint32_t aFlags
= 0) {
5209 nsIFrame
* child
= aGridItem
.mFrame
;
5211 nscoord extraMargin
= 0;
5212 nsGridContainerFrame::Subgrid
* subgrid
= nullptr;
5213 if (child
->GetParent() != aState
.mFrame
) {
5214 // |child| is a subgrid descendant, so it contributes its subgrids'
5215 // margin+border+padding for any edge tracks that it spans.
5216 auto* subgridFrame
= child
->GetParent();
5217 subgrid
= subgridFrame
->GetProperty(Subgrid::Prop());
5218 const auto itemEdgeBits
= aGridItem
.mState
[aAxis
] & ItemState::eEdgeBits
;
5220 LogicalMargin mbp
= SubgridAccumulatedMarginBorderPadding(
5221 subgridFrame
, subgrid
, aCBWM
, aAxis
);
5222 if (itemEdgeBits
& ItemState::eStartEdge
) {
5223 extraMargin
+= mbp
.Start(aAxis
, aCBWM
);
5225 if (itemEdgeBits
& ItemState::eEndEdge
) {
5226 extraMargin
+= mbp
.End(aAxis
, aCBWM
);
5229 // It also contributes (half of) the subgrid's gap on its edges (if any)
5230 // subtracted by the non-subgrid ancestor grid container's gap.
5231 // Note that this can also be negative since it's considered a margin.
5232 if (itemEdgeBits
!= ItemState::eEdgeBits
) {
5233 auto subgridAxis
= aCBWM
.IsOrthogonalTo(subgridFrame
->GetWritingMode())
5234 ? GetOrthogonalAxis(aAxis
)
5236 auto& gapStyle
= subgridAxis
== eLogicalAxisBlock
5237 ? subgridFrame
->StylePosition()->mRowGap
5238 : subgridFrame
->StylePosition()->mColumnGap
;
5239 if (!gapStyle
.IsNormal()) {
5240 auto subgridExtent
= subgridAxis
== eLogicalAxisBlock
5241 ? subgrid
->mGridRowEnd
5242 : subgrid
->mGridColEnd
;
5243 if (subgridExtent
> 1) {
5244 nscoord subgridGap
=
5245 nsLayoutUtils::ResolveGapToLength(gapStyle
, NS_UNCONSTRAINEDSIZE
);
5247 aAxis
== eLogicalAxisBlock
? aState
.mRows
: aState
.mCols
;
5248 auto gapDelta
= subgridGap
- tracks
.mGridGap
;
5249 if (!itemEdgeBits
) {
5250 extraMargin
+= gapDelta
;
5252 extraMargin
+= gapDelta
/ 2;
5259 PhysicalAxis
axis(aCBWM
.PhysicalAxis(aAxis
));
5260 nscoord size
= nsLayoutUtils::IntrinsicForAxis(
5261 axis
, aRC
, child
, aConstraint
, aPercentageBasis
,
5262 aFlags
| nsLayoutUtils::BAIL_IF_REFLOW_NEEDED
, aMinSizeClamp
);
5263 auto childWM
= child
->GetWritingMode();
5264 const bool isOrthogonal
= childWM
.IsOrthogonalTo(aCBWM
);
5265 auto childAxis
= isOrthogonal
? GetOrthogonalAxis(aAxis
) : aAxis
;
5266 if (size
== NS_INTRINSIC_ISIZE_UNKNOWN
&& childAxis
== eLogicalAxisBlock
) {
5267 // We need to reflow the child to find its BSize contribution.
5268 // XXX this will give mostly correct results for now (until bug 1174569).
5269 nscoord availISize
= INFINITE_ISIZE_COORD
;
5270 nscoord availBSize
= NS_UNCONSTRAINEDSIZE
;
5271 // The next two variables are MinSizeClamp values in the child's axes.
5272 nscoord iMinSizeClamp
= NS_MAXSIZE
;
5273 nscoord bMinSizeClamp
= NS_MAXSIZE
;
5274 LogicalSize
cbSize(childWM
, 0, NS_UNCONSTRAINEDSIZE
);
5275 // Below, we try to resolve the child's grid-area size in its inline-axis
5276 // to use as the CB/Available size in the MeasuringReflow that follows.
5277 if (child
->GetParent() != aState
.mFrame
) {
5278 // This item is a child of a subgrid descendant.
5279 auto* subgridFrame
=
5280 static_cast<nsGridContainerFrame
*>(child
->GetParent());
5281 MOZ_ASSERT(subgridFrame
->IsGridContainerFrame());
5282 auto* uts
= subgridFrame
->GetProperty(UsedTrackSizes::Prop());
5284 uts
= new UsedTrackSizes();
5285 subgridFrame
->SetProperty(UsedTrackSizes::Prop(), uts
);
5287 // The grid-item's inline-axis as expressed in the subgrid's WM.
5288 auto subgridAxis
= childWM
.IsOrthogonalTo(subgridFrame
->GetWritingMode())
5290 : eLogicalAxisInline
;
5291 uts
->ResolveTrackSizesForAxis(subgridFrame
, subgridAxis
, *aRC
);
5292 if (uts
->mCanResolveLineRangeSize
[subgridAxis
]) {
5294 subgridFrame
->GetProperty(nsGridContainerFrame::Subgrid::Prop());
5295 const GridItemInfo
* originalItem
= nullptr;
5296 for (const auto& item
: subgrid
->mGridItems
) {
5297 if (item
.mFrame
== child
) {
5298 originalItem
= &item
;
5302 MOZ_ASSERT(originalItem
, "huh?");
5303 const auto& range
= originalItem
->mArea
.LineRangeForAxis(subgridAxis
);
5305 range
.ToPositionAndLength(uts
->mSizes
[subgridAxis
], &pos
, &sz
);
5306 if (childWM
.IsOrthogonalTo(subgridFrame
->GetWritingMode())) {
5308 cbSize
.BSize(childWM
) = sz
;
5309 if (aGridItem
.mState
[aAxis
] & ItemState::eClampMarginBoxMinSize
) {
5314 cbSize
.ISize(childWM
) = sz
;
5315 if (aGridItem
.mState
[aAxis
] & ItemState::eClampMarginBoxMinSize
) {
5320 } else if (aState
.mCols
.mCanResolveLineRangeSize
) {
5321 nscoord sz
= aState
.mCols
.ResolveSize(aGridItem
.mArea
.mCols
);
5324 cbSize
.BSize(childWM
) = sz
;
5325 if (aGridItem
.mState
[aAxis
] & ItemState::eClampMarginBoxMinSize
) {
5330 cbSize
.ISize(childWM
) = sz
;
5331 if (aGridItem
.mState
[aAxis
] & ItemState::eClampMarginBoxMinSize
) {
5336 if (isOrthogonal
== (aAxis
== eLogicalAxisInline
)) {
5337 bMinSizeClamp
= aMinSizeClamp
;
5339 iMinSizeClamp
= aMinSizeClamp
;
5341 LogicalSize
availableSize(childWM
, availISize
, availBSize
);
5342 size
= ::MeasuringReflow(child
, aState
.mReflowInput
, aRC
, availableSize
,
5343 cbSize
, iMinSizeClamp
, bMinSizeClamp
);
5344 size
+= child
->GetLogicalUsedMargin(childWM
).BStartEnd(childWM
);
5345 nscoord overflow
= size
- aMinSizeClamp
;
5346 if (MOZ_UNLIKELY(overflow
> 0)) {
5347 nscoord contentSize
= child
->ContentBSize(childWM
);
5348 nscoord newContentSize
= std::max(nscoord(0), contentSize
- overflow
);
5349 // XXXmats deal with percentages better, see bug 1300369 comment 27.
5350 size
-= contentSize
- newContentSize
;
5353 MOZ_ASSERT(aGridItem
.mBaselineOffset
[aAxis
] >= 0,
5354 "baseline offset should be non-negative at this point");
5355 MOZ_ASSERT((aGridItem
.mState
[aAxis
] & ItemState::eIsBaselineAligned
) ||
5356 aGridItem
.mBaselineOffset
[aAxis
] == nscoord(0),
5357 "baseline offset should be zero when not baseline-aligned");
5358 size
+= aGridItem
.mBaselineOffset
[aAxis
];
5359 size
+= extraMargin
;
5360 return std::max(size
, 0);
5363 struct CachedIntrinsicSizes
{
5364 Maybe
<nscoord
> mMinSize
;
5365 Maybe
<nscoord
> mMinContentContribution
;
5366 Maybe
<nscoord
> mMaxContentContribution
;
5368 // The item's percentage basis for intrinsic sizing purposes.
5369 Maybe
<LogicalSize
> mPercentageBasis
;
5371 // "if the grid item spans only grid tracks that have a fixed max track
5372 // sizing function, its automatic minimum size in that dimension is
5373 // further clamped to less than or equal to the size necessary to fit its
5374 // margin box within the resulting grid area (flooring at zero)"
5375 // https://drafts.csswg.org/css-grid/#min-size-auto
5376 // This is the clamp value to use for that:
5377 nscoord mMinSizeClamp
= NS_MAXSIZE
;
5380 static nscoord
MinContentContribution(const GridItemInfo
& aGridItem
,
5381 const GridReflowInput
& aState
,
5382 gfxContext
* aRC
, WritingMode aCBWM
,
5384 CachedIntrinsicSizes
* aCache
) {
5385 if (aCache
->mMinContentContribution
.isSome()) {
5386 return aCache
->mMinContentContribution
.value();
5388 if (aCache
->mPercentageBasis
.isNothing()) {
5389 aCache
->mPercentageBasis
.emplace(
5390 aState
.PercentageBasisFor(aAxis
, aGridItem
));
5392 nscoord s
= ContentContribution(
5393 aGridItem
, aState
, aRC
, aCBWM
, aAxis
, aCache
->mPercentageBasis
,
5394 IntrinsicISizeType::MinISize
, aCache
->mMinSizeClamp
);
5395 aCache
->mMinContentContribution
.emplace(s
);
5399 static nscoord
MaxContentContribution(const GridItemInfo
& aGridItem
,
5400 const GridReflowInput
& aState
,
5401 gfxContext
* aRC
, WritingMode aCBWM
,
5403 CachedIntrinsicSizes
* aCache
) {
5404 if (aCache
->mMaxContentContribution
.isSome()) {
5405 return aCache
->mMaxContentContribution
.value();
5407 if (aCache
->mPercentageBasis
.isNothing()) {
5408 aCache
->mPercentageBasis
.emplace(
5409 aState
.PercentageBasisFor(aAxis
, aGridItem
));
5411 nscoord s
= ContentContribution(
5412 aGridItem
, aState
, aRC
, aCBWM
, aAxis
, aCache
->mPercentageBasis
,
5413 IntrinsicISizeType::PrefISize
, aCache
->mMinSizeClamp
);
5414 aCache
->mMaxContentContribution
.emplace(s
);
5418 // Computes the min-size contribution for a grid item, as defined at
5419 // https://drafts.csswg.org/css-grid/#min-size-contribution
5420 static nscoord
MinSize(const GridItemInfo
& aGridItem
,
5421 const GridReflowInput
& aState
, gfxContext
* aRC
,
5422 WritingMode aCBWM
, LogicalAxis aAxis
,
5423 CachedIntrinsicSizes
* aCache
) {
5424 if (aCache
->mMinSize
.isSome()) {
5425 return aCache
->mMinSize
.value();
5427 nsIFrame
* child
= aGridItem
.mFrame
;
5428 PhysicalAxis
axis(aCBWM
.PhysicalAxis(aAxis
));
5429 const nsStylePosition
* stylePos
= child
->StylePosition();
5430 StyleSize sizeStyle
=
5431 axis
== eAxisHorizontal
? stylePos
->mWidth
: stylePos
->mHeight
;
5433 auto ourInlineAxis
= child
->GetWritingMode().PhysicalAxis(eLogicalAxisInline
);
5434 // max-content and min-content should behave as initial value in block axis.
5435 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
5436 // for block size dimension on sizing properties (e.g. height), so we
5437 // treat it as `auto`.
5438 if (axis
!= ourInlineAxis
&& sizeStyle
.BehavesLikeInitialValueOnBlockAxis()) {
5439 sizeStyle
= StyleSize::Auto();
5442 if (!sizeStyle
.IsAuto() && !sizeStyle
.HasPercent()) {
5444 MinContentContribution(aGridItem
, aState
, aRC
, aCBWM
, aAxis
, aCache
);
5445 aCache
->mMinSize
.emplace(s
);
5449 if (aCache
->mPercentageBasis
.isNothing()) {
5450 aCache
->mPercentageBasis
.emplace(
5451 aState
.PercentageBasisFor(aAxis
, aGridItem
));
5454 // https://drafts.csswg.org/css-grid/#min-size-auto
5455 // This calculates the min-content contribution from either a definite
5456 // min-width (or min-height depending on aAxis), or the "specified /
5457 // transferred size" for min-width:auto if overflow == visible (as min-width:0
5458 // otherwise), or NS_UNCONSTRAINEDSIZE for other min-width intrinsic values
5459 // (which results in always taking the "content size" part below).
5460 MOZ_ASSERT(aGridItem
.mBaselineOffset
[aAxis
] >= 0,
5461 "baseline offset should be non-negative at this point");
5462 MOZ_ASSERT((aGridItem
.mState
[aAxis
] & ItemState::eIsBaselineAligned
) ||
5463 aGridItem
.mBaselineOffset
[aAxis
] == nscoord(0),
5464 "baseline offset should be zero when not baseline-aligned");
5465 nscoord sz
= aGridItem
.mBaselineOffset
[aAxis
] +
5466 nsLayoutUtils::MinSizeContributionForAxis(
5467 axis
, aRC
, child
, IntrinsicISizeType::MinISize
,
5468 *aCache
->mPercentageBasis
);
5469 const StyleSize
& style
=
5470 axis
== eAxisHorizontal
? stylePos
->mMinWidth
: stylePos
->mMinHeight
;
5471 // max-content and min-content should behave as initial value in block axis.
5472 // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
5473 // for block size dimension on sizing properties (e.g. height), so we
5474 // treat it as `auto`.
5475 const bool inInlineAxis
= axis
== ourInlineAxis
;
5478 (!inInlineAxis
&& style
.BehavesLikeInitialValueOnBlockAxis());
5479 if ((inInlineAxis
&& nsIFrame::ToExtremumLength(style
)) ||
5480 (isAuto
&& child
->StyleDisplay()->mOverflowX
== StyleOverflow::Visible
)) {
5481 // Now calculate the "content size" part and return whichever is smaller.
5482 MOZ_ASSERT(isAuto
|| sz
== NS_UNCONSTRAINEDSIZE
);
5483 sz
= std::min(sz
, ContentContribution(aGridItem
, aState
, aRC
, aCBWM
, aAxis
,
5484 aCache
->mPercentageBasis
,
5485 IntrinsicISizeType::MinISize
,
5486 aCache
->mMinSizeClamp
,
5487 nsLayoutUtils::MIN_INTRINSIC_ISIZE
));
5489 aCache
->mMinSize
.emplace(sz
);
5493 void nsGridContainerFrame::Tracks::CalculateSizes(
5494 GridReflowInput
& aState
, nsTArray
<GridItemInfo
>& aGridItems
,
5495 const TrackSizingFunctions
& aFunctions
, nscoord aContentBoxSize
,
5496 LineRange
GridArea::*aRange
, SizingConstraint aConstraint
) {
5497 nscoord percentageBasis
= aContentBoxSize
;
5498 if (percentageBasis
== NS_UNCONSTRAINEDSIZE
) {
5499 percentageBasis
= 0;
5501 InitializeItemBaselines(aState
, aGridItems
);
5502 ResolveIntrinsicSize(aState
, aGridItems
, aFunctions
, aRange
, percentageBasis
,
5504 if (aConstraint
!= SizingConstraint::MinContent
) {
5505 nscoord freeSpace
= aContentBoxSize
;
5506 if (freeSpace
!= NS_UNCONSTRAINEDSIZE
) {
5507 freeSpace
-= SumOfGridGaps();
5509 DistributeFreeSpace(freeSpace
);
5510 StretchFlexibleTracks(aState
, aGridItems
, aFunctions
, freeSpace
);
5514 TrackSize::StateBits
nsGridContainerFrame::Tracks::StateBitsForRange(
5515 const LineRange
& aRange
) const {
5516 MOZ_ASSERT(!aRange
.IsAuto(), "must have a definite range");
5517 TrackSize::StateBits state
= TrackSize::StateBits
{0};
5518 for (auto i
: aRange
.Range()) {
5519 state
|= mSizes
[i
].mState
;
5524 static void AddSubgridContribution(TrackSize
& aSize
,
5525 nscoord aMarginBorderPadding
) {
5526 if (aSize
.mState
& TrackSize::eIntrinsicMinSizing
) {
5527 aSize
.mBase
= std::max(aSize
.mBase
, aMarginBorderPadding
);
5528 aSize
.mLimit
= std::max(aSize
.mLimit
, aSize
.mBase
);
5530 // XXX maybe eFlexMaxSizing too?
5531 // (once we implement https://github.com/w3c/csswg-drafts/issues/2177)
5533 (TrackSize::eIntrinsicMaxSizing
| TrackSize::eFitContent
)) {
5534 aSize
.mLimit
= std::max(aSize
.mLimit
, aMarginBorderPadding
);
5538 bool nsGridContainerFrame::Tracks::ResolveIntrinsicSizeForNonSpanningItems(
5539 GridReflowInput
& aState
, const TrackSizingFunctions
& aFunctions
,
5540 nscoord aPercentageBasis
, SizingConstraint aConstraint
,
5541 const LineRange
& aRange
, const GridItemInfo
& aGridItem
) {
5542 gfxContext
* rc
= &aState
.mRenderingContext
;
5543 WritingMode wm
= aState
.mWM
;
5544 CachedIntrinsicSizes cache
;
5545 TrackSize
& sz
= mSizes
[aRange
.mStart
];
5548 if (sz
.mState
& TrackSize::eAutoMinSizing
) {
5550 // Check if we need to apply "Automatic Minimum Size" and cache it.
5551 if (aGridItem
.ShouldApplyAutoMinSize(wm
, mAxis
, aPercentageBasis
)) {
5552 aGridItem
.mState
[mAxis
] |= ItemState::eApplyAutoMinSize
;
5553 // Clamp it if it's spanning a definite track max-sizing function.
5554 if (TrackSize::IsDefiniteMaxSizing(sz
.mState
)) {
5555 cache
.mMinSizeClamp
= aFunctions
.MaxSizingFor(aRange
.mStart
)
5557 .Resolve(aPercentageBasis
);
5558 aGridItem
.mState
[mAxis
] |= ItemState::eClampMarginBoxMinSize
;
5560 if (aConstraint
!= SizingConstraint::MaxContent
) {
5561 s
= MinContentContribution(aGridItem
, aState
, rc
, wm
, mAxis
, &cache
);
5563 s
= MaxContentContribution(aGridItem
, aState
, rc
, wm
, mAxis
, &cache
);
5566 s
= MinSize(aGridItem
, aState
, rc
, wm
, mAxis
, &cache
);
5568 sz
.mBase
= std::max(sz
.mBase
, s
);
5569 } else if (sz
.mState
& TrackSize::eMinContentMinSizing
) {
5570 auto s
= MinContentContribution(aGridItem
, aState
, rc
, wm
, mAxis
, &cache
);
5571 sz
.mBase
= std::max(sz
.mBase
, s
);
5572 } else if (sz
.mState
& TrackSize::eMaxContentMinSizing
) {
5573 auto s
= MaxContentContribution(aGridItem
, aState
, rc
, wm
, mAxis
, &cache
);
5574 sz
.mBase
= std::max(sz
.mBase
, s
);
5578 if (sz
.mState
& TrackSize::eMinContentMaxSizing
) {
5579 auto s
= MinContentContribution(aGridItem
, aState
, rc
, wm
, mAxis
, &cache
);
5580 if (sz
.mLimit
== NS_UNCONSTRAINEDSIZE
) {
5583 sz
.mLimit
= std::max(sz
.mLimit
, s
);
5585 } else if (sz
.mState
&
5586 (TrackSize::eAutoMaxSizing
| TrackSize::eMaxContentMaxSizing
)) {
5587 auto s
= MaxContentContribution(aGridItem
, aState
, rc
, wm
, mAxis
, &cache
);
5588 if (sz
.mLimit
== NS_UNCONSTRAINEDSIZE
) {
5591 sz
.mLimit
= std::max(sz
.mLimit
, s
);
5593 if (MOZ_UNLIKELY(sz
.mState
& TrackSize::eFitContent
)) {
5594 // Clamp mLimit to the fit-content() size, for §12.5.1.
5595 nscoord fitContentClamp
= aFunctions
.SizingFor(aRange
.mStart
)
5598 .Resolve(aPercentageBasis
);
5599 sz
.mLimit
= std::min(sz
.mLimit
, fitContentClamp
);
5603 if (sz
.mLimit
< sz
.mBase
) {
5604 sz
.mLimit
= sz
.mBase
;
5607 return sz
.mState
& TrackSize::eFlexMaxSizing
;
5610 void nsGridContainerFrame::Tracks::CalculateItemBaselines(
5611 nsTArray
<ItemBaselineData
>& aBaselineItems
,
5612 BaselineSharingGroup aBaselineGroup
) {
5613 if (aBaselineItems
.IsEmpty()) {
5617 // Sort the collected items on their baseline track.
5618 std::sort(aBaselineItems
.begin(), aBaselineItems
.end(),
5619 ItemBaselineData::IsBaselineTrackLessThan
);
5621 MOZ_ASSERT(mSizes
.Length() > 0, "having an item implies at least one track");
5622 const uint32_t lastTrack
= mSizes
.Length() - 1;
5623 nscoord maxBaseline
= 0;
5624 nscoord maxDescent
= 0;
5625 uint32_t currentTrack
= kAutoLine
; // guaranteed to not match any item
5626 uint32_t trackStartIndex
= 0;
5627 for (uint32_t i
= 0, len
= aBaselineItems
.Length(); true; ++i
) {
5628 // Find the maximum baseline and descent in the current track.
5630 const ItemBaselineData
& item
= aBaselineItems
[i
];
5631 if (currentTrack
== item
.mBaselineTrack
) {
5632 maxBaseline
= std::max(maxBaseline
, item
.mBaseline
);
5633 maxDescent
= std::max(maxDescent
, item
.mSize
- item
.mBaseline
);
5637 // Iterate the current track again and update the baseline offsets making
5638 // all items baseline-aligned within this group in this track.
5639 for (uint32_t j
= trackStartIndex
; j
< i
; ++j
) {
5640 const ItemBaselineData
& item
= aBaselineItems
[j
];
5641 item
.mGridItem
->mBaselineOffset
[mAxis
] = maxBaseline
- item
.mBaseline
;
5642 MOZ_ASSERT(item
.mGridItem
->mBaselineOffset
[mAxis
] >= 0);
5645 // Store the size of this baseline-aligned subtree.
5646 mSizes
[currentTrack
].mBaselineSubtreeSize
[aBaselineGroup
] =
5647 maxBaseline
+ maxDescent
;
5648 // Record the first(last) baseline for the first(last) track.
5649 if (currentTrack
== 0 && aBaselineGroup
== BaselineSharingGroup::First
) {
5650 mBaseline
[aBaselineGroup
] = maxBaseline
;
5652 if (currentTrack
== lastTrack
&&
5653 aBaselineGroup
== BaselineSharingGroup::Last
) {
5654 mBaseline
[aBaselineGroup
] = maxBaseline
;
5660 // Initialize data for the next track with baseline-aligned items.
5661 const ItemBaselineData
& item
= aBaselineItems
[i
];
5662 currentTrack
= item
.mBaselineTrack
;
5663 trackStartIndex
= i
;
5664 maxBaseline
= item
.mBaseline
;
5665 maxDescent
= item
.mSize
- item
.mBaseline
;
5669 void nsGridContainerFrame::Tracks::InitializeItemBaselines(
5670 GridReflowInput
& aState
, nsTArray
<GridItemInfo
>& aGridItems
) {
5671 MOZ_ASSERT(!mIsMasonry
);
5672 if (aState
.mFrame
->IsSubgrid(mAxis
)) {
5673 // A grid container's subgridded axis doesn't have a baseline.
5676 nsTArray
<ItemBaselineData
> firstBaselineItems
;
5677 nsTArray
<ItemBaselineData
> lastBaselineItems
;
5678 WritingMode wm
= aState
.mWM
;
5679 ComputedStyle
* containerSC
= aState
.mFrame
->Style();
5680 for (GridItemInfo
& gridItem
: aGridItems
) {
5681 if (gridItem
.IsSubgrid(mAxis
)) {
5682 // A subgrid itself is never baseline-aligned.
5685 nsIFrame
* child
= gridItem
.mFrame
;
5686 uint32_t baselineTrack
= kAutoLine
;
5687 auto state
= ItemState(0);
5688 auto childWM
= child
->GetWritingMode();
5689 const bool isOrthogonal
= wm
.IsOrthogonalTo(childWM
);
5690 const bool isInlineAxis
= mAxis
== eLogicalAxisInline
; // i.e. columns
5691 // XXX update the line below to include orthogonal grid/table boxes
5692 // XXX since they have baselines in both dimensions. And flexbox with
5693 // XXX reversed main/cross axis?
5694 const bool itemHasBaselineParallelToTrack
= isInlineAxis
== isOrthogonal
;
5695 if (itemHasBaselineParallelToTrack
) {
5696 // [align|justify]-self:[last ]baseline.
5697 auto selfAlignment
=
5698 isOrthogonal
? child
->StylePosition()->UsedJustifySelf(containerSC
)._0
5699 : child
->StylePosition()->UsedAlignSelf(containerSC
)._0
;
5700 selfAlignment
&= ~StyleAlignFlags::FLAG_BITS
;
5701 if (selfAlignment
== StyleAlignFlags::BASELINE
) {
5702 state
|= ItemState::eFirstBaseline
| ItemState::eSelfBaseline
;
5703 const GridArea
& area
= gridItem
.mArea
;
5704 baselineTrack
= isInlineAxis
? area
.mCols
.mStart
: area
.mRows
.mStart
;
5705 } else if (selfAlignment
== StyleAlignFlags::LAST_BASELINE
) {
5706 state
|= ItemState::eLastBaseline
| ItemState::eSelfBaseline
;
5707 const GridArea
& area
= gridItem
.mArea
;
5708 baselineTrack
= (isInlineAxis
? area
.mCols
.mEnd
: area
.mRows
.mEnd
) - 1;
5711 // [align|justify]-content:[last ]baseline.
5712 // https://drafts.csswg.org/css-align-3/#baseline-align-content
5713 // "[...] and its computed 'align-self' or 'justify-self' (whichever
5714 // affects its block axis) is 'stretch' or 'self-start' ('self-end').
5715 // For this purpose, the 'start', 'end', 'flex-start', and 'flex-end'
5716 // values of 'align-self' are treated as either 'self-start' or
5717 // 'self-end', whichever they end up equivalent to.
5718 auto alignContent
= child
->StylePosition()->mAlignContent
.primary
;
5719 alignContent
&= ~StyleAlignFlags::FLAG_BITS
;
5720 if (alignContent
== StyleAlignFlags::BASELINE
||
5721 alignContent
== StyleAlignFlags::LAST_BASELINE
) {
5722 const auto selfAlignEdge
= alignContent
== StyleAlignFlags::BASELINE
5723 ? StyleAlignFlags::SELF_START
5724 : StyleAlignFlags::SELF_END
;
5725 bool validCombo
= selfAlignment
== StyleAlignFlags::NORMAL
||
5726 selfAlignment
== StyleAlignFlags::STRETCH
||
5727 selfAlignment
== selfAlignEdge
;
5729 // We're doing alignment in the axis that's orthogonal to mAxis here.
5730 LogicalAxis alignAxis
= GetOrthogonalAxis(mAxis
);
5731 // |sameSide| is true if the container's start side in this axis is
5732 // the same as the child's start side, in the child's parallel axis.
5733 bool sameSide
= wm
.ParallelAxisStartsOnSameSide(alignAxis
, childWM
);
5734 if (selfAlignment
== StyleAlignFlags::LEFT
) {
5735 selfAlignment
= !isInlineAxis
|| wm
.IsBidiLTR()
5736 ? StyleAlignFlags::START
5737 : StyleAlignFlags::END
;
5738 } else if (selfAlignment
== StyleAlignFlags::RIGHT
) {
5739 selfAlignment
= isInlineAxis
&& wm
.IsBidiLTR()
5740 ? StyleAlignFlags::END
5741 : StyleAlignFlags::START
;
5744 if (selfAlignment
== StyleAlignFlags::START
||
5745 selfAlignment
== StyleAlignFlags::FLEX_START
) {
5747 sameSide
== (alignContent
== StyleAlignFlags::BASELINE
);
5748 } else if (selfAlignment
== StyleAlignFlags::END
||
5749 selfAlignment
== StyleAlignFlags::FLEX_END
) {
5751 sameSide
== (alignContent
== StyleAlignFlags::LAST_BASELINE
);
5755 const GridArea
& area
= gridItem
.mArea
;
5756 if (alignContent
== StyleAlignFlags::BASELINE
) {
5757 state
|= ItemState::eFirstBaseline
| ItemState::eContentBaseline
;
5759 isInlineAxis
? area
.mCols
.mStart
: area
.mRows
.mStart
;
5760 } else if (alignContent
== StyleAlignFlags::LAST_BASELINE
) {
5761 state
|= ItemState::eLastBaseline
| ItemState::eContentBaseline
;
5763 (isInlineAxis
? area
.mCols
.mEnd
: area
.mRows
.mEnd
) - 1;
5769 if (state
& ItemState::eIsBaselineAligned
) {
5770 // XXXmats if |child| is a descendant of a subgrid then the metrics
5771 // below needs to account for the accumulated MPB somehow...
5773 // XXX available size issue
5774 LogicalSize
avail(childWM
, INFINITE_ISIZE_COORD
, NS_UNCONSTRAINEDSIZE
);
5775 auto* rc
= &aState
.mRenderingContext
;
5776 // XXX figure out if we can avoid/merge this reflow with the main reflow.
5777 // XXX (after bug 1174569 is sorted out)
5779 // XXX How should we handle percentage padding here? (bug 1330866)
5780 // XXX (see ::ContentContribution and how it deals with percentages)
5781 // XXX What if the true baseline after line-breaking differs from this
5782 // XXX hypothetical baseline based on an infinite inline size?
5783 // XXX Maybe we should just call ::ContentContribution here instead?
5784 // XXX For now we just pass an unconstrined-bsize CB:
5785 LogicalSize
cbSize(childWM
, 0, NS_UNCONSTRAINEDSIZE
);
5786 ::MeasuringReflow(child
, aState
.mReflowInput
, rc
, avail
, cbSize
);
5788 nsGridContainerFrame
* grid
= do_QueryFrame(child
);
5789 if (state
& ItemState::eFirstBaseline
) {
5791 if (isOrthogonal
== isInlineAxis
) {
5792 baseline
= grid
->GetBBaseline(BaselineSharingGroup::First
);
5794 baseline
= grid
->GetIBaseline(BaselineSharingGroup::First
);
5797 if (grid
|| nsLayoutUtils::GetFirstLineBaseline(wm
, child
, &baseline
)) {
5798 NS_ASSERTION(baseline
!= NS_INTRINSIC_ISIZE_UNKNOWN
,
5799 "about to use an unknown baseline");
5800 auto frameSize
= isInlineAxis
? child
->ISize(wm
) : child
->BSize(wm
);
5801 auto m
= child
->GetLogicalUsedMargin(wm
);
5802 baseline
+= isInlineAxis
? m
.IStart(wm
) : m
.BStart(wm
);
5804 frameSize
+ (isInlineAxis
? m
.IStartEnd(wm
) : m
.BStartEnd(wm
));
5805 firstBaselineItems
.AppendElement(ItemBaselineData(
5806 {baselineTrack
, baseline
, alignSize
, &gridItem
}));
5808 state
&= ~ItemState::eAllBaselineBits
;
5812 if (isOrthogonal
== isInlineAxis
) {
5813 baseline
= grid
->GetBBaseline(BaselineSharingGroup::Last
);
5815 baseline
= grid
->GetIBaseline(BaselineSharingGroup::Last
);
5818 if (grid
|| nsLayoutUtils::GetLastLineBaseline(wm
, child
, &baseline
)) {
5819 NS_ASSERTION(baseline
!= NS_INTRINSIC_ISIZE_UNKNOWN
,
5820 "about to use an unknown baseline");
5821 auto frameSize
= isInlineAxis
? child
->ISize(wm
) : child
->BSize(wm
);
5822 auto m
= child
->GetLogicalUsedMargin(wm
);
5824 // Convert to distance from border-box end.
5825 baseline
= frameSize
- baseline
;
5827 auto descent
= baseline
+ (isInlineAxis
? m
.IEnd(wm
) : m
.BEnd(wm
));
5829 frameSize
+ (isInlineAxis
? m
.IStartEnd(wm
) : m
.BStartEnd(wm
));
5830 lastBaselineItems
.AppendElement(
5831 ItemBaselineData({baselineTrack
, descent
, alignSize
, &gridItem
}));
5832 state
|= ItemState::eEndSideBaseline
;
5834 state
&= ~ItemState::eAllBaselineBits
;
5839 (state
& (ItemState::eFirstBaseline
| ItemState::eLastBaseline
)) !=
5840 (ItemState::eFirstBaseline
| ItemState::eLastBaseline
),
5841 "first/last baseline bits are mutually exclusive");
5843 (state
& (ItemState::eSelfBaseline
| ItemState::eContentBaseline
)) !=
5844 (ItemState::eSelfBaseline
| ItemState::eContentBaseline
),
5845 "*-self and *-content baseline bits are mutually exclusive");
5847 !(state
& (ItemState::eFirstBaseline
| ItemState::eLastBaseline
)) ==
5848 !(state
& (ItemState::eSelfBaseline
| ItemState::eContentBaseline
)),
5849 "first/last bit requires self/content bit and vice versa");
5850 gridItem
.mState
[mAxis
] |= state
;
5851 gridItem
.mBaselineOffset
[mAxis
] = nscoord(0);
5854 if (firstBaselineItems
.IsEmpty() && lastBaselineItems
.IsEmpty()) {
5858 // TODO: CSS Align spec issue - how to align a baseline subtree in a track?
5859 // https://lists.w3.org/Archives/Public/www-style/2016May/0141.html
5860 mBaselineSubtreeAlign
[BaselineSharingGroup::First
] = StyleAlignFlags::START
;
5861 mBaselineSubtreeAlign
[BaselineSharingGroup::Last
] = StyleAlignFlags::END
;
5863 CalculateItemBaselines(firstBaselineItems
, BaselineSharingGroup::First
);
5864 CalculateItemBaselines(lastBaselineItems
, BaselineSharingGroup::Last
);
5867 // TODO: we store the wrong baseline group offset in some cases (bug 1632200)
5868 void nsGridContainerFrame::Tracks::InitializeItemBaselinesInMasonryAxis(
5869 GridReflowInput
& aState
, nsTArray
<GridItemInfo
>& aGridItems
,
5870 BaselineAlignmentSet aSet
, const nsSize
& aContainerSize
,
5871 nsTArray
<nscoord
>& aTrackSizes
,
5872 nsTArray
<ItemBaselineData
>& aFirstBaselineItems
,
5873 nsTArray
<ItemBaselineData
>& aLastBaselineItems
) {
5874 MOZ_ASSERT(mIsMasonry
);
5875 WritingMode wm
= aState
.mWM
;
5876 ComputedStyle
* containerSC
= aState
.mFrame
->Style();
5877 for (GridItemInfo
& gridItem
: aGridItems
) {
5878 if (gridItem
.IsSubgrid(mAxis
)) {
5879 // A subgrid itself is never baseline-aligned.
5882 const auto& area
= gridItem
.mArea
;
5883 if (aSet
.mItemSet
== BaselineAlignmentSet::LastItems
) {
5884 // NOTE: eIsLastItemInMasonryTrack is set also if the item is the ONLY
5885 // item in its track; the eIsBaselineAligned check excludes it though
5886 // since it participates in the start baseline groups in that case.
5888 // XXX what if it's the only item in THAT baseline group?
5889 // XXX should it participate in the last-item group instead then
5890 // if there are more baseline-aligned items there?
5891 if (!(gridItem
.mState
[mAxis
] & ItemState::eIsLastItemInMasonryTrack
) ||
5892 (gridItem
.mState
[mAxis
] & ItemState::eIsBaselineAligned
)) {
5896 if (area
.LineRangeForAxis(mAxis
).mStart
> 0 ||
5897 (gridItem
.mState
[mAxis
] & ItemState::eIsBaselineAligned
)) {
5903 ->UsedTracksAlignment(
5904 mAxis
, area
.LineRangeForAxis(GetOrthogonalAxis(mAxis
)).mStart
)
5906 if (!aSet
.MatchTrackAlignment(trackAlign
)) {
5910 nsIFrame
* child
= gridItem
.mFrame
;
5911 uint32_t baselineTrack
= kAutoLine
;
5912 auto state
= ItemState(0);
5913 auto childWM
= child
->GetWritingMode();
5914 const bool isOrthogonal
= wm
.IsOrthogonalTo(childWM
);
5915 const bool isInlineAxis
= mAxis
== eLogicalAxisInline
; // i.e. columns
5916 // XXX update the line below to include orthogonal grid/table boxes
5917 // XXX since they have baselines in both dimensions. And flexbox with
5918 // XXX reversed main/cross axis?
5919 const bool itemHasBaselineParallelToTrack
= isInlineAxis
== isOrthogonal
;
5920 if (itemHasBaselineParallelToTrack
) {
5921 const auto* pos
= child
->StylePosition();
5922 // [align|justify]-self:[last ]baseline.
5923 auto selfAlignment
= pos
->UsedSelfAlignment(mAxis
, containerSC
);
5924 selfAlignment
&= ~StyleAlignFlags::FLAG_BITS
;
5925 if (selfAlignment
== StyleAlignFlags::BASELINE
) {
5926 state
|= ItemState::eFirstBaseline
| ItemState::eSelfBaseline
;
5927 baselineTrack
= isInlineAxis
? area
.mCols
.mStart
: area
.mRows
.mStart
;
5928 } else if (selfAlignment
== StyleAlignFlags::LAST_BASELINE
) {
5929 state
|= ItemState::eLastBaseline
| ItemState::eSelfBaseline
;
5930 baselineTrack
= (isInlineAxis
? area
.mCols
.mEnd
: area
.mRows
.mEnd
) - 1;
5932 // [align|justify]-content:[last ]baseline.
5933 auto childAxis
= isOrthogonal
? GetOrthogonalAxis(mAxis
) : mAxis
;
5934 auto alignContent
= pos
->UsedContentAlignment(childAxis
).primary
;
5935 alignContent
&= ~StyleAlignFlags::FLAG_BITS
;
5936 if (alignContent
== StyleAlignFlags::BASELINE
) {
5937 state
|= ItemState::eFirstBaseline
| ItemState::eContentBaseline
;
5938 baselineTrack
= isInlineAxis
? area
.mCols
.mStart
: area
.mRows
.mStart
;
5939 } else if (alignContent
== StyleAlignFlags::LAST_BASELINE
) {
5940 state
|= ItemState::eLastBaseline
| ItemState::eContentBaseline
;
5942 (isInlineAxis
? area
.mCols
.mEnd
: area
.mRows
.mEnd
) - 1;
5947 if (state
& ItemState::eIsBaselineAligned
) {
5948 // XXXmats if |child| is a descendant of a subgrid then the metrics
5949 // below needs to account for the accumulated MPB somehow...
5952 nsGridContainerFrame
* grid
= do_QueryFrame(child
);
5953 if (state
& ItemState::eFirstBaseline
) {
5955 if (isOrthogonal
== isInlineAxis
) {
5956 baseline
= grid
->GetBBaseline(BaselineSharingGroup::First
);
5958 baseline
= grid
->GetIBaseline(BaselineSharingGroup::First
);
5961 if (grid
|| nsLayoutUtils::GetFirstLineBaseline(wm
, child
, &baseline
)) {
5962 NS_ASSERTION(baseline
!= NS_INTRINSIC_ISIZE_UNKNOWN
,
5963 "about to use an unknown baseline");
5964 auto frameSize
= isInlineAxis
? child
->ISize(wm
) : child
->BSize(wm
);
5967 child
->GetLogicalNormalPosition(wm
, aContainerSize
);
5968 baseline
+= pos
.Pos(mAxis
, wm
);
5969 if (aSet
.mTrackAlignmentSet
== BaselineAlignmentSet::EndStretch
) {
5970 state
|= ItemState::eEndSideBaseline
;
5971 // Convert to distance from the track end.
5973 aTrackSizes
[gridItem
.mArea
5974 .LineRangeForAxis(GetOrthogonalAxis(mAxis
))
5978 alignSize
= frameSize
;
5979 aFirstBaselineItems
.AppendElement(ItemBaselineData(
5980 {baselineTrack
, baseline
, alignSize
, &gridItem
}));
5982 state
&= ~ItemState::eAllBaselineBits
;
5986 if (isOrthogonal
== isInlineAxis
) {
5987 baseline
= grid
->GetBBaseline(BaselineSharingGroup::Last
);
5989 baseline
= grid
->GetIBaseline(BaselineSharingGroup::Last
);
5992 if (grid
|| nsLayoutUtils::GetLastLineBaseline(wm
, child
, &baseline
)) {
5993 NS_ASSERTION(baseline
!= NS_INTRINSIC_ISIZE_UNKNOWN
,
5994 "about to use an unknown baseline");
5995 auto frameSize
= isInlineAxis
? child
->ISize(wm
) : child
->BSize(wm
);
5996 auto m
= child
->GetLogicalUsedMargin(wm
);
5998 aSet
.mTrackAlignmentSet
== BaselineAlignmentSet::EndStretch
) {
5999 // Convert to distance from border-box end.
6000 state
|= ItemState::eEndSideBaseline
;
6002 child
->GetLogicalNormalPosition(wm
, aContainerSize
);
6003 baseline
+= pos
.Pos(mAxis
, wm
);
6005 aTrackSizes
[gridItem
.mArea
6006 .LineRangeForAxis(GetOrthogonalAxis(mAxis
))
6009 } else if (grid
&& aSet
.mTrackAlignmentSet
==
6010 BaselineAlignmentSet::StartStretch
) {
6011 // Convert to distance from border-box start.
6012 baseline
= frameSize
- baseline
;
6014 if (aSet
.mItemSet
== BaselineAlignmentSet::LastItems
&&
6015 aSet
.mTrackAlignmentSet
== BaselineAlignmentSet::StartStretch
) {
6017 child
->GetLogicalNormalPosition(wm
, aContainerSize
);
6018 baseline
+= pos
.B(wm
);
6020 if (aSet
.mTrackAlignmentSet
== BaselineAlignmentSet::EndStretch
) {
6021 state
|= ItemState::eEndSideBaseline
;
6024 baseline
+ ((state
& ItemState::eEndSideBaseline
)
6025 ? (isInlineAxis
? m
.IEnd(wm
) : m
.BEnd(wm
))
6026 : (isInlineAxis
? m
.IStart(wm
) : m
.BStart(wm
)));
6028 frameSize
+ (isInlineAxis
? m
.IStartEnd(wm
) : m
.BStartEnd(wm
));
6029 aLastBaselineItems
.AppendElement(
6030 ItemBaselineData({baselineTrack
, descent
, alignSize
, &gridItem
}));
6032 state
&= ~ItemState::eAllBaselineBits
;
6037 (state
& (ItemState::eFirstBaseline
| ItemState::eLastBaseline
)) !=
6038 (ItemState::eFirstBaseline
| ItemState::eLastBaseline
),
6039 "first/last baseline bits are mutually exclusive");
6041 (state
& (ItemState::eSelfBaseline
| ItemState::eContentBaseline
)) !=
6042 (ItemState::eSelfBaseline
| ItemState::eContentBaseline
),
6043 "*-self and *-content baseline bits are mutually exclusive");
6045 !(state
& (ItemState::eFirstBaseline
| ItemState::eLastBaseline
)) ==
6046 !(state
& (ItemState::eSelfBaseline
| ItemState::eContentBaseline
)),
6047 "first/last bit requires self/content bit and vice versa");
6048 gridItem
.mState
[mAxis
] |= state
;
6049 gridItem
.mBaselineOffset
[mAxis
] = nscoord(0);
6052 CalculateItemBaselines(aFirstBaselineItems
, BaselineSharingGroup::First
);
6053 CalculateItemBaselines(aLastBaselineItems
, BaselineSharingGroup::Last
);
6055 // TODO: make sure the mBaselines (i.e. the baselines we export from
6056 // the grid container) are offset from the correct container edge.
6057 // Also, which of the baselines do we pick to export exactly?
6059 MOZ_ASSERT(aFirstBaselineItems
.Length() != 1 ||
6060 aFirstBaselineItems
[0].mGridItem
->mBaselineOffset
[mAxis
] == 0,
6061 "a baseline group that contains only one item should not "
6062 "produce a non-zero item baseline offset");
6063 MOZ_ASSERT(aLastBaselineItems
.Length() != 1 ||
6064 aLastBaselineItems
[0].mGridItem
->mBaselineOffset
[mAxis
] == 0,
6065 "a baseline group that contains only one item should not "
6066 "produce a non-zero item baseline offset");
6069 void nsGridContainerFrame::Tracks::AlignBaselineSubtree(
6070 const GridItemInfo
& aGridItem
) const {
6074 auto state
= aGridItem
.mState
[mAxis
];
6075 if (!(state
& ItemState::eIsBaselineAligned
)) {
6078 const GridArea
& area
= aGridItem
.mArea
;
6079 int32_t baselineTrack
;
6080 const bool isFirstBaseline
= state
& ItemState::eFirstBaseline
;
6081 if (isFirstBaseline
) {
6083 mAxis
== eLogicalAxisBlock
? area
.mRows
.mStart
: area
.mCols
.mStart
;
6086 (mAxis
== eLogicalAxisBlock
? area
.mRows
.mEnd
: area
.mCols
.mEnd
) - 1;
6088 const TrackSize
& sz
= mSizes
[baselineTrack
];
6089 auto baselineGroup
= isFirstBaseline
? BaselineSharingGroup::First
6090 : BaselineSharingGroup::Last
;
6091 nscoord delta
= sz
.mBase
- sz
.mBaselineSubtreeSize
[baselineGroup
];
6092 const auto subtreeAlign
= mBaselineSubtreeAlign
[baselineGroup
];
6093 if (subtreeAlign
== StyleAlignFlags::START
) {
6094 if (state
& ItemState::eLastBaseline
) {
6095 aGridItem
.mBaselineOffset
[mAxis
] += delta
;
6097 } else if (subtreeAlign
== StyleAlignFlags::END
) {
6098 if (isFirstBaseline
) {
6099 aGridItem
.mBaselineOffset
[mAxis
] += delta
;
6101 } else if (subtreeAlign
== StyleAlignFlags::CENTER
) {
6102 aGridItem
.mBaselineOffset
[mAxis
] += delta
/ 2;
6104 MOZ_ASSERT_UNREACHABLE("unexpected baseline subtree alignment");
6108 template <nsGridContainerFrame::Tracks::TrackSizingPhase phase
>
6109 bool nsGridContainerFrame::Tracks::GrowSizeForSpanningItems(
6110 nsTArray
<SpanningItemData
>::iterator aIter
,
6111 nsTArray
<SpanningItemData
>::iterator aIterEnd
, nsTArray
<uint32_t>& aTracks
,
6112 nsTArray
<TrackSize
>& aPlan
, nsTArray
<TrackSize
>& aItemPlan
,
6113 TrackSize::StateBits aSelector
, const FitContentClamper
& aFitContentClamper
,
6114 bool aNeedInfinitelyGrowableFlag
) {
6115 constexpr bool isMaxSizingPhase
=
6116 phase
== TrackSizingPhase::IntrinsicMaximums
||
6117 phase
== TrackSizingPhase::MaxContentMaximums
;
6118 bool needToUpdateSizes
= false;
6119 InitializePlan
<phase
>(aPlan
);
6120 for (; aIter
!= aIterEnd
; ++aIter
) {
6121 const SpanningItemData
& item
= *aIter
;
6122 if (!(item
.mState
& aSelector
)) {
6125 if (isMaxSizingPhase
) {
6126 for (auto i
: item
.mLineRange
.Range()) {
6127 aPlan
[i
].mState
|= TrackSize::eModified
;
6130 nscoord space
= item
.SizeContributionForPhase
<phase
>();
6134 aTracks
.ClearAndRetainStorage();
6135 space
= CollectGrowable
<phase
>(space
, item
.mLineRange
, aSelector
, aTracks
);
6137 DistributeToTrackSizes
<phase
>(space
, aPlan
, aItemPlan
, aTracks
, aSelector
,
6138 aFitContentClamper
);
6139 needToUpdateSizes
= true;
6142 if (isMaxSizingPhase
) {
6143 needToUpdateSizes
= true;
6145 if (needToUpdateSizes
) {
6146 CopyPlanToSize
<phase
>(aPlan
, aNeedInfinitelyGrowableFlag
);
6148 return needToUpdateSizes
;
6151 void nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
6152 GridReflowInput
& aState
, nsTArray
<GridItemInfo
>& aGridItems
,
6153 const TrackSizingFunctions
& aFunctions
, LineRange
GridArea::*aRange
,
6154 nscoord aPercentageBasis
, SizingConstraint aConstraint
) {
6155 // Resolve Intrinsic Track Sizes
6156 // https://w3c.github.io/csswg-drafts/css-grid-1/#algo-content
6157 // We're also setting eIsFlexing on the item state here to speed up
6158 // FindUsedFlexFraction later.
6160 gfxContext
* rc
= &aState
.mRenderingContext
;
6161 WritingMode wm
= aState
.mWM
;
6163 // Data we accumulate when grouping similar sized spans together.
6164 struct PerSpanData
{
6165 uint32_t mItemCountWithSameSpan
= 0;
6166 TrackSize::StateBits mStateBits
= TrackSize::StateBits
{0};
6168 AutoTArray
<PerSpanData
, 16> perSpanData
;
6170 nsTArray
<SpanningItemData
> spanningItems
;
6171 uint32_t maxSpan
= 0; // max span of items in `spanningItems`.
6173 // Setup track selector for step 3.2:
6174 const auto contentBasedMinSelector
=
6175 aConstraint
== SizingConstraint::MinContent
6176 ? TrackSize::eIntrinsicMinSizing
6177 : TrackSize::eMinOrMaxContentMinSizing
;
6179 // Setup track selector for step 3.3:
6180 const auto maxContentMinSelector
=
6181 aConstraint
== SizingConstraint::MaxContent
6182 ? (TrackSize::eMaxContentMinSizing
| TrackSize::eAutoMinSizing
)
6183 : TrackSize::eMaxContentMinSizing
;
6185 const auto orthogonalAxis
= GetOrthogonalAxis(mAxis
);
6186 const bool isMasonryInOtherAxis
= aState
.mFrame
->IsMasonry(orthogonalAxis
);
6188 for (auto& gridItem
: aGridItems
) {
6189 MOZ_ASSERT(!(gridItem
.mState
[mAxis
] &
6190 (ItemState::eApplyAutoMinSize
| ItemState::eIsFlexing
|
6191 ItemState::eClampMarginBoxMinSize
)),
6192 "Why are any of these bits set already?");
6194 const GridArea
& area
= gridItem
.mArea
;
6195 const LineRange
& lineRange
= area
.*aRange
;
6197 // If we have masonry layout in the other axis then skip this item unless
6198 // it's in the first masonry track, or has definite placement in this axis,
6199 // or spans all tracks in this axis (since that implies it will be placed
6200 // at line 1 regardless of layout results of other items).
6201 if (isMasonryInOtherAxis
&&
6202 gridItem
.mArea
.LineRangeForAxis(orthogonalAxis
).mStart
!= 0 &&
6203 (gridItem
.mState
[mAxis
] & ItemState::eAutoPlacement
) &&
6204 gridItem
.mArea
.LineRangeForAxis(mAxis
).Extent() != mSizes
.Length()) {
6208 uint32_t span
= lineRange
.Extent();
6209 if (MOZ_UNLIKELY(gridItem
.mState
[mAxis
] & ItemState::eIsSubgrid
)) {
6210 auto itemWM
= gridItem
.mFrame
->GetWritingMode();
6211 auto percentageBasis
= aState
.PercentageBasisFor(mAxis
, gridItem
);
6213 if (percentageBasis
.ISize(itemWM
) == NS_UNCONSTRAINEDSIZE
) {
6214 percentageBasis
.ISize(itemWM
) = nscoord(0);
6217 if (percentageBasis
.BSize(itemWM
) == NS_UNCONSTRAINEDSIZE
) {
6218 percentageBasis
.BSize(itemWM
) = nscoord(0);
6222 SubgridComputeMarginBorderPadding(gridItem
, percentageBasis
);
6223 LogicalMargin mbp
= SubgridAccumulatedMarginBorderPadding(
6224 gridItem
.SubgridFrame(), subgrid
, wm
, mAxis
);
6227 AddSubgridContribution(mSizes
[lineRange
.mStart
],
6228 mbp
.StartEnd(mAxis
, wm
));
6230 AddSubgridContribution(mSizes
[lineRange
.mStart
], mbp
.Start(mAxis
, wm
));
6231 AddSubgridContribution(mSizes
[lineRange
.mEnd
- 1], mbp
.End(mAxis
, wm
));
6237 // Step 2. Size tracks to fit non-spanning items.
6238 if (ResolveIntrinsicSizeForNonSpanningItems(aState
, aFunctions
,
6239 aPercentageBasis
, aConstraint
,
6240 lineRange
, gridItem
)) {
6241 gridItem
.mState
[mAxis
] |= ItemState::eIsFlexing
;
6244 TrackSize::StateBits state
= StateBitsForRange(lineRange
);
6246 // Check if we need to apply "Automatic Minimum Size" and cache it.
6247 if ((state
& TrackSize::eAutoMinSizing
) &&
6248 !(state
& TrackSize::eFlexMaxSizing
) &&
6249 gridItem
.ShouldApplyAutoMinSize(wm
, mAxis
, aPercentageBasis
)) {
6250 gridItem
.mState
[mAxis
] |= ItemState::eApplyAutoMinSize
;
6253 if (state
& TrackSize::eFlexMaxSizing
) {
6254 gridItem
.mState
[mAxis
] |= ItemState::eIsFlexing
;
6255 } else if (state
& (TrackSize::eIntrinsicMinSizing
|
6256 TrackSize::eIntrinsicMaxSizing
)) {
6257 // Collect data for Step 3.
6258 maxSpan
= std::max(maxSpan
, span
);
6259 if (span
>= perSpanData
.Length()) {
6260 perSpanData
.SetLength(2 * span
);
6263 perSpanData
[span
].mItemCountWithSameSpan
++;
6264 perSpanData
[span
].mStateBits
|= state
;
6266 CachedIntrinsicSizes cache
;
6268 // Calculate data for "Automatic Minimum Size" clamping, if needed.
6269 if (TrackSize::IsDefiniteMaxSizing(state
) &&
6270 (gridItem
.mState
[mAxis
] & ItemState::eApplyAutoMinSize
)) {
6271 nscoord minSizeClamp
= 0;
6272 for (auto i
: lineRange
.Range()) {
6273 minSizeClamp
+= aFunctions
.MaxSizingFor(i
).AsBreadth().Resolve(
6276 minSizeClamp
+= mGridGap
* (span
- 1);
6277 cache
.mMinSizeClamp
= minSizeClamp
;
6278 gridItem
.mState
[mAxis
] |= ItemState::eClampMarginBoxMinSize
;
6281 // Collect the various grid item size contributions we need.
6282 nscoord minSize
= 0;
6283 if (state
& TrackSize::eIntrinsicMinSizing
) { // for 3.1
6284 minSize
= MinSize(gridItem
, aState
, rc
, wm
, mAxis
, &cache
);
6286 nscoord minContent
= 0;
6287 if (state
& (contentBasedMinSelector
| // for 3.2
6288 TrackSize::eIntrinsicMaxSizing
)) { // for 3.5
6290 MinContentContribution(gridItem
, aState
, rc
, wm
, mAxis
, &cache
);
6292 nscoord maxContent
= 0;
6293 if (state
& (maxContentMinSelector
| // for 3.3
6294 TrackSize::eAutoOrMaxContentMaxSizing
)) { // for 3.6
6296 MaxContentContribution(gridItem
, aState
, rc
, wm
, mAxis
, &cache
);
6299 spanningItems
.AppendElement(
6300 SpanningItemData({span
, state
, lineRange
, minSize
, minContent
,
6301 maxContent
, gridItem
.mFrame
}));
6305 MOZ_ASSERT(!(gridItem
.mState
[mAxis
] & ItemState::eClampMarginBoxMinSize
) ||
6306 (gridItem
.mState
[mAxis
] & ItemState::eApplyAutoMinSize
),
6307 "clamping only applies to Automatic Minimum Size");
6310 // Step 3 - Increase sizes to accommodate spanning items crossing
6311 // content-sized tracks.
6313 auto fitContentClamper
= [&aFunctions
, aPercentageBasis
](uint32_t aTrack
,
6316 nscoord fitContentLimit
= ::ResolveToDefiniteSize(
6317 aFunctions
.MaxSizingFor(aTrack
), aPercentageBasis
);
6318 if (*aSize
> fitContentLimit
) {
6319 *aSize
= std::max(aMinSize
, fitContentLimit
);
6325 // Sort the collected items on span length, shortest first. There's no need
6326 // for a stable sort here since the sizing isn't order dependent within
6327 // a group of items with the same span length.
6328 std::sort(spanningItems
.begin(), spanningItems
.end(),
6329 SpanningItemData::IsSpanLessThan
);
6331 nsTArray
<uint32_t> tracks(maxSpan
);
6332 nsTArray
<TrackSize
> plan(mSizes
.Length());
6333 plan
.SetLength(mSizes
.Length());
6334 nsTArray
<TrackSize
> itemPlan(mSizes
.Length());
6335 itemPlan
.SetLength(mSizes
.Length());
6336 // Start / end iterator for items of the same span length:
6337 auto spanGroupStart
= spanningItems
.begin();
6338 auto spanGroupEnd
= spanGroupStart
;
6339 const auto end
= spanningItems
.end();
6340 for (; spanGroupStart
!= end
; spanGroupStart
= spanGroupEnd
) {
6341 const uint32_t span
= spanGroupStart
->mSpan
;
6342 spanGroupEnd
= spanGroupStart
+ perSpanData
[span
].mItemCountWithSameSpan
;
6343 TrackSize::StateBits stateBitsForSpan
= perSpanData
[span
].mStateBits
;
6344 bool updatedBase
= false; // Did we update any mBase in step 3.1..3.3?
6345 TrackSize::StateBits
selector(TrackSize::eIntrinsicMinSizing
);
6346 if (stateBitsForSpan
& selector
) {
6347 // Step 3.1 MinSize to intrinsic min-sizing.
6349 GrowSizeForSpanningItems
<TrackSizingPhase::IntrinsicMinimums
>(
6350 spanGroupStart
, spanGroupEnd
, tracks
, plan
, itemPlan
, selector
);
6353 selector
= contentBasedMinSelector
;
6354 if (stateBitsForSpan
& selector
) {
6355 // Step 3.2 MinContentContribution to min-/max-content (and 'auto' when
6356 // sizing under a min-content constraint) min-sizing.
6358 GrowSizeForSpanningItems
<TrackSizingPhase::ContentBasedMinimums
>(
6359 spanGroupStart
, spanGroupEnd
, tracks
, plan
, itemPlan
, selector
);
6362 selector
= maxContentMinSelector
;
6363 if (stateBitsForSpan
& selector
) {
6364 // Step 3.3 MaxContentContribution to max-content (and 'auto' when
6365 // sizing under a max-content constraint) min-sizing.
6367 GrowSizeForSpanningItems
<TrackSizingPhase::MaxContentMinimums
>(
6368 spanGroupStart
, spanGroupEnd
, tracks
, plan
, itemPlan
, selector
);
6373 for (TrackSize
& sz
: mSizes
) {
6374 if (sz
.mBase
> sz
.mLimit
) {
6375 sz
.mLimit
= sz
.mBase
;
6380 selector
= TrackSize::eIntrinsicMaxSizing
;
6381 if (stateBitsForSpan
& selector
) {
6382 const bool willRunStep3_6
=
6383 stateBitsForSpan
& TrackSize::eAutoOrMaxContentMaxSizing
;
6384 // Step 3.5 MinContentContribution to intrinsic max-sizing.
6385 GrowSizeForSpanningItems
<TrackSizingPhase::IntrinsicMaximums
>(
6386 spanGroupStart
, spanGroupEnd
, tracks
, plan
, itemPlan
, selector
,
6387 fitContentClamper
, willRunStep3_6
);
6389 if (willRunStep3_6
) {
6390 // Step 2.6 MaxContentContribution to max-content max-sizing.
6391 selector
= TrackSize::eAutoOrMaxContentMaxSizing
;
6392 GrowSizeForSpanningItems
<TrackSizingPhase::MaxContentMaximums
>(
6393 spanGroupStart
, spanGroupEnd
, tracks
, plan
, itemPlan
, selector
,
6400 // Step 5 - If any track still has an infinite growth limit, set its growth
6401 // limit to its base size.
6402 for (TrackSize
& sz
: mSizes
) {
6403 if (sz
.mLimit
== NS_UNCONSTRAINEDSIZE
) {
6404 sz
.mLimit
= sz
.mBase
;
6409 float nsGridContainerFrame::Tracks::FindFrUnitSize(
6410 const LineRange
& aRange
, const nsTArray
<uint32_t>& aFlexTracks
,
6411 const TrackSizingFunctions
& aFunctions
, nscoord aSpaceToFill
) const {
6412 MOZ_ASSERT(aSpaceToFill
> 0 && !aFlexTracks
.IsEmpty());
6413 float flexFactorSum
= 0.0f
;
6414 nscoord leftOverSpace
= aSpaceToFill
;
6415 for (auto i
: aRange
.Range()) {
6416 const TrackSize
& sz
= mSizes
[i
];
6417 if (sz
.mState
& TrackSize::eFlexMaxSizing
) {
6418 flexFactorSum
+= aFunctions
.MaxSizingFor(i
).AsFr();
6420 leftOverSpace
-= sz
.mBase
;
6421 if (leftOverSpace
<= 0) {
6427 float hypotheticalFrSize
;
6428 nsTArray
<uint32_t> flexTracks(aFlexTracks
.Clone());
6429 uint32_t numFlexTracks
= flexTracks
.Length();
6432 hypotheticalFrSize
= leftOverSpace
/ std::max(flexFactorSum
, 1.0f
);
6433 for (uint32_t i
= 0, len
= flexTracks
.Length(); i
< len
; ++i
) {
6434 uint32_t track
= flexTracks
[i
];
6435 if (track
== kAutoLine
) {
6436 continue; // Track marked as inflexible in a prev. iter of this loop.
6438 float flexFactor
= aFunctions
.MaxSizingFor(track
).AsFr();
6439 const nscoord base
= mSizes
[track
].mBase
;
6440 if (flexFactor
* hypotheticalFrSize
< base
) {
6441 // 12.7.1.4: Treat this track as inflexible.
6442 flexTracks
[i
] = kAutoLine
;
6443 flexFactorSum
-= flexFactor
;
6444 leftOverSpace
-= base
;
6446 if (numFlexTracks
== 0 || leftOverSpace
<= 0) {
6450 // break; XXX (bug 1176621 comment 16) measure which is more common
6454 return hypotheticalFrSize
;
6457 float nsGridContainerFrame::Tracks::FindUsedFlexFraction(
6458 GridReflowInput
& aState
, nsTArray
<GridItemInfo
>& aGridItems
,
6459 const nsTArray
<uint32_t>& aFlexTracks
,
6460 const TrackSizingFunctions
& aFunctions
, nscoord aAvailableSize
) const {
6461 if (aAvailableSize
!= NS_UNCONSTRAINEDSIZE
) {
6462 // Use all of the grid tracks and a 'space to fill' of the available space.
6463 const TranslatedLineRange
range(0, mSizes
.Length());
6464 return FindFrUnitSize(range
, aFlexTracks
, aFunctions
, aAvailableSize
);
6467 // The used flex fraction is the maximum of:
6468 // ... each flexible track's base size divided by its flex factor (which is
6471 for (uint32_t track
: aFlexTracks
) {
6472 float flexFactor
= aFunctions
.MaxSizingFor(track
).AsFr();
6473 float possiblyDividedBaseSize
= (flexFactor
> 1.0f
)
6474 ? mSizes
[track
].mBase
/ flexFactor
6475 : mSizes
[track
].mBase
;
6476 fr
= std::max(fr
, possiblyDividedBaseSize
);
6478 WritingMode wm
= aState
.mWM
;
6479 gfxContext
* rc
= &aState
.mRenderingContext
;
6480 // ... the result of 'finding the size of an fr' for each item that spans
6481 // a flex track with its max-content contribution as 'space to fill'
6482 for (const GridItemInfo
& item
: aGridItems
) {
6483 if (item
.mState
[mAxis
] & ItemState::eIsFlexing
) {
6484 // XXX optimize: bug 1194446
6485 auto pb
= Some(aState
.PercentageBasisFor(mAxis
, item
));
6486 nscoord spaceToFill
= ContentContribution(item
, aState
, rc
, wm
, mAxis
, pb
,
6487 IntrinsicISizeType::PrefISize
);
6488 const LineRange
& range
=
6489 mAxis
== eLogicalAxisInline
? item
.mArea
.mCols
: item
.mArea
.mRows
;
6490 MOZ_ASSERT(range
.Extent() >= 1);
6491 const auto spannedGaps
= range
.Extent() - 1;
6492 if (spannedGaps
> 0) {
6493 spaceToFill
-= mGridGap
* spannedGaps
;
6495 if (spaceToFill
<= 0) {
6498 // ... and all its spanned tracks as input.
6499 nsTArray
<uint32_t> itemFlexTracks
;
6500 for (auto i
: range
.Range()) {
6501 if (mSizes
[i
].mState
& TrackSize::eFlexMaxSizing
) {
6502 itemFlexTracks
.AppendElement(i
);
6506 FindFrUnitSize(range
, itemFlexTracks
, aFunctions
, spaceToFill
);
6507 fr
= std::max(fr
, itemFr
);
6513 void nsGridContainerFrame::Tracks::StretchFlexibleTracks(
6514 GridReflowInput
& aState
, nsTArray
<GridItemInfo
>& aGridItems
,
6515 const TrackSizingFunctions
& aFunctions
, nscoord aAvailableSize
) {
6516 if (aAvailableSize
<= 0) {
6519 nsTArray
<uint32_t> flexTracks(mSizes
.Length());
6520 for (uint32_t i
= 0, len
= mSizes
.Length(); i
< len
; ++i
) {
6521 if (mSizes
[i
].mState
& TrackSize::eFlexMaxSizing
) {
6522 flexTracks
.AppendElement(i
);
6525 if (flexTracks
.IsEmpty()) {
6528 nscoord minSize
= 0;
6529 nscoord maxSize
= NS_UNCONSTRAINEDSIZE
;
6530 if (aState
.mReflowInput
) {
6531 auto* ri
= aState
.mReflowInput
;
6532 minSize
= mAxis
== eLogicalAxisBlock
? ri
->ComputedMinBSize()
6533 : ri
->ComputedMinISize();
6534 maxSize
= mAxis
== eLogicalAxisBlock
? ri
->ComputedMaxBSize()
6535 : ri
->ComputedMaxISize();
6537 Maybe
<CopyableAutoTArray
<TrackSize
, 32>> origSizes
;
6538 bool applyMinMax
= (minSize
!= 0 || maxSize
!= NS_UNCONSTRAINEDSIZE
) &&
6539 aAvailableSize
== NS_UNCONSTRAINEDSIZE
;
6540 // We iterate twice at most. The 2nd time if the grid size changed after
6541 // applying a min/max-size (can only occur if aAvailableSize is indefinite).
6543 float fr
= FindUsedFlexFraction(aState
, aGridItems
, flexTracks
, aFunctions
,
6546 for (uint32_t i
: flexTracks
) {
6547 float flexFactor
= aFunctions
.MaxSizingFor(i
).AsFr();
6548 nscoord flexLength
= NSToCoordRound(flexFactor
* fr
);
6549 nscoord
& base
= mSizes
[i
].mBase
;
6550 if (flexLength
> base
) {
6551 if (applyMinMax
&& origSizes
.isNothing()) {
6552 origSizes
.emplace(mSizes
);
6559 applyMinMax
= false;
6560 // https://drafts.csswg.org/css-grid/#algo-flex-tracks
6561 // "If using this flex fraction would cause the grid to be smaller than
6562 // the grid container’s min-width/height (or larger than the grid
6563 // container’s max-width/height), then redo this step, treating the free
6564 // space as definite [...]"
6565 const auto sumOfGridGaps
= SumOfGridGaps();
6566 nscoord newSize
= SumOfGridTracks() + sumOfGridGaps
;
6567 if (newSize
> maxSize
) {
6568 aAvailableSize
= maxSize
;
6569 } else if (newSize
< minSize
) {
6570 aAvailableSize
= minSize
;
6572 if (aAvailableSize
!= NS_UNCONSTRAINEDSIZE
) {
6573 aAvailableSize
= std::max(0, aAvailableSize
- sumOfGridGaps
);
6574 // Restart with the original track sizes and definite aAvailableSize.
6575 if (origSizes
.isSome()) {
6576 mSizes
= std::move(*origSizes
);
6578 } // else, no mSizes[].mBase were changed above so it's still correct
6579 if (aAvailableSize
== 0) {
6580 break; // zero available size wouldn't change any sizes though...
6589 void nsGridContainerFrame::Tracks::AlignJustifyContent(
6590 const nsStylePosition
* aStyle
, StyleContentDistribution aAligmentStyleValue
,
6591 WritingMode aWM
, nscoord aContentBoxSize
, bool aIsSubgriddedAxis
) {
6592 const bool isAlign
= mAxis
== eLogicalAxisBlock
;
6593 // Align-/justify-content doesn't apply in a subgridded axis.
6594 // Gap properties do apply though so we need to stretch/position the tracks
6595 // to center-align the gaps with the parent's gaps.
6596 if (MOZ_UNLIKELY(aIsSubgriddedAxis
)) {
6597 auto& gap
= isAlign
? aStyle
->mRowGap
: aStyle
->mColumnGap
;
6598 if (gap
.IsNormal()) {
6601 auto len
= mSizes
.Length();
6605 // This stores the gap deltas between the subgrid gap and the gaps in
6606 // the used track sizes (as encoded in its tracks' mPosition):
6607 nsTArray
<nscoord
> gapDeltas
;
6608 const size_t numGaps
= len
- 1;
6609 gapDeltas
.SetLength(numGaps
);
6610 for (size_t i
= 0; i
< numGaps
; ++i
) {
6611 TrackSize
& sz1
= mSizes
[i
];
6612 TrackSize
& sz2
= mSizes
[i
+ 1];
6613 nscoord currentGap
= sz2
.mPosition
- (sz1
.mPosition
+ sz1
.mBase
);
6614 gapDeltas
[i
] = mGridGap
- currentGap
;
6616 // Recompute the tracks' size/position so that they end up with
6617 // a subgrid-gap centered on the original track gap.
6618 nscoord currentPos
= mSizes
[0].mPosition
;
6619 nscoord
lastHalfDelta(0);
6620 for (size_t i
= 0; i
< numGaps
; ++i
) {
6621 TrackSize
& sz
= mSizes
[i
];
6622 nscoord delta
= gapDeltas
[i
];
6624 nscoord roundingError
= NSCoordDivRem(delta
, 2, &halfDelta
);
6625 auto newSize
= sz
.mBase
- (halfDelta
+ roundingError
) - lastHalfDelta
;
6626 lastHalfDelta
= halfDelta
;
6629 sz
.mPosition
= currentPos
;
6630 currentPos
+= newSize
+ mGridGap
;
6632 sz
.mBase
= nscoord(0);
6633 sz
.mPosition
= currentPos
+ newSize
;
6634 currentPos
= sz
.mPosition
+ mGridGap
;
6637 auto& lastTrack
= mSizes
.LastElement();
6638 auto newSize
= lastTrack
.mBase
- lastHalfDelta
;
6640 lastTrack
.mBase
= newSize
;
6641 lastTrack
.mPosition
= currentPos
;
6643 lastTrack
.mBase
= nscoord(0);
6644 lastTrack
.mPosition
= currentPos
+ newSize
;
6649 if (mSizes
.IsEmpty()) {
6654 auto alignment
= ::GetAlignJustifyValue(aAligmentStyleValue
.primary
, aWM
,
6655 isAlign
, &overflowSafe
);
6656 if (alignment
== StyleAlignFlags::NORMAL
) {
6657 alignment
= StyleAlignFlags::STRETCH
;
6658 // we may need a fallback for 'stretch' below
6659 aAligmentStyleValue
= {alignment
};
6662 // Compute the free space and count auto-sized tracks.
6663 size_t numAutoTracks
= 0;
6665 if (alignment
!= StyleAlignFlags::START
) {
6666 nscoord trackSizeSum
= 0;
6667 if (aIsSubgriddedAxis
) {
6668 numAutoTracks
= mSizes
.Length();
6670 for (const TrackSize
& sz
: mSizes
) {
6671 trackSizeSum
+= sz
.mBase
;
6672 if (sz
.mState
& TrackSize::eAutoMaxSizing
) {
6677 space
= aContentBoxSize
- trackSizeSum
- SumOfGridGaps();
6678 // Use the fallback value instead when applicable.
6680 (alignment
== StyleAlignFlags::SPACE_BETWEEN
&& mSizes
.Length() == 1)) {
6681 auto fallback
= ::GetAlignJustifyFallbackIfAny(aAligmentStyleValue
, aWM
,
6682 isAlign
, &overflowSafe
);
6684 alignment
= *fallback
;
6687 if (space
== 0 || (space
< 0 && overflowSafe
)) {
6688 // XXX check that this makes sense also for [last ]baseline (bug 1151204).
6689 alignment
= StyleAlignFlags::START
;
6693 // Optimize the cases where we just need to set each track's position.
6695 bool distribute
= true;
6696 if (alignment
== StyleAlignFlags::BASELINE
||
6697 alignment
== StyleAlignFlags::LAST_BASELINE
) {
6698 NS_WARNING("NYI: 'first/last baseline' (bug 1151204)"); // XXX
6699 alignment
= StyleAlignFlags::START
;
6701 if (alignment
== StyleAlignFlags::START
) {
6703 } else if (alignment
== StyleAlignFlags::END
) {
6706 } else if (alignment
== StyleAlignFlags::CENTER
) {
6709 } else if (alignment
== StyleAlignFlags::STRETCH
) {
6710 distribute
= numAutoTracks
!= 0;
6713 for (TrackSize
& sz
: mSizes
) {
6715 pos
+= sz
.mBase
+ mGridGap
;
6720 // Distribute free space to/between tracks and set their position.
6721 MOZ_ASSERT(space
> 0, "should've handled that on the fallback path above");
6722 nscoord between
, roundingError
;
6723 if (alignment
== StyleAlignFlags::STRETCH
) {
6724 MOZ_ASSERT(numAutoTracks
> 0, "we handled numAutoTracks == 0 above");
6725 // The outer loop typically only runs once - it repeats only in a masonry
6726 // axis when some stretchable items reach their `max-size`.
6727 // It's O(n^2) worst case; if all items are stretchable with a `max-size`
6728 // and exactly one item reaches its `max-size` each round.
6731 nscoord spacePerTrack
;
6732 roundingError
= NSCoordDivRem(space
, numAutoTracks
, &spacePerTrack
);
6734 for (TrackSize
& sz
: mSizes
) {
6736 if (!(sz
.mState
& TrackSize::eAutoMaxSizing
)) {
6737 pos
+= sz
.mBase
+ mGridGap
;
6740 nscoord stretch
= spacePerTrack
;
6741 if (roundingError
) {
6745 nscoord newBase
= sz
.mBase
+ stretch
;
6746 if (mIsMasonry
&& (sz
.mState
& TrackSize::eClampToLimit
)) {
6747 auto clampedSize
= std::min(newBase
, sz
.mLimit
);
6748 auto sizeOverLimit
= newBase
- clampedSize
;
6749 if (sizeOverLimit
> 0) {
6750 newBase
= clampedSize
;
6751 sz
.mState
&= ~(sz
.mState
& TrackSize::eAutoMaxSizing
);
6752 // This repeats the outer loop to distribute the superfluous space:
6753 space
+= sizeOverLimit
;
6754 if (--numAutoTracks
== 0) {
6755 // ... except if we don't have any stretchable items left.
6761 pos
+= newBase
+ mGridGap
;
6764 MOZ_ASSERT(!roundingError
, "we didn't distribute all rounding error?");
6767 if (alignment
== StyleAlignFlags::SPACE_BETWEEN
) {
6768 MOZ_ASSERT(mSizes
.Length() > 1, "should've used a fallback above");
6769 roundingError
= NSCoordDivRem(space
, mSizes
.Length() - 1, &between
);
6770 } else if (alignment
== StyleAlignFlags::SPACE_AROUND
) {
6771 roundingError
= NSCoordDivRem(space
, mSizes
.Length(), &between
);
6773 } else if (alignment
== StyleAlignFlags::SPACE_EVENLY
) {
6774 roundingError
= NSCoordDivRem(space
, mSizes
.Length() + 1, &between
);
6777 MOZ_ASSERT_UNREACHABLE("unknown align-/justify-content value");
6778 between
= 0; // just to avoid a compiler warning
6779 roundingError
= 0; // just to avoid a compiler warning
6781 between
+= mGridGap
;
6782 for (TrackSize
& sz
: mSizes
) {
6784 nscoord spacing
= between
;
6785 if (roundingError
) {
6789 pos
+= sz
.mBase
+ spacing
;
6791 MOZ_ASSERT(!roundingError
, "we didn't distribute all rounding error?");
6794 void nsGridContainerFrame::LineRange::ToPositionAndLength(
6795 const nsTArray
<TrackSize
>& aTrackSizes
, nscoord
* aPos
,
6796 nscoord
* aLength
) const {
6797 MOZ_ASSERT(mStart
!= kAutoLine
&& mEnd
!= kAutoLine
,
6798 "expected a definite LineRange");
6799 MOZ_ASSERT(mStart
< mEnd
);
6800 nscoord startPos
= aTrackSizes
[mStart
].mPosition
;
6801 const TrackSize
& sz
= aTrackSizes
[mEnd
- 1];
6803 *aLength
= (sz
.mPosition
+ sz
.mBase
) - startPos
;
6806 nscoord
nsGridContainerFrame::LineRange::ToLength(
6807 const nsTArray
<TrackSize
>& aTrackSizes
) const {
6808 MOZ_ASSERT(mStart
!= kAutoLine
&& mEnd
!= kAutoLine
,
6809 "expected a definite LineRange");
6810 MOZ_ASSERT(mStart
< mEnd
);
6811 nscoord startPos
= aTrackSizes
[mStart
].mPosition
;
6812 const TrackSize
& sz
= aTrackSizes
[mEnd
- 1];
6813 return (sz
.mPosition
+ sz
.mBase
) - startPos
;
6816 void nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos(
6817 const Tracks
& aTracks
, nscoord aGridOrigin
, nscoord
* aPos
,
6818 nscoord
* aLength
) const {
6819 // kAutoLine for abspos children contributes the corresponding edge
6820 // of the grid container's padding-box.
6821 if (mEnd
== kAutoLine
) {
6822 if (mStart
== kAutoLine
) {
6825 const nscoord endPos
= *aPos
+ *aLength
;
6826 auto side
= mStart
== aTracks
.mSizes
.Length()
6827 ? GridLineSide::BeforeGridGap
6828 : GridLineSide::AfterGridGap
;
6829 nscoord startPos
= aTracks
.GridLineEdge(mStart
, side
);
6830 *aPos
= aGridOrigin
+ startPos
;
6831 *aLength
= std::max(endPos
- *aPos
, 0);
6834 if (mStart
== kAutoLine
) {
6836 mEnd
== 0 ? GridLineSide::AfterGridGap
: GridLineSide::BeforeGridGap
;
6837 nscoord endPos
= aTracks
.GridLineEdge(mEnd
, side
);
6838 *aLength
= std::max(aGridOrigin
+ endPos
, 0);
6839 } else if (MOZ_LIKELY(mStart
!= mEnd
)) {
6841 ToPositionAndLength(aTracks
.mSizes
, &pos
, aLength
);
6842 *aPos
= aGridOrigin
+ pos
;
6844 // The grid area only covers removed 'auto-fit' tracks.
6845 nscoord pos
= aTracks
.GridLineEdge(mStart
, GridLineSide::BeforeGridGap
);
6846 *aPos
= aGridOrigin
+ pos
;
6847 *aLength
= nscoord(0);
6852 LogicalSize
nsGridContainerFrame::GridReflowInput::PercentageBasisFor(
6853 LogicalAxis aAxis
, const GridItemInfo
& aGridItem
) const {
6854 auto wm
= aGridItem
.mFrame
->GetWritingMode();
6855 const auto* itemParent
= aGridItem
.mFrame
->GetParent();
6856 if (MOZ_UNLIKELY(itemParent
!= mFrame
)) {
6857 // The item comes from a descendant subgrid. Use the subgrid's
6858 // used track sizes to resolve the grid area size, if present.
6859 MOZ_ASSERT(itemParent
->IsGridContainerFrame());
6860 auto* subgridFrame
= static_cast<const nsGridContainerFrame
*>(itemParent
);
6861 MOZ_ASSERT(subgridFrame
->IsSubgrid());
6862 if (auto* uts
= subgridFrame
->GetUsedTrackSizes()) {
6863 auto subgridWM
= subgridFrame
->GetWritingMode();
6864 LogicalSize
cbSize(subgridWM
, NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
6865 if (!subgridFrame
->IsSubgrid(eLogicalAxisInline
) &&
6866 uts
->mCanResolveLineRangeSize
[eLogicalAxisInline
]) {
6867 // NOTE: At this point aGridItem.mArea is in this->mFrame coordinates
6868 // and thus may have been transposed. The range values in a non-
6869 // subgridded axis still has its original values in subgridFrame's
6870 // coordinates though.
6871 auto rangeAxis
= subgridWM
.IsOrthogonalTo(mWM
) ? eLogicalAxisBlock
6872 : eLogicalAxisInline
;
6873 const auto& range
= aGridItem
.mArea
.LineRangeForAxis(rangeAxis
);
6874 cbSize
.ISize(subgridWM
) =
6875 range
.ToLength(uts
->mSizes
[eLogicalAxisInline
]);
6877 if (!subgridFrame
->IsSubgrid(eLogicalAxisBlock
) &&
6878 uts
->mCanResolveLineRangeSize
[eLogicalAxisBlock
]) {
6879 auto rangeAxis
= subgridWM
.IsOrthogonalTo(mWM
) ? eLogicalAxisInline
6880 : eLogicalAxisBlock
;
6881 const auto& range
= aGridItem
.mArea
.LineRangeForAxis(rangeAxis
);
6882 cbSize
.BSize(subgridWM
) =
6883 range
.ToLength(uts
->mSizes
[eLogicalAxisBlock
]);
6885 return cbSize
.ConvertTo(wm
, subgridWM
);
6888 return LogicalSize(wm
, NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
6891 if (aAxis
== eLogicalAxisInline
|| !mCols
.mCanResolveLineRangeSize
) {
6892 return LogicalSize(wm
, NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
6894 // Note: for now, we only resolve transferred percentages to row sizing.
6895 // We may need to adjust these assertions once we implement bug 1300366.
6896 MOZ_ASSERT(!mRows
.mCanResolveLineRangeSize
);
6897 nscoord colSize
= aGridItem
.mArea
.mCols
.ToLength(mCols
.mSizes
);
6898 nscoord rowSize
= NS_UNCONSTRAINEDSIZE
;
6899 return !wm
.IsOrthogonalTo(mWM
) ? LogicalSize(wm
, colSize
, rowSize
)
6900 : LogicalSize(wm
, rowSize
, colSize
);
6903 LogicalRect
nsGridContainerFrame::GridReflowInput::ContainingBlockFor(
6904 const GridArea
& aArea
) const {
6905 nscoord i
, b
, iSize
, bSize
;
6906 MOZ_ASSERT(aArea
.mCols
.Extent() > 0, "grid items cover at least one track");
6907 MOZ_ASSERT(aArea
.mRows
.Extent() > 0, "grid items cover at least one track");
6908 aArea
.mCols
.ToPositionAndLength(mCols
.mSizes
, &i
, &iSize
);
6909 aArea
.mRows
.ToPositionAndLength(mRows
.mSizes
, &b
, &bSize
);
6910 return LogicalRect(mWM
, i
, b
, iSize
, bSize
);
6913 LogicalRect
nsGridContainerFrame::GridReflowInput::ContainingBlockForAbsPos(
6914 const GridArea
& aArea
, const LogicalPoint
& aGridOrigin
,
6915 const LogicalRect
& aGridCB
) const {
6916 nscoord i
= aGridCB
.IStart(mWM
);
6917 nscoord b
= aGridCB
.BStart(mWM
);
6918 nscoord iSize
= aGridCB
.ISize(mWM
);
6919 nscoord bSize
= aGridCB
.BSize(mWM
);
6920 aArea
.mCols
.ToPositionAndLengthForAbsPos(mCols
, aGridOrigin
.I(mWM
), &i
,
6922 aArea
.mRows
.ToPositionAndLengthForAbsPos(mRows
, aGridOrigin
.B(mWM
), &b
,
6924 return LogicalRect(mWM
, i
, b
, iSize
, bSize
);
6927 void nsGridContainerFrame::GridReflowInput::AlignJustifyContentInMasonryAxis(
6928 nscoord aMasonryBoxSize
, nscoord aContentBoxSize
) {
6929 if (aContentBoxSize
== NS_UNCONSTRAINEDSIZE
) {
6930 aContentBoxSize
= aMasonryBoxSize
;
6932 auto& masonryAxisTracks
= mRows
.mIsMasonry
? mRows
: mCols
;
6933 MOZ_ASSERT(masonryAxisTracks
.mSizes
.Length() == 2,
6934 "unexpected masonry axis tracks");
6935 const auto masonryAxis
= masonryAxisTracks
.mAxis
;
6936 const auto contentAlignment
= mGridStyle
->UsedContentAlignment(masonryAxis
);
6937 if (contentAlignment
.primary
== StyleAlignFlags::NORMAL
||
6938 contentAlignment
.primary
== StyleAlignFlags::STRETCH
) {
6939 // Stretch the "masonry box" to the full content box if it's smaller.
6940 nscoord cbSize
= std::max(aMasonryBoxSize
, aContentBoxSize
);
6941 for (auto& sz
: masonryAxisTracks
.mSizes
) {
6947 // Save our current track sizes; replace them with one track sized to
6948 // the masonry box and align that within our content box.
6949 auto savedTrackSizes(std::move(masonryAxisTracks
.mSizes
));
6950 masonryAxisTracks
.mSizes
.AppendElement(savedTrackSizes
[0]);
6951 masonryAxisTracks
.mSizes
[0].mBase
= aMasonryBoxSize
;
6952 masonryAxisTracks
.AlignJustifyContent(mGridStyle
, contentAlignment
, mWM
,
6953 aContentBoxSize
, false);
6954 nscoord masonryBoxOffset
= masonryAxisTracks
.mSizes
[0].mPosition
;
6955 // Restore the original track sizes...
6956 masonryAxisTracks
.mSizes
= std::move(savedTrackSizes
);
6957 // ...then reposition and resize all of them to the aligned result.
6958 for (auto& sz
: masonryAxisTracks
.mSizes
) {
6959 sz
.mPosition
= masonryBoxOffset
;
6960 sz
.mBase
= aMasonryBoxSize
;
6964 // Note: this is called after all items have been positioned/reflowed.
6965 // The masonry-axis tracks have the size of the "masonry box" at this point
6966 // and are positioned according to 'align/justify-content'.
6967 void nsGridContainerFrame::GridReflowInput::AlignJustifyTracksInMasonryAxis(
6968 const LogicalSize
& aContentSize
, const nsSize
& aContainerSize
) {
6969 auto& masonryAxisTracks
= mRows
.mIsMasonry
? mRows
: mCols
;
6970 MOZ_ASSERT(masonryAxisTracks
.mSizes
.Length() == 2,
6971 "unexpected masonry axis tracks");
6972 const auto masonryAxis
= masonryAxisTracks
.mAxis
;
6973 auto gridAxis
= GetOrthogonalAxis(masonryAxis
);
6974 auto& gridAxisTracks
= TracksFor(gridAxis
);
6975 AutoTArray
<TrackSize
, 32> savedSizes
;
6976 savedSizes
.AppendElements(masonryAxisTracks
.mSizes
);
6978 nscoord contentAreaStart
= mBorderPadding
.Start(masonryAxis
, wm
);
6979 // The offset to the "masonry box" from our content-box start edge.
6980 nscoord masonryBoxOffset
= masonryAxisTracks
.mSizes
[0].mPosition
;
6981 nscoord alignmentContainerSize
= masonryAxisTracks
.mSizes
[0].mBase
;
6983 for (auto i
: IntegerRange(gridAxisTracks
.mSizes
.Length())) {
6984 auto tracksAlignment
= mGridStyle
->UsedTracksAlignment(masonryAxis
, i
);
6985 if (tracksAlignment
.primary
!= StyleAlignFlags::START
) {
6986 masonryAxisTracks
.mSizes
.ClearAndRetainStorage();
6987 for (const auto& item
: mGridItems
) {
6988 if (item
.mArea
.LineRangeForAxis(gridAxis
).mStart
== i
) {
6989 const auto* child
= item
.mFrame
;
6990 LogicalRect rect
= child
->GetLogicalRect(wm
, aContainerSize
);
6991 TrackSize sz
= {0, 0, 0, {0, 0}, TrackSize::StateBits
{0}};
6992 const auto& margin
= child
->GetLogicalUsedMargin(wm
);
6993 sz
.mPosition
= rect
.Start(masonryAxis
, wm
) -
6994 margin
.Start(masonryAxis
, wm
) - contentAreaStart
;
6996 rect
.Size(masonryAxis
, wm
) + margin
.StartEnd(masonryAxis
, wm
);
6997 // Account for a align-self baseline offset on the end side.
6998 // XXXmats hmm, it seems it would be a lot simpler to just store
6999 // these baseline adjustments into the UsedMarginProperty instead
7000 auto state
= item
.mState
[masonryAxis
];
7001 if ((state
& ItemState::eSelfBaseline
) &&
7002 (state
& ItemState::eEndSideBaseline
)) {
7003 sz
.mBase
+= item
.mBaselineOffset
[masonryAxis
];
7005 if (tracksAlignment
.primary
== StyleAlignFlags::STRETCH
) {
7006 const auto* pos
= child
->StylePosition();
7007 auto itemAlignment
=
7008 pos
->UsedSelfAlignment(masonryAxis
, mFrame
->Style());
7009 if (child
->StyleMargin()->HasAuto(masonryAxis
, wm
)) {
7010 sz
.mState
|= TrackSize::eAutoMaxSizing
;
7011 sz
.mState
|= TrackSize::eItemHasAutoMargin
;
7012 } else if (pos
->Size(masonryAxis
, wm
).IsAuto() &&
7013 (itemAlignment
== StyleAlignFlags::NORMAL
||
7014 itemAlignment
== StyleAlignFlags::STRETCH
)) {
7015 sz
.mState
|= TrackSize::eAutoMaxSizing
;
7016 sz
.mState
|= TrackSize::eItemStretchSize
;
7017 const auto& max
= pos
->MaxSize(masonryAxis
, wm
);
7018 if (max
.ConvertsToLength()) { // XXX deal with percentages
7019 // XXX add in baselineOffset ? use actual frame size - content
7021 nscoord boxSizingAdjust
=
7022 child
->GetLogicalUsedBorderAndPadding(wm
).StartEnd(
7024 if (pos
->mBoxSizing
== StyleBoxSizing::Border
) {
7025 boxSizingAdjust
= 0;
7027 sz
.mLimit
= nsLayoutUtils::ComputeBSizeValue(
7028 aContentSize
.Size(masonryAxis
, wm
), boxSizingAdjust
,
7029 max
.AsLengthPercentage());
7030 sz
.mLimit
+= margin
.StartEnd(masonryAxis
, wm
);
7031 sz
.mState
|= TrackSize::eClampToLimit
;
7035 masonryAxisTracks
.mSizes
.AppendElement(std::move(sz
));
7038 masonryAxisTracks
.AlignJustifyContent(mGridStyle
, tracksAlignment
, wm
,
7039 alignmentContainerSize
, false);
7040 auto iter
= mGridItems
.begin();
7041 auto end
= mGridItems
.end();
7042 // We limit the loop to the number of items we found in the current
7043 // grid-axis axis track (in the outer loop) as an optimization.
7044 for (auto r
: IntegerRange(masonryAxisTracks
.mSizes
.Length())) {
7045 GridItemInfo
* item
= nullptr;
7046 auto& sz
= masonryAxisTracks
.mSizes
[r
];
7047 // Find the next item in the current grid-axis axis track.
7048 for (; iter
!= end
; ++iter
) {
7049 if (iter
->mArea
.LineRangeForAxis(gridAxis
).mStart
== i
) {
7055 nsIFrame
* child
= item
->mFrame
;
7056 const auto childWM
= child
->GetWritingMode();
7057 auto masonryChildAxis
=
7058 childWM
.IsOrthogonalTo(wm
) ? gridAxis
: masonryAxis
;
7059 LogicalMargin margin
= child
->GetLogicalUsedMargin(childWM
);
7060 bool forceReposition
= false;
7061 if (sz
.mState
& TrackSize::eItemStretchSize
) {
7062 auto size
= child
->GetLogicalSize().Size(masonryChildAxis
, childWM
);
7063 auto newSize
= sz
.mBase
- margin
.StartEnd(masonryChildAxis
, childWM
);
7064 if (size
!= newSize
) {
7065 // XXX need to pass aIMinSizeClamp aBMinSizeClamp ?
7067 ContainingBlockFor(item
->mArea
).Size(wm
).ConvertTo(childWM
, wm
);
7068 LogicalSize availableSize
= cb
;
7069 cb
.Size(masonryChildAxis
, childWM
) = alignmentContainerSize
;
7070 availableSize
.Size(eLogicalAxisBlock
, childWM
) =
7071 NS_UNCONSTRAINEDSIZE
;
7072 const auto& bp
= child
->GetLogicalUsedBorderAndPadding(childWM
);
7073 newSize
-= bp
.StartEnd(masonryChildAxis
, childWM
);
7074 ::PostReflowStretchChild(child
, *mReflowInput
, availableSize
, cb
,
7075 masonryChildAxis
, newSize
);
7076 if (childWM
.IsPhysicalRTL()) {
7077 // The NormalPosition of this child is frame-size dependent so we
7078 // need to reset its stored position below.
7079 forceReposition
= true;
7082 } else if (sz
.mState
& TrackSize::eItemHasAutoMargin
) {
7083 // Re-compute the auto-margin(s) in the masonry axis.
7084 auto size
= child
->GetLogicalSize().Size(masonryChildAxis
, childWM
);
7085 auto spaceToFill
= sz
.mBase
- size
;
7086 if (spaceToFill
> nscoord(0)) {
7087 const auto& marginStyle
= child
->StyleMargin();
7088 if (marginStyle
->mMargin
.Start(masonryChildAxis
, childWM
)
7090 if (marginStyle
->mMargin
.End(masonryChildAxis
, childWM
)
7093 nscoord roundingError
= NSCoordDivRem(spaceToFill
, 2, &half
);
7094 margin
.Start(masonryChildAxis
, childWM
) = half
;
7095 margin
.End(masonryChildAxis
, childWM
) = half
+ roundingError
;
7097 margin
.Start(masonryChildAxis
, childWM
) = spaceToFill
;
7101 marginStyle
->mMargin
.End(masonryChildAxis
, childWM
).IsAuto());
7102 margin
.End(masonryChildAxis
, childWM
) = spaceToFill
;
7104 nsMargin
* propValue
=
7105 child
->GetProperty(nsIFrame::UsedMarginProperty());
7107 *propValue
= margin
.GetPhysicalMargin(childWM
);
7110 nsIFrame::UsedMarginProperty(),
7111 new nsMargin(margin
.GetPhysicalMargin(childWM
)));
7115 nscoord newPos
= contentAreaStart
+ masonryBoxOffset
+ sz
.mPosition
+
7116 margin
.Start(masonryChildAxis
, childWM
);
7117 LogicalPoint pos
= child
->GetLogicalNormalPosition(wm
, aContainerSize
);
7118 auto delta
= newPos
- pos
.Pos(masonryAxis
, wm
);
7119 if (delta
!= 0 || forceReposition
) {
7120 LogicalPoint
logicalDelta(wm
);
7121 logicalDelta
.Pos(masonryAxis
, wm
) = delta
;
7122 child
->MovePositionBy(wm
, logicalDelta
);
7125 } else if (masonryBoxOffset
!= nscoord(0)) {
7126 // TODO move placeholders too
7127 auto delta
= masonryBoxOffset
;
7128 LogicalPoint
logicalDelta(wm
);
7129 logicalDelta
.Pos(masonryAxis
, wm
) = delta
;
7130 for (const auto& item
: mGridItems
) {
7131 if (item
.mArea
.LineRangeForAxis(gridAxis
).mStart
!= i
) {
7134 item
.mFrame
->MovePositionBy(wm
, logicalDelta
);
7138 masonryAxisTracks
.mSizes
= std::move(savedSizes
);
7142 * Return a Fragmentainer object if we have a fragmentainer frame in our
7143 * ancestor chain of containing block (CB) reflow inputs. We'll only
7144 * continue traversing the ancestor chain as long as the CBs have
7145 * the same writing-mode and have overflow:visible.
7147 Maybe
<nsGridContainerFrame::Fragmentainer
>
7148 nsGridContainerFrame::GetNearestFragmentainer(
7149 const GridReflowInput
& aState
) const {
7150 Maybe
<nsGridContainerFrame::Fragmentainer
> data
;
7151 const ReflowInput
* gridRI
= aState
.mReflowInput
;
7152 if (!gridRI
->IsInFragmentedContext()) {
7155 WritingMode wm
= aState
.mWM
;
7156 const ReflowInput
* cbRI
= gridRI
->mCBReflowInput
;
7157 for (; cbRI
; cbRI
= cbRI
->mCBReflowInput
) {
7158 nsIScrollableFrame
* sf
= do_QueryFrame(cbRI
->mFrame
);
7162 if (wm
.IsOrthogonalTo(cbRI
->GetWritingMode())) {
7165 LayoutFrameType frameType
= cbRI
->mFrame
->Type();
7166 if ((frameType
== LayoutFrameType::Canvas
&&
7167 PresContext()->IsPaginated()) ||
7168 frameType
== LayoutFrameType::ColumnSet
) {
7170 data
->mIsTopOfPage
= gridRI
->mFlags
.mIsTopOfPage
;
7171 if (gridRI
->AvailableBSize() != NS_UNCONSTRAINEDSIZE
) {
7172 data
->mToFragmentainerEnd
= aState
.mFragBStart
+
7173 gridRI
->AvailableBSize() -
7174 aState
.mBorderPadding
.BStart(wm
);
7176 // This occurs when nsColumnSetFrame reflows its last column in
7177 // unconstrained available block-size.
7178 data
->mToFragmentainerEnd
= NS_UNCONSTRAINEDSIZE
;
7180 const auto numRows
= aState
.mRows
.mSizes
.Length();
7181 data
->mCanBreakAtStart
=
7182 numRows
> 0 && aState
.mRows
.mSizes
[0].mPosition
> 0;
7183 nscoord bSize
= gridRI
->ComputedBSize();
7184 data
->mIsAutoBSize
= bSize
== NS_UNCONSTRAINEDSIZE
;
7185 if (data
->mIsAutoBSize
) {
7186 bSize
= gridRI
->ComputedMinBSize();
7188 bSize
= NS_CSS_MINMAX(bSize
, gridRI
->ComputedMinBSize(),
7189 gridRI
->ComputedMaxBSize());
7192 aState
.mRows
.GridLineEdge(numRows
, GridLineSide::BeforeGridGap
);
7193 data
->mCanBreakAtEnd
= bSize
> gridEnd
&& bSize
> aState
.mFragBStart
;
7200 void nsGridContainerFrame::ReflowInFlowChild(
7201 nsIFrame
* aChild
, const GridItemInfo
* aGridItemInfo
, nsSize aContainerSize
,
7202 const Maybe
<nscoord
>& aStretchBSize
, const Fragmentainer
* aFragmentainer
,
7203 const GridReflowInput
& aState
, const LogicalRect
& aContentArea
,
7204 ReflowOutput
& aDesiredSize
, nsReflowStatus
& aStatus
) {
7205 nsPresContext
* pc
= PresContext();
7206 ComputedStyle
* containerSC
= Style();
7207 WritingMode wm
= aState
.mReflowInput
->GetWritingMode();
7208 const bool isGridItem
= !!aGridItemInfo
;
7209 MOZ_ASSERT(isGridItem
== !aChild
->IsPlaceholderFrame());
7211 WritingMode childWM
= aChild
->GetWritingMode();
7212 bool isConstrainedBSize
= false;
7213 nscoord toFragmentainerEnd
;
7214 // The part of the child's grid area that's in previous container fragments.
7215 nscoord consumedGridAreaBSize
= 0;
7216 const bool isOrthogonal
= wm
.IsOrthogonalTo(childWM
);
7217 if (MOZ_LIKELY(isGridItem
)) {
7218 MOZ_ASSERT(aGridItemInfo
->mFrame
== aChild
);
7219 const GridArea
& area
= aGridItemInfo
->mArea
;
7220 MOZ_ASSERT(area
.IsDefinite());
7221 cb
= aState
.ContainingBlockFor(area
);
7222 if (aFragmentainer
&& !wm
.IsOrthogonalTo(childWM
)) {
7223 // |gridAreaBOffset| is the offset of the child's grid area in this
7224 // container fragment (if negative, that distance is the child CB size
7225 // consumed in previous container fragments). Note that cb.BStart
7226 // (initially) and aState.mFragBStart are in "global" grid coordinates
7227 // (like all track positions).
7228 nscoord gridAreaBOffset
= cb
.BStart(wm
) - aState
.mFragBStart
;
7229 consumedGridAreaBSize
= std::max(0, -gridAreaBOffset
);
7230 cb
.BStart(wm
) = std::max(0, gridAreaBOffset
);
7231 if (aFragmentainer
->mToFragmentainerEnd
!= NS_UNCONSTRAINEDSIZE
) {
7232 toFragmentainerEnd
= aFragmentainer
->mToFragmentainerEnd
-
7233 aState
.mFragBStart
- cb
.BStart(wm
);
7234 toFragmentainerEnd
= std::max(toFragmentainerEnd
, 0);
7235 isConstrainedBSize
= true;
7238 cb
+= aContentArea
.Origin(wm
);
7239 aState
.mRows
.AlignBaselineSubtree(*aGridItemInfo
);
7240 aState
.mCols
.AlignBaselineSubtree(*aGridItemInfo
);
7241 // Setup [align|justify]-content:[last ]baseline related frame properties.
7242 // These are added to the padding in SizeComputationInput::InitOffsets.
7243 // (a negative value signals the value is for 'last baseline' and should be
7244 // added to the (logical) end padding)
7245 typedef const FramePropertyDescriptor
<SmallValueHolder
<nscoord
>>* Prop
;
7246 auto SetProp
= [aGridItemInfo
, aChild
](LogicalAxis aGridAxis
, Prop aProp
) {
7247 auto state
= aGridItemInfo
->mState
[aGridAxis
];
7248 auto baselineAdjust
= (state
& ItemState::eContentBaseline
)
7249 ? aGridItemInfo
->mBaselineOffset
[aGridAxis
]
7251 if (baselineAdjust
< nscoord(0)) {
7252 // This happens when the subtree overflows its track.
7253 // XXX spec issue? it's unclear how to handle this.
7254 baselineAdjust
= nscoord(0);
7255 } else if (GridItemInfo::BaselineAlignmentAffectsEndSide(state
)) {
7256 baselineAdjust
= -baselineAdjust
;
7258 if (baselineAdjust
!= nscoord(0)) {
7259 aChild
->SetProperty(aProp
, baselineAdjust
);
7261 aChild
->RemoveProperty(aProp
);
7264 SetProp(eLogicalAxisBlock
,
7265 isOrthogonal
? IBaselinePadProperty() : BBaselinePadProperty());
7266 SetProp(eLogicalAxisInline
,
7267 isOrthogonal
? BBaselinePadProperty() : IBaselinePadProperty());
7269 // By convention, for frames that perform CSS Box Alignment, we position
7270 // placeholder children at the start corner of their alignment container,
7271 // and in this case that's usually the grid's content-box.
7272 // ("Usually" - the exception is when the grid *also* forms the
7273 // abs.pos. containing block. In that case, the alignment container isn't
7274 // the content-box -- it's some grid area instead. But that case doesn't
7275 // require any special handling here, because we handle it later using a
7276 // special flag (ReflowInput::InitFlag::StaticPosIsCBOrigin) which will make
7277 // us ignore the placeholder's position entirely.)
7279 aChild
->AddStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN
);
7282 LogicalSize
reflowSize(cb
.Size(wm
));
7283 if (isConstrainedBSize
) {
7284 reflowSize
.BSize(wm
) = toFragmentainerEnd
;
7286 LogicalSize childCBSize
= reflowSize
.ConvertTo(childWM
, wm
);
7288 // Setup the ClampMarginBoxMinSize reflow flags and property, if needed.
7289 ComputeSizeFlags csFlags
;
7290 if (aGridItemInfo
) {
7291 // AlignJustifyTracksInMasonryAxis stretches items in a masonry-axis so we
7292 // don't do that here.
7293 auto* pos
= aChild
->StylePosition();
7294 auto j
= IsMasonry(eLogicalAxisInline
) ? StyleAlignFlags::START
7295 : pos
->UsedJustifySelf(Style())._0
;
7296 auto a
= IsMasonry(eLogicalAxisBlock
) ? StyleAlignFlags::START
7297 : pos
->UsedAlignSelf(Style())._0
;
7299 stretch
[eLogicalAxisInline
] =
7300 j
== StyleAlignFlags::NORMAL
|| j
== StyleAlignFlags::STRETCH
;
7301 stretch
[eLogicalAxisBlock
] =
7302 a
== StyleAlignFlags::NORMAL
|| a
== StyleAlignFlags::STRETCH
;
7303 auto childIAxis
= isOrthogonal
? eLogicalAxisBlock
: eLogicalAxisInline
;
7304 // Clamp during reflow if we're stretching in that axis.
7305 if (stretch
[childIAxis
]) {
7306 if (aGridItemInfo
->mState
[childIAxis
] &
7307 ItemState::eClampMarginBoxMinSize
) {
7308 csFlags
+= ComputeSizeFlag::IClampMarginBoxMinSize
;
7311 csFlags
+= ComputeSizeFlag::ShrinkWrap
;
7314 auto childBAxis
= GetOrthogonalAxis(childIAxis
);
7315 if (stretch
[childBAxis
] &&
7316 aGridItemInfo
->mState
[childBAxis
] & ItemState::eClampMarginBoxMinSize
) {
7317 csFlags
+= ComputeSizeFlag::BClampMarginBoxMinSize
;
7318 aChild
->SetProperty(BClampMarginBoxMinSizeProperty(),
7319 childCBSize
.BSize(childWM
));
7321 aChild
->RemoveProperty(BClampMarginBoxMinSizeProperty());
7324 if ((aGridItemInfo
->mState
[childIAxis
] & ItemState::eApplyAutoMinSize
)) {
7325 csFlags
+= ComputeSizeFlag::IApplyAutoMinSize
;
7329 if (!isConstrainedBSize
) {
7330 childCBSize
.BSize(childWM
) = NS_UNCONSTRAINEDSIZE
;
7332 LogicalSize
percentBasis(cb
.Size(wm
).ConvertTo(childWM
, wm
));
7333 ReflowInput
childRI(pc
, *aState
.mReflowInput
, aChild
, childCBSize
,
7334 Some(percentBasis
), {}, {}, csFlags
);
7335 childRI
.mFlags
.mIsTopOfPage
=
7336 aFragmentainer
? aFragmentainer
->mIsTopOfPage
: false;
7338 // FIXME (perf): It would be faster to do this only if the previous reflow of
7339 // the child was a measuring reflow, and only if the child does some of the
7340 // things that are affected by ComputeSizeFlag::IsGridMeasuringReflow.
7341 childRI
.SetBResize(true);
7342 childRI
.mFlags
.mIsBResizeForPercentages
= true;
7344 // If the child is stretching in its block axis, and we might be fragmenting
7345 // it in that axis, then setup a frame property to tell
7346 // nsBlockFrame::ComputeFinalSize the size.
7347 if (isConstrainedBSize
&& !wm
.IsOrthogonalTo(childWM
)) {
7348 bool stretch
= false;
7349 if (!childRI
.mStyleMargin
->HasBlockAxisAuto(childWM
) &&
7350 childRI
.mStylePosition
->BSize(childWM
).IsAuto()) {
7351 auto blockAxisAlignment
= childRI
.mStylePosition
->UsedAlignSelf(Style());
7352 if (!IsMasonry(eLogicalAxisBlock
) &&
7353 (blockAxisAlignment
._0
== StyleAlignFlags::NORMAL
||
7354 blockAxisAlignment
._0
== StyleAlignFlags::STRETCH
)) {
7359 aChild
->SetProperty(FragStretchBSizeProperty(), *aStretchBSize
);
7361 aChild
->RemoveProperty(FragStretchBSizeProperty());
7365 // We need the width of the child before we can correctly convert
7366 // the writing-mode of its origin, so we reflow at (0, 0) using a dummy
7367 // aContainerSize, and then pass the correct position to FinishReflowChild.
7368 ReflowOutput
childSize(childRI
);
7369 const nsSize dummyContainerSize
;
7371 // XXXdholbert The childPos that we use for ReflowChild shouldn't matter,
7372 // since we finalize it in FinishReflowChild. However, it does matter if the
7373 // child happens to be XUL (which sizes menu popup frames based on the
7374 // position within the viewport, during this ReflowChild call). So we make an
7375 // educated guess that the child will be at the origin of its containing
7376 // block, and then use align/justify to correct that as-needed further
7377 // down. (If the child has a different writing mode than its parent, though,
7378 // then we can't express the CB origin until we've reflowed the child and
7379 // determined its size. In that case, we throw up our hands and don't bother
7380 // trying to guess the position up-front after all.)
7381 // XXXdholbert We'll remove this special case in bug 1600542, and then we can
7382 // go back to just setting childPos in a single call after ReflowChild.
7383 LogicalPoint
childPos(childWM
);
7384 if (MOZ_LIKELY(childWM
== wm
)) {
7385 // Initially, assume the child will be at the containing block origin.
7386 // (This may get corrected during alignment/justification below.)
7387 childPos
= cb
.Origin(wm
);
7389 ReflowChild(aChild
, pc
, childSize
, childRI
, childWM
, childPos
,
7390 dummyContainerSize
, ReflowChildFlags::Default
, aStatus
);
7391 if (MOZ_UNLIKELY(childWM
!= wm
)) {
7392 // As above: assume the child will be at the containing block origin.
7393 // (which we can now compute in terms of the childWM, now that we know the
7395 childPos
= cb
.Origin(wm
).ConvertTo(
7396 childWM
, wm
, aContainerSize
- childSize
.PhysicalSize());
7398 // Apply align/justify-self and reflow again if that affects the size.
7399 if (MOZ_LIKELY(isGridItem
)) {
7400 LogicalSize size
= childSize
.Size(childWM
); // from the ReflowChild()
7401 auto applyItemSelfAlignment
= [&](LogicalAxis aAxis
, nscoord aCBSize
) {
7403 childRI
.mStylePosition
->UsedSelfAlignment(aAxis
, containerSC
);
7404 auto state
= aGridItemInfo
->mState
[aAxis
];
7405 auto flags
= AlignJustifyFlags::NoFlags
;
7406 if (IsMasonry(aAxis
)) {
7407 // In a masonry axis, we inhibit applying 'stretch' and auto-margins
7408 // here since AlignJustifyTracksInMasonryAxis deals with that.
7409 // The only other {align,justify}-{self,content} values that have an
7410 // effect are '[last] baseline', the rest behave as 'start'.
7411 if (MOZ_LIKELY(!(state
& ItemState::eSelfBaseline
))) {
7412 align
= {StyleAlignFlags::START
};
7414 auto group
= (state
& ItemState::eFirstBaseline
)
7415 ? BaselineSharingGroup::First
7416 : BaselineSharingGroup::Last
;
7417 auto itemStart
= aGridItemInfo
->mArea
.LineRangeForAxis(aAxis
).mStart
;
7418 aCBSize
= aState
.TracksFor(aAxis
)
7420 .mBaselineSubtreeSize
[group
];
7422 flags
= AlignJustifyFlags::IgnoreAutoMargins
;
7423 } else if (state
& ItemState::eContentBaseline
) {
7424 align
= {(state
& ItemState::eFirstBaseline
)
7425 ? StyleAlignFlags::SELF_START
7426 : StyleAlignFlags::SELF_END
};
7428 if (aAxis
== eLogicalAxisBlock
) {
7429 AlignSelf(*aGridItemInfo
, align
, aCBSize
, wm
, childRI
, size
, flags
,
7432 JustifySelf(*aGridItemInfo
, align
, aCBSize
, wm
, childRI
, size
, flags
,
7436 if (aStatus
.IsComplete()) {
7437 applyItemSelfAlignment(eLogicalAxisBlock
,
7438 cb
.BSize(wm
) - consumedGridAreaBSize
);
7440 applyItemSelfAlignment(eLogicalAxisInline
, cb
.ISize(wm
));
7441 } // else, nsAbsoluteContainingBlock.cpp will handle align/justify-self.
7443 FinishReflowChild(aChild
, pc
, childSize
, &childRI
, childWM
, childPos
,
7444 aContainerSize
, ReflowChildFlags::ApplyRelativePositioning
);
7445 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, aChild
);
7448 nscoord
nsGridContainerFrame::ReflowInFragmentainer(
7449 GridReflowInput
& aState
, const LogicalRect
& aContentArea
,
7450 ReflowOutput
& aDesiredSize
, nsReflowStatus
& aStatus
,
7451 Fragmentainer
& aFragmentainer
, const nsSize
& aContainerSize
) {
7452 MOZ_ASSERT(aStatus
.IsEmpty());
7453 MOZ_ASSERT(aState
.mReflowInput
);
7455 // Collect our grid items and sort them in row order. Collect placeholders
7456 // and put them in a separate array.
7457 nsTArray
<const GridItemInfo
*> sortedItems(aState
.mGridItems
.Length());
7458 nsTArray
<nsIFrame
*> placeholders(aState
.mAbsPosItems
.Length());
7459 aState
.mIter
.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll
);
7460 for (; !aState
.mIter
.AtEnd(); aState
.mIter
.Next()) {
7461 nsIFrame
* child
= *aState
.mIter
;
7462 if (!child
->IsPlaceholderFrame()) {
7463 const GridItemInfo
* info
= &aState
.mGridItems
[aState
.mIter
.ItemIndex()];
7464 sortedItems
.AppendElement(info
);
7466 placeholders
.AppendElement(child
);
7469 // NOTE: We don't need stable_sort here, except in Masonry layout. There are
7470 // no dependencies on having content order between items on the same row in
7471 // the code below in the non-Masonry case.
7473 std::stable_sort(sortedItems
.begin(), sortedItems
.end(),
7474 GridItemInfo::IsStartRowLessThan
);
7476 std::sort(sortedItems
.begin(), sortedItems
.end(),
7477 GridItemInfo::IsStartRowLessThan
);
7480 // Reflow our placeholder children; they must all be complete.
7481 for (auto child
: placeholders
) {
7482 nsReflowStatus childStatus
;
7483 ReflowInFlowChild(child
, nullptr, aContainerSize
, Nothing(),
7484 &aFragmentainer
, aState
, aContentArea
, aDesiredSize
,
7486 MOZ_ASSERT(childStatus
.IsComplete(),
7487 "nsPlaceholderFrame should never need to be fragmented");
7490 // The available size for children - we'll set this to the edge of the last
7491 // row in most cases below, but for now use the full size.
7492 nscoord childAvailableSize
= aFragmentainer
.mToFragmentainerEnd
;
7493 const uint32_t startRow
= aState
.mStartRow
;
7494 const uint32_t numRows
= aState
.mRows
.mSizes
.Length();
7495 bool isBDBClone
= aState
.mReflowInput
->mStyleBorder
->mBoxDecorationBreak
==
7496 StyleBoxDecorationBreak::Clone
;
7497 nscoord bpBEnd
= aState
.mBorderPadding
.BEnd(aState
.mWM
);
7499 // Set |endRow| to the first row that doesn't fit.
7500 uint32_t endRow
= numRows
;
7501 for (uint32_t row
= startRow
; row
< numRows
; ++row
) {
7502 auto& sz
= aState
.mRows
.mSizes
[row
];
7503 const nscoord bEnd
= sz
.mPosition
+ sz
.mBase
;
7504 nscoord remainingAvailableSize
= childAvailableSize
- bEnd
;
7505 if (remainingAvailableSize
< 0 ||
7506 (isBDBClone
&& remainingAvailableSize
< bpBEnd
)) {
7512 // Check for forced breaks on the items if available block-size for children
7513 // is constrained. That is, ignore forced breaks if available block-size for
7514 // children is unconstrained since our parent expected us to be fully
7516 bool isForcedBreak
= false;
7517 const bool avoidBreakInside
= ShouldAvoidBreakInside(*aState
.mReflowInput
);
7518 if (childAvailableSize
!= NS_UNCONSTRAINEDSIZE
) {
7519 const bool isTopOfPage
= aFragmentainer
.mIsTopOfPage
;
7520 for (const GridItemInfo
* info
: sortedItems
) {
7521 uint32_t itemStartRow
= info
->mArea
.mRows
.mStart
;
7522 if (itemStartRow
== endRow
) {
7525 const auto* disp
= info
->mFrame
->StyleDisplay();
7526 if (disp
->BreakBefore()) {
7527 // Propagate break-before on the first row to the container unless we're
7528 // already at top-of-page.
7529 if ((itemStartRow
== 0 && !isTopOfPage
) || avoidBreakInside
) {
7530 aStatus
.SetInlineLineBreakBeforeAndReset();
7531 return aState
.mFragBStart
;
7533 if ((itemStartRow
> startRow
||
7534 (itemStartRow
== startRow
&& !isTopOfPage
)) &&
7535 itemStartRow
< endRow
) {
7536 endRow
= itemStartRow
;
7537 isForcedBreak
= true;
7538 // reset any BREAK_AFTER we found on an earlier item
7540 break; // we're done since the items are sorted in row order
7543 uint32_t itemEndRow
= info
->mArea
.mRows
.mEnd
;
7544 if (disp
->BreakAfter()) {
7545 if (itemEndRow
!= numRows
) {
7546 if (itemEndRow
> startRow
&& itemEndRow
< endRow
) {
7547 endRow
= itemEndRow
;
7548 isForcedBreak
= true;
7549 // No "break;" here since later items with break-after may have
7553 // Propagate break-after on the last row to the container, we may
7554 // still find a break-before on this row though (and reset aStatus).
7555 aStatus
.SetInlineLineBreakAfter(); // tentative
7560 // Consume at least one row in each fragment until we have consumed them
7561 // all. Except for the first row if there's a break opportunity before it.
7562 if (startRow
== endRow
&& startRow
!= numRows
&&
7563 (startRow
!= 0 || !aFragmentainer
.mCanBreakAtStart
)) {
7567 // Honor break-inside:avoid if we can't fit all rows.
7568 if (avoidBreakInside
&& endRow
< numRows
) {
7569 aStatus
.SetInlineLineBreakBeforeAndReset();
7570 return aState
.mFragBStart
;
7574 // Calculate the block-size including this fragment.
7576 aState
.mRows
.GridLineEdge(endRow
, GridLineSide::BeforeGridGap
);
7578 if (aFragmentainer
.mIsAutoBSize
) {
7579 // We only apply min-bsize once all rows are complete (when bsize is auto).
7580 if (endRow
< numRows
) {
7582 auto clampedBSize
= ClampToCSSMaxBSize(bSize
, aState
.mReflowInput
);
7583 if (MOZ_UNLIKELY(clampedBSize
!= bSize
)) {
7584 // We apply max-bsize in all fragments though.
7585 bSize
= clampedBSize
;
7586 } else if (!isBDBClone
) {
7587 // The max-bsize won't make this fragment COMPLETE, so the block-end
7588 // border will be in a later fragment.
7592 bSize
= NS_CSS_MINMAX(bEndRow
, aState
.mReflowInput
->ComputedMinBSize(),
7593 aState
.mReflowInput
->ComputedMaxBSize());
7596 bSize
= NS_CSS_MINMAX(aState
.mReflowInput
->ComputedBSize(),
7597 aState
.mReflowInput
->ComputedMinBSize(),
7598 aState
.mReflowInput
->ComputedMaxBSize());
7601 // Check for overflow and set aStatus INCOMPLETE if so.
7602 bool overflow
= bSize
+ bpBEnd
> childAvailableSize
;
7604 if (avoidBreakInside
) {
7605 aStatus
.SetInlineLineBreakBeforeAndReset();
7606 return aState
.mFragBStart
;
7608 bool breakAfterLastRow
= endRow
== numRows
&& aFragmentainer
.mCanBreakAtEnd
;
7609 if (breakAfterLastRow
) {
7610 MOZ_ASSERT(bEndRow
< bSize
, "bogus aFragmentainer.mCanBreakAtEnd");
7611 nscoord availableSize
= childAvailableSize
;
7613 availableSize
-= bpBEnd
;
7615 // Pretend we have at least 1px available size, otherwise we'll never make
7616 // progress in consuming our bSize.
7618 std::max(availableSize
, aState
.mFragBStart
+ AppUnitsPerCSSPixel());
7619 // Fill the fragmentainer, but not more than our desired block-size and
7620 // at least to the size of the last row (even if that overflows).
7621 nscoord newBSize
= std::min(bSize
, availableSize
);
7622 newBSize
= std::max(newBSize
, bEndRow
);
7623 // If it's just the border+padding that is overflowing and we have
7624 // box-decoration-break:clone then we are technically COMPLETE. There's
7625 // no point in creating another zero-bsize fragment in this case.
7626 if (newBSize
< bSize
|| !isBDBClone
) {
7627 aStatus
.SetIncomplete();
7630 } else if (bSize
<= bEndRow
&& startRow
+ 1 < endRow
) {
7631 if (endRow
== numRows
) {
7632 // We have more than one row in this fragment, so we can break before
7633 // the last row instead.
7636 aState
.mRows
.GridLineEdge(endRow
, GridLineSide::BeforeGridGap
);
7638 if (aFragmentainer
.mIsAutoBSize
) {
7639 bSize
= ClampToCSSMaxBSize(bSize
, aState
.mReflowInput
);
7642 aStatus
.SetIncomplete();
7643 } else if (endRow
< numRows
) {
7644 bSize
= ClampToCSSMaxBSize(bEndRow
, aState
.mReflowInput
, &aStatus
);
7645 } // else - no break opportunities.
7647 // Even though our block-size fits we need to honor forced breaks, or if
7648 // a row doesn't fit in an auto-sized container (unless it's constrained
7649 // by a max-bsize which make us overflow-incomplete).
7650 if (endRow
< numRows
&&
7651 (isForcedBreak
|| (aFragmentainer
.mIsAutoBSize
&& bEndRow
== bSize
))) {
7652 bSize
= ClampToCSSMaxBSize(bEndRow
, aState
.mReflowInput
, &aStatus
);
7656 // If we can't fit all rows then we're at least overflow-incomplete.
7657 if (endRow
< numRows
) {
7658 childAvailableSize
= bEndRow
;
7659 if (aStatus
.IsComplete()) {
7660 aStatus
.SetOverflowIncomplete();
7661 aStatus
.SetNextInFlowNeedsReflow();
7664 // Children always have the full size of the rows in this fragment.
7665 childAvailableSize
= std::max(childAvailableSize
, bEndRow
);
7668 return ReflowRowsInFragmentainer(aState
, aContentArea
, aDesiredSize
, aStatus
,
7669 aFragmentainer
, aContainerSize
, sortedItems
,
7670 startRow
, endRow
, bSize
, childAvailableSize
);
7673 nscoord
nsGridContainerFrame::ReflowRowsInFragmentainer(
7674 GridReflowInput
& aState
, const LogicalRect
& aContentArea
,
7675 ReflowOutput
& aDesiredSize
, nsReflowStatus
& aStatus
,
7676 Fragmentainer
& aFragmentainer
, const nsSize
& aContainerSize
,
7677 const nsTArray
<const GridItemInfo
*>& aSortedItems
, uint32_t aStartRow
,
7678 uint32_t aEndRow
, nscoord aBSize
, nscoord aAvailableSize
) {
7679 FrameHashtable pushedItems
;
7680 FrameHashtable incompleteItems
;
7681 FrameHashtable overflowIncompleteItems
;
7682 Maybe
<nsTArray
<nscoord
>> masonryAxisPos
;
7683 const auto rowCount
= aState
.mRows
.mSizes
.Length();
7684 nscoord masonryAxisGap
;
7685 const auto wm
= aState
.mWM
;
7686 const bool isColMasonry
= IsMasonry(eLogicalAxisInline
);
7688 for (auto& sz
: aState
.mCols
.mSizes
) {
7691 masonryAxisGap
= nsLayoutUtils::ResolveGapToLength(
7692 aState
.mGridStyle
->mColumnGap
, aContentArea
.ISize(wm
));
7693 aState
.mCols
.mGridGap
= masonryAxisGap
;
7694 masonryAxisPos
.emplace(rowCount
);
7695 masonryAxisPos
->SetLength(rowCount
);
7696 PodZero(masonryAxisPos
->Elements(), rowCount
);
7698 bool isBDBClone
= aState
.mReflowInput
->mStyleBorder
->mBoxDecorationBreak
==
7699 StyleBoxDecorationBreak::Clone
;
7700 bool didGrowRow
= false;
7701 // As we walk across rows, we track whether the current row is at the top
7702 // of its grid-fragment, to help decide whether we can break before it. When
7703 // this function starts, our row is at the top of the current fragment if:
7704 // - we're starting with a nonzero row (i.e. we're a continuation)
7706 // - we're starting with the first row, & we're not allowed to break before
7707 // it (which makes it effectively at the top of its grid-fragment).
7708 bool isRowTopOfPage
= aStartRow
!= 0 || !aFragmentainer
.mCanBreakAtStart
;
7709 const bool isStartRowTopOfPage
= isRowTopOfPage
;
7710 // Save our full available size for later.
7711 const nscoord gridAvailableSize
= aFragmentainer
.mToFragmentainerEnd
;
7712 // Propagate the constrained size to our children.
7713 aFragmentainer
.mToFragmentainerEnd
= aAvailableSize
;
7714 // Reflow the items in row order up to |aEndRow| and push items after that.
7716 // |i| is intentionally signed, so we can set it to -1 to restart the loop.
7717 for (int32_t i
= 0, len
= aSortedItems
.Length(); i
< len
; ++i
) {
7718 const GridItemInfo
* const info
= aSortedItems
[i
];
7719 nsIFrame
* child
= info
->mFrame
;
7720 row
= info
->mArea
.mRows
.mStart
;
7721 MOZ_ASSERT(child
->GetPrevInFlow() ? row
< aStartRow
: row
>= aStartRow
,
7722 "unexpected child start row");
7723 if (row
>= aEndRow
) {
7724 pushedItems
.Insert(child
);
7728 bool rowCanGrow
= false;
7729 nscoord maxRowSize
= 0;
7730 if (row
>= aStartRow
) {
7731 if (row
> aStartRow
) {
7732 isRowTopOfPage
= false;
7734 // Can we grow this row? Only consider span=1 items per spec...
7735 rowCanGrow
= !didGrowRow
&& info
->mArea
.mRows
.Extent() == 1;
7737 auto& sz
= aState
.mRows
.mSizes
[row
];
7738 // and only min-/max-content rows or flex rows in an auto-sized
7740 rowCanGrow
= (sz
.mState
& TrackSize::eMinOrMaxContentMinSizing
) ||
7741 ((sz
.mState
& TrackSize::eFlexMaxSizing
) &&
7742 aFragmentainer
.mIsAutoBSize
);
7745 maxRowSize
= gridAvailableSize
- aState
.mBorderPadding
.BEnd(wm
);
7747 maxRowSize
= gridAvailableSize
;
7749 maxRowSize
-= sz
.mPosition
;
7750 // ...and only if there is space for it to grow.
7751 rowCanGrow
= maxRowSize
> sz
.mBase
;
7757 const auto& cols
= info
->mArea
.mCols
;
7758 MOZ_ASSERT((cols
.mStart
== 0 || cols
.mStart
== 1) && cols
.Extent() == 1);
7759 aState
.mCols
.mSizes
[cols
.mStart
].mPosition
= masonryAxisPos
.ref()[row
];
7762 // aFragmentainer.mIsTopOfPage is propagated to the child reflow input.
7763 // When it's false the child may request InlineBreak::Before. We set it
7764 // to false when the row is growable (as determined in the CSS Grid
7765 // Fragmentation spec) and there is a non-zero space between it and the
7766 // fragmentainer end (that can be used to grow it). If the child reports
7767 // a forced break in this case, we grow this row to fill the fragment and
7768 // restart the loop. We also restart the loop with |aEndRow = row|
7769 // (but without growing any row) for a InlineBreak::Before child if it spans
7770 // beyond the last row in this fragment. This is to avoid fragmenting it.
7771 // We only restart the loop once.
7772 aFragmentainer
.mIsTopOfPage
= isRowTopOfPage
&& !rowCanGrow
;
7773 nsReflowStatus childStatus
;
7774 // Pass along how much to stretch this fragment, in case it's needed.
7776 aState
.mRows
.GridLineEdge(std::min(aEndRow
, info
->mArea
.mRows
.mEnd
),
7777 GridLineSide::BeforeGridGap
) -
7778 aState
.mRows
.GridLineEdge(std::max(aStartRow
, row
),
7779 GridLineSide::AfterGridGap
);
7780 ReflowInFlowChild(child
, info
, aContainerSize
, Some(bSize
), &aFragmentainer
,
7781 aState
, aContentArea
, aDesiredSize
, childStatus
);
7782 MOZ_ASSERT(childStatus
.IsInlineBreakBefore() ||
7783 !childStatus
.IsFullyComplete() || !child
->GetNextInFlow(),
7784 "fully-complete reflow should destroy any NIFs");
7786 if (childStatus
.IsInlineBreakBefore()) {
7788 !child
->GetPrevInFlow(),
7789 "continuations should never report InlineBreak::Before status");
7790 MOZ_ASSERT(!aFragmentainer
.mIsTopOfPage
,
7791 "got IsInlineBreakBefore() at top of page");
7794 // Grow this row and restart with the next row as |aEndRow|.
7795 aState
.mRows
.ResizeRow(row
, maxRowSize
);
7796 if (aState
.mSharedGridData
) {
7797 aState
.mSharedGridData
->mRows
.ResizeRow(row
, maxRowSize
);
7800 aEndRow
= row
+ 1; // growing this row makes the next one not fit
7801 i
= -1; // i == 0 after the next loop increment
7802 isRowTopOfPage
= isStartRowTopOfPage
;
7803 overflowIncompleteItems
.Clear();
7804 incompleteItems
.Clear();
7806 aState
.mRows
.GridLineEdge(aEndRow
, GridLineSide::BeforeGridGap
);
7807 aFragmentainer
.mToFragmentainerEnd
= bEndRow
;
7808 if (aFragmentainer
.mIsAutoBSize
) {
7809 aBSize
= ClampToCSSMaxBSize(bEndRow
, aState
.mReflowInput
, &aStatus
);
7810 } else if (aStatus
.IsIncomplete()) {
7811 aBSize
= NS_CSS_MINMAX(aState
.mReflowInput
->ComputedBSize(),
7812 aState
.mReflowInput
->ComputedMinBSize(),
7813 aState
.mReflowInput
->ComputedMaxBSize());
7814 aBSize
= std::min(bEndRow
, aBSize
);
7819 if (!isRowTopOfPage
) {
7820 // We can break before this row - restart with it as the new end row.
7823 aState
.mRows
.GridLineEdge(aEndRow
, GridLineSide::BeforeGridGap
);
7824 i
= -1; // i == 0 after the next loop increment
7825 isRowTopOfPage
= isStartRowTopOfPage
;
7826 overflowIncompleteItems
.Clear();
7827 incompleteItems
.Clear();
7828 aStatus
.SetIncomplete();
7831 NS_ERROR("got InlineBreak::Before at top-of-page");
7832 childStatus
.Reset();
7834 // We got InlineBreak::Before again after growing the row - this can
7835 // happen if the child isn't splittable, e.g. some form controls.
7836 childStatus
.Reset();
7837 if (child
->GetNextInFlow()) {
7838 // The child already has a fragment, so we know it's splittable.
7839 childStatus
.SetIncomplete();
7840 } // else, report that it's complete
7842 } else if (childStatus
.IsInlineBreakAfter()) {
7843 MOZ_ASSERT_UNREACHABLE("unexpected child reflow status");
7846 MOZ_ASSERT(!childStatus
.IsInlineBreakBefore(),
7847 "should've handled InlineBreak::Before above");
7848 if (childStatus
.IsIncomplete()) {
7849 incompleteItems
.Insert(child
);
7850 } else if (!childStatus
.IsFullyComplete()) {
7851 overflowIncompleteItems
.Insert(child
);
7854 auto childWM
= child
->GetWritingMode();
7856 !childWM
.IsOrthogonalTo(wm
) ? eLogicalAxisInline
: eLogicalAxisBlock
;
7857 auto normalPos
= child
->GetLogicalNormalPosition(wm
, aContainerSize
);
7859 childAxis
== eLogicalAxisBlock
? child
->BSize() : child
->ISize();
7860 auto pos
= normalPos
.Pos(eLogicalAxisInline
, wm
) + sz
+
7861 child
->GetLogicalUsedMargin(childWM
).End(childAxis
, childWM
);
7862 masonryAxisPos
.ref()[row
] =
7863 pos
+ masonryAxisGap
- aContentArea
.Start(eLogicalAxisInline
, wm
);
7867 // Record a break before |aEndRow|.
7868 aState
.mNextFragmentStartRow
= aEndRow
;
7869 if (aEndRow
< rowCount
) {
7870 aState
.mRows
.BreakBeforeRow(aEndRow
);
7871 if (aState
.mSharedGridData
) {
7872 aState
.mSharedGridData
->mRows
.BreakBeforeRow(aEndRow
);
7876 const bool childrenMoved
= PushIncompleteChildren(
7877 pushedItems
, incompleteItems
, overflowIncompleteItems
);
7878 if (childrenMoved
&& aStatus
.IsComplete()) {
7879 aStatus
.SetOverflowIncomplete();
7880 aStatus
.SetNextInFlowNeedsReflow();
7882 if (!pushedItems
.IsEmpty()) {
7883 AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS
);
7884 // NOTE since we messed with our child list here, we intentionally
7885 // make aState.mIter invalid to avoid any use of it after this point.
7886 aState
.mIter
.Invalidate();
7888 if (!incompleteItems
.IsEmpty()) {
7889 // NOTE since we messed with our child list here, we intentionally
7890 // make aState.mIter invalid to avoid any use of it after this point.
7891 aState
.mIter
.Invalidate();
7895 nscoord maxSize
= 0;
7896 for (auto pos
: masonryAxisPos
.ref()) {
7897 maxSize
= std::max(maxSize
, pos
);
7899 maxSize
= std::max(nscoord(0), maxSize
- masonryAxisGap
);
7900 aState
.AlignJustifyContentInMasonryAxis(maxSize
, aContentArea
.ISize(wm
));
7906 // Here's a brief overview of how Masonry layout is implemented:
7907 // We setup two synthetic tracks in the Masonry axis so that the Reflow code
7908 // can treat it the same as for normal grid layout. The first track is
7909 // fixed (during item placement/layout) at the content box start and contains
7910 // the start items for each grid-axis track. The second track contains
7911 // all other items and is moved to the position where we want to position
7912 // the currently laid out item (like a sliding window as we place items).
7913 // Once item layout is done, the tracks are resized to be the size of
7914 // the "masonry box", which is the offset from the content box start to
7915 // the margin-box end of the item that is furthest away (this happens in
7916 // AlignJustifyContentInMasonryAxis() called at the end of this method).
7917 // This is to prepare for AlignJustifyTracksInMasonryAxis, which is called
7918 // later by our caller.
7919 // Both tracks store their first-/last-baseline group offsets as usual.
7920 // The first-baseline of the start track, and the last-baseline of the last
7921 // track (if they exist) are exported as the grid container's baselines, or
7922 // we fall back to picking an item's baseline (all this is per normal grid
7923 // layout). There's a slight difference in which items belongs to which
7924 // group though - see InitializeItemBaselinesInMasonryAxis for details.
7925 // This method returns the "masonry box" size (in the masonry axis).
7926 nscoord
nsGridContainerFrame::MasonryLayout(GridReflowInput
& aState
,
7927 const LogicalRect
& aContentArea
,
7928 SizingConstraint aConstraint
,
7929 ReflowOutput
& aDesiredSize
,
7930 nsReflowStatus
& aStatus
,
7931 Fragmentainer
* aFragmentainer
,
7932 const nsSize
& aContainerSize
) {
7933 using BaselineAlignmentSet
= Tracks::BaselineAlignmentSet
;
7935 auto recordAutoPlacement
= [this, &aState
](GridItemInfo
* aItem
,
7936 LogicalAxis aGridAxis
) {
7937 // When we're auto-placing an item in a continuation we need to record
7938 // the placement in mSharedGridData.
7939 if (MOZ_UNLIKELY(aState
.mSharedGridData
&& GetPrevInFlow()) &&
7940 (aItem
->mState
[aGridAxis
] & ItemState::eAutoPlacement
)) {
7941 auto* child
= aItem
->mFrame
;
7942 MOZ_RELEASE_ASSERT(!child
->GetPrevInFlow(),
7943 "continuations should never be auto-placed");
7944 for (auto& sharedItem
: aState
.mSharedGridData
->mGridItems
) {
7945 if (sharedItem
.mFrame
== child
) {
7946 sharedItem
.mArea
.LineRangeForAxis(aGridAxis
) =
7947 aItem
->mArea
.LineRangeForAxis(aGridAxis
);
7948 MOZ_ASSERT(sharedItem
.mState
[aGridAxis
] & ItemState::eAutoPlacement
);
7949 sharedItem
.mState
[aGridAxis
] &= ~ItemState::eAutoPlacement
;
7954 aItem
->mState
[aGridAxis
] &= ~ItemState::eAutoPlacement
;
7957 // Collect our grid items and sort them in grid order.
7958 nsTArray
<GridItemInfo
*> sortedItems(aState
.mGridItems
.Length());
7959 aState
.mIter
.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll
);
7960 size_t absposIndex
= 0;
7961 const LogicalAxis masonryAxis
=
7962 IsMasonry(eLogicalAxisBlock
) ? eLogicalAxisBlock
: eLogicalAxisInline
;
7963 const auto wm
= aState
.mWM
;
7964 for (; !aState
.mIter
.AtEnd(); aState
.mIter
.Next()) {
7965 nsIFrame
* child
= *aState
.mIter
;
7966 if (MOZ_LIKELY(!child
->IsPlaceholderFrame())) {
7967 GridItemInfo
* item
= &aState
.mGridItems
[aState
.mIter
.ItemIndex()];
7968 sortedItems
.AppendElement(item
);
7969 } else if (aConstraint
== SizingConstraint::NoConstraint
) {
7970 // (we only collect placeholders in the NoConstraint case since they
7971 // don't affect intrinsic sizing in any way)
7972 GridItemInfo
* item
= nullptr;
7973 auto* ph
= static_cast<nsPlaceholderFrame
*>(child
);
7974 if (ph
->GetOutOfFlowFrame()->GetParent() == this) {
7975 item
= &aState
.mAbsPosItems
[absposIndex
++];
7976 MOZ_RELEASE_ASSERT(item
->mFrame
== ph
->GetOutOfFlowFrame());
7977 auto masonryStart
= item
->mArea
.LineRangeForAxis(masonryAxis
).mStart
;
7978 // If the item was placed by the author at line 1 (masonryStart == 0)
7979 // then include it to be placed at the masonry-box start. If it's
7980 // auto-placed and has an `auto` inset value in the masonry axis then
7981 // we include it to be placed after the last grid item with the same
7982 // grid-axis start track.
7983 // XXXmats this is all a bit experimental at this point, pending a spec
7984 if (masonryStart
== 0 ||
7985 (masonryStart
== kAutoLine
&& item
->mFrame
->StylePosition()
7986 ->mOffset
.Start(masonryAxis
, wm
)
7988 sortedItems
.AppendElement(item
);
7994 // It wasn't included above - just reflow it and be done with it.
7995 nsReflowStatus childStatus
;
7996 ReflowInFlowChild(child
, nullptr, aContainerSize
, Nothing(), nullptr,
7997 aState
, aContentArea
, aDesiredSize
, childStatus
);
8001 const auto masonryAutoFlow
= aState
.mGridStyle
->mMasonryAutoFlow
;
8002 const bool definiteFirst
=
8003 masonryAutoFlow
.order
== StyleMasonryItemOrder::DefiniteFirst
;
8004 if (masonryAxis
== eLogicalAxisBlock
) {
8005 std::stable_sort(sortedItems
.begin(), sortedItems
.end(),
8006 definiteFirst
? GridItemInfo::RowMasonryDefiniteFirst
8007 : GridItemInfo::RowMasonryOrdered
);
8009 std::stable_sort(sortedItems
.begin(), sortedItems
.end(),
8010 definiteFirst
? GridItemInfo::ColMasonryDefiniteFirst
8011 : GridItemInfo::ColMasonryOrdered
);
8014 FrameHashtable pushedItems
;
8015 FrameHashtable incompleteItems
;
8016 FrameHashtable overflowIncompleteItems
;
8017 nscoord toFragmentainerEnd
= nscoord_MAX
;
8018 nscoord fragStartPos
= aState
.mFragBStart
;
8019 const bool avoidBreakInside
=
8020 aFragmentainer
&& ShouldAvoidBreakInside(*aState
.mReflowInput
);
8021 const bool isTopOfPageAtStart
=
8022 aFragmentainer
&& aFragmentainer
->mIsTopOfPage
;
8023 if (aFragmentainer
) {
8024 toFragmentainerEnd
= std::max(0, aFragmentainer
->mToFragmentainerEnd
);
8026 const LogicalAxis gridAxis
= GetOrthogonalAxis(masonryAxis
);
8027 const auto gridAxisTrackCount
= aState
.TracksFor(gridAxis
).mSizes
.Length();
8028 auto& masonryTracks
= aState
.TracksFor(masonryAxis
);
8029 auto& masonrySizes
= masonryTracks
.mSizes
;
8030 MOZ_ASSERT(masonrySizes
.Length() == 2);
8031 for (auto& sz
: masonrySizes
) {
8032 sz
.mPosition
= fragStartPos
;
8034 // The current running position for each grid-axis track where the next item
8035 // should be positioned. When an item is placed we'll update the tracks it
8036 // spans to the end of its margin box + 'gap'.
8037 nsTArray
<nscoord
> currentPos(gridAxisTrackCount
);
8038 currentPos
.SetLength(gridAxisTrackCount
);
8039 for (auto& sz
: currentPos
) {
8042 nsTArray
<nscoord
> lastPos(currentPos
.Clone());
8043 nsTArray
<GridItemInfo
*> lastItems(gridAxisTrackCount
);
8044 lastItems
.SetLength(gridAxisTrackCount
);
8045 PodZero(lastItems
.Elements(), gridAxisTrackCount
);
8046 const nscoord gap
= nsLayoutUtils::ResolveGapToLength(
8047 masonryAxis
== eLogicalAxisBlock
? aState
.mGridStyle
->mRowGap
8048 : aState
.mGridStyle
->mColumnGap
,
8049 masonryTracks
.mContentBoxSize
);
8050 masonryTracks
.mGridGap
= gap
;
8051 uint32_t cursor
= 0;
8052 const auto containerToMasonryBoxOffset
=
8053 fragStartPos
- aContentArea
.Start(masonryAxis
, wm
);
8054 const bool isPack
= masonryAutoFlow
.placement
== StyleMasonryPlacement::Pack
;
8055 bool didAlignStartAlignedFirstItems
= false;
8057 // Return true if any of the lastItems in aRange are baseline-aligned in
8058 // the masonry axis.
8059 auto lastItemHasBaselineAlignment
= [&](const LineRange
& aRange
) {
8060 for (auto i
: aRange
.Range()) {
8061 if (auto* child
= lastItems
[i
] ? lastItems
[i
]->mFrame
: nullptr) {
8062 const auto& pos
= child
->StylePosition();
8063 auto selfAlignment
= pos
->UsedSelfAlignment(masonryAxis
, this->Style());
8064 if (selfAlignment
== StyleAlignFlags::BASELINE
||
8065 selfAlignment
== StyleAlignFlags::LAST_BASELINE
) {
8068 auto childAxis
= masonryAxis
;
8069 if (child
->GetWritingMode().IsOrthogonalTo(wm
)) {
8070 childAxis
= gridAxis
;
8072 auto contentAlignment
= pos
->UsedContentAlignment(childAxis
).primary
;
8073 if (contentAlignment
== StyleAlignFlags::BASELINE
||
8074 contentAlignment
== StyleAlignFlags::LAST_BASELINE
) {
8082 // Resolve aItem's placement, unless it's definite already. Return its
8083 // masonry axis position with that placement.
8084 auto placeItem
= [&](GridItemInfo
* aItem
) -> nscoord
{
8085 auto& masonryAxisRange
= aItem
->mArea
.LineRangeForAxis(masonryAxis
);
8086 MOZ_ASSERT(masonryAxisRange
.mStart
!= 0, "item placement is already final");
8087 auto& gridAxisRange
= aItem
->mArea
.LineRangeForAxis(gridAxis
);
8088 bool isAutoPlaced
= aItem
->mState
[gridAxis
] & ItemState::eAutoPlacement
;
8089 uint32_t start
= isAutoPlaced
? 0 : gridAxisRange
.mStart
;
8090 if (isAutoPlaced
&& !isPack
) {
8092 isAutoPlaced
= false;
8094 const uint32_t extent
= gridAxisRange
.Extent();
8095 if (start
+ extent
> gridAxisTrackCount
) {
8096 // Note that this will only happen to auto-placed items since the grid is
8097 // always wide enough to fit other items.
8100 // This keeps track of the smallest `maxPosForRange` value that
8101 // we discover in the loop below:
8102 nscoord minPos
= nscoord_MAX
;
8103 MOZ_ASSERT(extent
<= gridAxisTrackCount
);
8104 const uint32_t iEnd
= gridAxisTrackCount
+ 1 - extent
;
8105 for (uint32_t i
= start
; i
< iEnd
; ++i
) {
8106 // Find the max `currentPos` value for the tracks that we would span
8107 // if we were to use `i` as our start track:
8108 nscoord maxPosForRange
= 0;
8109 for (auto j
= i
, jEnd
= j
+ extent
; j
< jEnd
; ++j
) {
8110 maxPosForRange
= std::max(currentPos
[j
], maxPosForRange
);
8112 if (maxPosForRange
< minPos
) {
8113 minPos
= maxPosForRange
;
8116 if (!isAutoPlaced
) {
8120 gridAxisRange
.mStart
= start
;
8121 gridAxisRange
.mEnd
= start
+ extent
;
8122 bool isFirstItem
= true;
8123 for (uint32_t i
: gridAxisRange
.Range()) {
8125 isFirstItem
= false;
8129 // If this is the first item in its spanned grid tracks, then place it in
8130 // the first masonry track. Otherwise, place it in the second masonry track.
8131 masonryAxisRange
.mStart
= isFirstItem
? 0 : 1;
8132 masonryAxisRange
.mEnd
= masonryAxisRange
.mStart
+ 1;
8136 // Handle the resulting reflow status after reflowing aItem.
8137 // This may set aStatus to BreakBefore which the caller is expected
8138 // to handle by returning from MasonryLayout.
8139 // @return true if this item should consume all remaining space
8140 auto handleChildStatus
= [&](GridItemInfo
* aItem
,
8141 const nsReflowStatus
& aChildStatus
) {
8142 bool result
= false;
8143 if (MOZ_UNLIKELY(aFragmentainer
)) {
8144 auto* child
= aItem
->mFrame
;
8145 if (!aChildStatus
.IsComplete() || aChildStatus
.IsInlineBreakBefore() ||
8146 aChildStatus
.IsInlineBreakAfter() ||
8147 child
->StyleDisplay()->BreakAfter()) {
8148 if (!isTopOfPageAtStart
&& avoidBreakInside
) {
8149 aStatus
.SetInlineLineBreakBeforeAndReset();
8154 if (aChildStatus
.IsInlineBreakBefore()) {
8155 aStatus
.SetIncomplete();
8156 pushedItems
.Insert(child
);
8157 } else if (aChildStatus
.IsIncomplete()) {
8158 recordAutoPlacement(aItem
, gridAxis
);
8159 aStatus
.SetIncomplete();
8160 incompleteItems
.Insert(child
);
8161 } else if (!aChildStatus
.IsFullyComplete()) {
8162 recordAutoPlacement(aItem
, gridAxis
);
8163 overflowIncompleteItems
.Insert(child
);
8169 // @return the distance from the masonry-box start to the end of the margin-
8171 auto offsetToMarginBoxEnd
= [&](nsIFrame
* aChild
) {
8172 auto childWM
= aChild
->GetWritingMode();
8173 auto childAxis
= !childWM
.IsOrthogonalTo(wm
) ? masonryAxis
: gridAxis
;
8174 auto normalPos
= aChild
->GetLogicalNormalPosition(wm
, aContainerSize
);
8176 childAxis
== eLogicalAxisBlock
? aChild
->BSize() : aChild
->ISize();
8177 return containerToMasonryBoxOffset
+ normalPos
.Pos(masonryAxis
, wm
) + sz
+
8178 aChild
->GetLogicalUsedMargin(childWM
).End(childAxis
, childWM
);
8181 // Apply baseline alignment to items belonging to the given set.
8182 nsTArray
<Tracks::ItemBaselineData
> firstBaselineItems
;
8183 nsTArray
<Tracks::ItemBaselineData
> lastBaselineItems
;
8184 auto applyBaselineAlignment
= [&](BaselineAlignmentSet aSet
) {
8185 firstBaselineItems
.ClearAndRetainStorage();
8186 lastBaselineItems
.ClearAndRetainStorage();
8187 masonryTracks
.InitializeItemBaselinesInMasonryAxis(
8188 aState
, aState
.mGridItems
, aSet
, aContainerSize
, currentPos
,
8189 firstBaselineItems
, lastBaselineItems
);
8191 bool didBaselineAdjustment
= false;
8192 nsTArray
<Tracks::ItemBaselineData
>* baselineItems
[] = {&firstBaselineItems
,
8193 &lastBaselineItems
};
8194 for (const auto* items
: baselineItems
) {
8195 for (const auto& data
: *items
) {
8196 GridItemInfo
* item
= data
.mGridItem
;
8197 MOZ_ASSERT((item
->mState
[masonryAxis
] & ItemState::eIsBaselineAligned
));
8198 nscoord baselineOffset
= item
->mBaselineOffset
[masonryAxis
];
8199 if (baselineOffset
== nscoord(0)) {
8200 continue; // no adjustment needed for this item
8202 didBaselineAdjustment
= true;
8203 auto* child
= item
->mFrame
;
8204 auto masonryAxisStart
=
8205 item
->mArea
.LineRangeForAxis(masonryAxis
).mStart
;
8206 auto gridAxisRange
= item
->mArea
.LineRangeForAxis(gridAxis
);
8207 masonrySizes
[masonryAxisStart
].mPosition
=
8208 aSet
.mItemSet
== BaselineAlignmentSet::LastItems
8209 ? lastPos
[gridAxisRange
.mStart
]
8211 bool consumeAllSpace
= false;
8212 const auto state
= item
->mState
[masonryAxis
];
8213 if ((state
& ItemState::eContentBaseline
) ||
8214 MOZ_UNLIKELY(aFragmentainer
)) {
8215 if (MOZ_UNLIKELY(aFragmentainer
)) {
8216 aFragmentainer
->mIsTopOfPage
=
8217 isTopOfPageAtStart
&&
8218 masonrySizes
[masonryAxisStart
].mPosition
== fragStartPos
;
8220 nsReflowStatus childStatus
;
8221 ReflowInFlowChild(child
, item
, aContainerSize
, Nothing(),
8222 aFragmentainer
, aState
, aContentArea
, aDesiredSize
,
8224 consumeAllSpace
= handleChildStatus(item
, childStatus
);
8225 if (aStatus
.IsInlineBreakBefore()) {
8228 } else if (!(state
& ItemState::eEndSideBaseline
)) {
8229 // `align/justify-self` baselines on the start side can be handled by
8230 // just moving the frame (except in a fragmentainer in which case we
8231 // reflow it above instead since it might make it INCOMPLETE).
8232 LogicalPoint
logicalDelta(wm
);
8233 logicalDelta
.Pos(masonryAxis
, wm
) = baselineOffset
;
8234 child
->MovePositionBy(wm
, logicalDelta
);
8236 if ((state
& ItemState::eEndSideBaseline
) && !consumeAllSpace
) {
8237 // Account for an end-side baseline adjustment.
8238 for (uint32_t i
: gridAxisRange
.Range()) {
8239 currentPos
[i
] += baselineOffset
;
8242 nscoord pos
= consumeAllSpace
? toFragmentainerEnd
8243 : offsetToMarginBoxEnd(child
);
8245 for (uint32_t i
: gridAxisRange
.Range()) {
8246 currentPos
[i
] = pos
;
8251 return didBaselineAdjustment
;
8254 // Place and reflow items. We'll use two fake tracks in the masonry axis.
8255 // The first contains items that were placed there by the regular grid
8256 // placement algo (PlaceGridItems) and we may add some items here if there
8257 // are still empty slots. The second track contains all other items.
8258 // Both tracks always have the size of the content box in the masonry axis.
8259 // The position of the first track is always at the start. The position
8260 // of the second track is updated as we go to a position where we want
8261 // the current item to be positioned.
8262 for (GridItemInfo
* item
: sortedItems
) {
8263 auto* child
= item
->mFrame
;
8264 auto& masonryRange
= item
->mArea
.LineRangeForAxis(masonryAxis
);
8265 auto& gridRange
= item
->mArea
.LineRangeForAxis(gridAxis
);
8266 nsReflowStatus childStatus
;
8267 if (MOZ_UNLIKELY(child
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
))) {
8268 auto contentArea
= aContentArea
;
8269 nscoord pos
= nscoord_MAX
;
8270 // XXXmats take mEnd into consideration...
8271 if (gridRange
.mStart
== kAutoLine
) {
8272 for (auto p
: currentPos
) {
8273 pos
= std::min(p
, pos
);
8275 } else if (gridRange
.mStart
< currentPos
.Length()) {
8276 pos
= currentPos
[gridRange
.mStart
];
8277 } else if (currentPos
.Length() > 0) {
8278 pos
= currentPos
.LastElement();
8280 if (pos
== nscoord_MAX
) {
8283 contentArea
.Start(masonryAxis
, wm
) = pos
;
8284 child
= child
->GetPlaceholderFrame();
8285 ReflowInFlowChild(child
, nullptr, aContainerSize
, Nothing(), nullptr,
8286 aState
, contentArea
, aDesiredSize
, childStatus
);
8288 MOZ_ASSERT(gridRange
.Extent() > 0 &&
8289 gridRange
.Extent() <= gridAxisTrackCount
);
8290 MOZ_ASSERT((masonryRange
.mStart
== 0 || masonryRange
.mStart
== 1) &&
8291 masonryRange
.Extent() == 1);
8292 if (masonryRange
.mStart
!= 0) {
8293 masonrySizes
[1].mPosition
= placeItem(item
);
8296 // If this is the first item NOT in the first track and if any of
8297 // the grid-axis tracks we span has a baseline-aligned item then we
8298 // need to do that baseline alignment now since it may affect
8299 // the placement of this and later items.
8300 if (!didAlignStartAlignedFirstItems
&&
8301 aConstraint
== SizingConstraint::NoConstraint
&&
8302 masonryRange
.mStart
!= 0 && lastItemHasBaselineAlignment(gridRange
)) {
8303 didAlignStartAlignedFirstItems
= true;
8304 if (applyBaselineAlignment({BaselineAlignmentSet::FirstItems
,
8305 BaselineAlignmentSet::StartStretch
})) {
8306 // Baseline alignment resized some items - redo our placement.
8307 masonrySizes
[1].mPosition
= placeItem(item
);
8309 if (aStatus
.IsInlineBreakBefore()) {
8310 return fragStartPos
;
8314 for (uint32_t i
: gridRange
.Range()) {
8315 lastItems
[i
] = item
;
8317 cursor
= gridRange
.mEnd
;
8318 if (cursor
>= gridAxisTrackCount
) {
8323 if (aConstraint
== SizingConstraint::NoConstraint
) {
8324 const auto* disp
= child
->StyleDisplay();
8325 if (MOZ_UNLIKELY(aFragmentainer
)) {
8326 aFragmentainer
->mIsTopOfPage
=
8327 isTopOfPageAtStart
&&
8328 masonrySizes
[masonryRange
.mStart
].mPosition
== fragStartPos
;
8329 if (!aFragmentainer
->mIsTopOfPage
&&
8330 (disp
->BreakBefore() ||
8331 masonrySizes
[masonryRange
.mStart
].mPosition
>=
8332 toFragmentainerEnd
)) {
8333 childStatus
.SetInlineLineBreakBeforeAndReset();
8336 if (!childStatus
.IsInlineBreakBefore()) {
8337 ReflowInFlowChild(child
, item
, aContainerSize
, Nothing(),
8338 aFragmentainer
, aState
, aContentArea
, aDesiredSize
,
8341 bool consumeAllSpace
= handleChildStatus(item
, childStatus
);
8342 if (aStatus
.IsInlineBreakBefore()) {
8343 return fragStartPos
;
8346 consumeAllSpace
? toFragmentainerEnd
: offsetToMarginBoxEnd(child
);
8348 LogicalSize
percentBasis(
8349 aState
.PercentageBasisFor(eLogicalAxisInline
, *item
));
8350 IntrinsicISizeType type
= aConstraint
== SizingConstraint::MaxContent
8351 ? IntrinsicISizeType::PrefISize
8352 : IntrinsicISizeType::MinISize
;
8354 ::ContentContribution(*item
, aState
, &aState
.mRenderingContext
, wm
,
8355 masonryAxis
, Some(percentBasis
), type
);
8356 pos
= sz
+ masonrySizes
[masonryRange
.mStart
].mPosition
;
8359 for (uint32_t i
: gridRange
.Range()) {
8360 lastPos
[i
] = currentPos
[i
];
8361 currentPos
[i
] = pos
;
8366 // Do the remaining baseline alignment sets.
8367 if (aConstraint
== SizingConstraint::NoConstraint
) {
8368 for (auto*& item
: lastItems
) {
8370 item
->mState
[masonryAxis
] |= ItemState::eIsLastItemInMasonryTrack
;
8373 BaselineAlignmentSet baselineSets
[] = {
8374 {BaselineAlignmentSet::FirstItems
, BaselineAlignmentSet::StartStretch
},
8375 {BaselineAlignmentSet::FirstItems
, BaselineAlignmentSet::EndStretch
},
8376 {BaselineAlignmentSet::LastItems
, BaselineAlignmentSet::StartStretch
},
8377 {BaselineAlignmentSet::LastItems
, BaselineAlignmentSet::EndStretch
},
8379 for (uint32_t i
= 0; i
< ArrayLength(baselineSets
); ++i
) {
8380 if (i
== 0 && didAlignStartAlignedFirstItems
) {
8383 applyBaselineAlignment(baselineSets
[i
]);
8387 const bool childrenMoved
= PushIncompleteChildren(
8388 pushedItems
, incompleteItems
, overflowIncompleteItems
);
8389 if (childrenMoved
&& aStatus
.IsComplete()) {
8390 aStatus
.SetOverflowIncomplete();
8391 aStatus
.SetNextInFlowNeedsReflow();
8393 if (!pushedItems
.IsEmpty()) {
8394 AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS
);
8395 // NOTE since we messed with our child list here, we intentionally
8396 // make aState.mIter invalid to avoid any use of it after this point.
8397 aState
.mIter
.Invalidate();
8399 if (!incompleteItems
.IsEmpty()) {
8400 // NOTE since we messed with our child list here, we intentionally
8401 // make aState.mIter invalid to avoid any use of it after this point.
8402 aState
.mIter
.Invalidate();
8405 nscoord masonryBoxSize
= 0;
8406 for (auto pos
: currentPos
) {
8407 masonryBoxSize
= std::max(masonryBoxSize
, pos
);
8409 masonryBoxSize
= std::max(nscoord(0), masonryBoxSize
- gap
);
8410 if (aConstraint
== SizingConstraint::NoConstraint
) {
8411 aState
.AlignJustifyContentInMasonryAxis(masonryBoxSize
,
8412 masonryTracks
.mContentBoxSize
);
8414 return masonryBoxSize
;
8417 nsGridContainerFrame
* nsGridContainerFrame::ParentGridContainerForSubgrid()
8419 MOZ_ASSERT(IsSubgrid());
8420 nsIFrame
* p
= GetParent();
8421 while (p
->GetContent() == GetContent()) {
8424 MOZ_ASSERT(p
->IsGridContainerFrame());
8425 auto* parent
= static_cast<nsGridContainerFrame
*>(p
);
8426 MOZ_ASSERT(parent
->HasSubgridItems());
8430 nscoord
nsGridContainerFrame::ReflowChildren(GridReflowInput
& aState
,
8431 const LogicalRect
& aContentArea
,
8432 const nsSize
& aContainerSize
,
8433 ReflowOutput
& aDesiredSize
,
8434 nsReflowStatus
& aStatus
) {
8435 WritingMode wm
= aState
.mReflowInput
->GetWritingMode();
8436 nscoord bSize
= aContentArea
.BSize(wm
);
8437 MOZ_ASSERT(aState
.mReflowInput
);
8438 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
8439 if (HidesContentForLayout()) {
8443 OverflowAreas ocBounds
;
8444 nsReflowStatus ocStatus
;
8445 if (GetPrevInFlow()) {
8446 ReflowOverflowContainerChildren(PresContext(), *aState
.mReflowInput
,
8447 ocBounds
, ReflowChildFlags::Default
,
8448 ocStatus
, MergeSortedFrameListsFor
);
8451 Maybe
<Fragmentainer
> fragmentainer
= GetNearestFragmentainer(aState
);
8452 // MasonryLayout() can only handle fragmentation in the masonry-axis,
8453 // so we let ReflowInFragmentainer() deal with grid-axis fragmentation
8454 // in the else-clause below.
8456 !(IsMasonry(eLogicalAxisInline
) && fragmentainer
.isSome())) {
8457 aState
.mInFragmentainer
= fragmentainer
.isSome();
8458 nscoord sz
= MasonryLayout(
8459 aState
, aContentArea
, SizingConstraint::NoConstraint
, aDesiredSize
,
8460 aStatus
, fragmentainer
.ptrOr(nullptr), aContainerSize
);
8461 if (IsMasonry(eLogicalAxisBlock
)) {
8462 bSize
= aState
.mReflowInput
->ComputedBSize();
8463 if (bSize
== NS_UNCONSTRAINEDSIZE
) {
8464 bSize
= NS_CSS_MINMAX(sz
, aState
.mReflowInput
->ComputedMinBSize(),
8465 aState
.mReflowInput
->ComputedMaxBSize());
8468 } else if (MOZ_UNLIKELY(fragmentainer
.isSome())) {
8469 if (IsMasonry(eLogicalAxisInline
) && !GetPrevInFlow()) {
8470 // First we do an unconstrained reflow to resolve the item placement
8471 // which is then kept as-is in the constrained reflow below.
8472 MasonryLayout(aState
, aContentArea
, SizingConstraint::NoConstraint
,
8473 aDesiredSize
, aStatus
, nullptr, aContainerSize
);
8475 aState
.mInFragmentainer
= true;
8476 bSize
= ReflowInFragmentainer(aState
, aContentArea
, aDesiredSize
, aStatus
,
8477 *fragmentainer
, aContainerSize
);
8479 aState
.mIter
.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll
);
8480 for (; !aState
.mIter
.AtEnd(); aState
.mIter
.Next()) {
8481 nsIFrame
* child
= *aState
.mIter
;
8482 const GridItemInfo
* info
= nullptr;
8483 if (!child
->IsPlaceholderFrame()) {
8484 info
= &aState
.mGridItems
[aState
.mIter
.ItemIndex()];
8486 ReflowInFlowChild(child
, info
, aContainerSize
, Nothing(), nullptr, aState
,
8487 aContentArea
, aDesiredSize
, aStatus
);
8488 MOZ_ASSERT(aStatus
.IsComplete(),
8489 "child should be complete in unconstrained reflow");
8493 // Merge overflow container bounds and status.
8494 aDesiredSize
.mOverflowAreas
.UnionWith(ocBounds
);
8495 aStatus
.MergeCompletionStatusFrom(ocStatus
);
8497 if (IsAbsoluteContainer()) {
8498 const nsFrameList
& children
= GetChildList(GetAbsoluteListID());
8499 if (!children
.IsEmpty()) {
8500 // 'gridOrigin' is the origin of the grid (the start of the first track),
8501 // with respect to the grid container's padding-box (CB).
8502 LogicalMargin
pad(aState
.mReflowInput
->ComputedLogicalPadding(wm
));
8503 const LogicalPoint
gridOrigin(wm
, pad
.IStart(wm
), pad
.BStart(wm
));
8504 const LogicalRect
gridCB(wm
, 0, 0,
8505 aContentArea
.ISize(wm
) + pad
.IStartEnd(wm
),
8506 bSize
+ pad
.BStartEnd(wm
));
8507 const nsSize gridCBPhysicalSize
= gridCB
.Size(wm
).GetPhysicalSize(wm
);
8509 for (nsIFrame
* child
: children
) {
8510 MOZ_ASSERT(i
< aState
.mAbsPosItems
.Length());
8511 MOZ_ASSERT(aState
.mAbsPosItems
[i
].mFrame
== child
);
8512 GridArea
& area
= aState
.mAbsPosItems
[i
].mArea
;
8513 LogicalRect itemCB
=
8514 aState
.ContainingBlockForAbsPos(area
, gridOrigin
, gridCB
);
8515 // nsAbsoluteContainingBlock::Reflow uses physical coordinates.
8516 nsRect
* cb
= child
->GetProperty(GridItemContainingBlockRect());
8519 child
->SetProperty(GridItemContainingBlockRect(), cb
);
8521 *cb
= itemCB
.GetPhysicalRect(wm
, gridCBPhysicalSize
);
8524 // We pass a dummy rect as CB because each child has its own CB rect.
8525 // The eIsGridContainerCB flag tells nsAbsoluteContainingBlock::Reflow to
8526 // use those instead.
8528 AbsPosReflowFlags flags
=
8529 AbsPosReflowFlags::CBWidthAndHeightChanged
; // XXX could be optimized
8530 flags
|= AbsPosReflowFlags::ConstrainHeight
;
8531 flags
|= AbsPosReflowFlags::IsGridContainerCB
;
8532 GetAbsoluteContainingBlock()->Reflow(
8533 this, PresContext(), *aState
.mReflowInput
, aStatus
, dummyRect
, flags
,
8534 &aDesiredSize
.mOverflowAreas
);
8540 void nsGridContainerFrame::Reflow(nsPresContext
* aPresContext
,
8541 ReflowOutput
& aDesiredSize
,
8542 const ReflowInput
& aReflowInput
,
8543 nsReflowStatus
& aStatus
) {
8544 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
8549 DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
8550 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
8551 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
8553 if (IsFrameTreeTooDeep(aReflowInput
, aDesiredSize
, aStatus
)) {
8557 NormalizeChildLists();
8560 mDidPushItemsBitMayLie
= false;
8561 SanityCheckChildListsBeforeReflow();
8564 for (auto& perAxisBaseline
: mBaseline
) {
8565 for (auto& baseline
: perAxisBaseline
) {
8566 baseline
= NS_INTRINSIC_ISIZE_UNKNOWN
;
8570 const nsStylePosition
* stylePos
= aReflowInput
.mStylePosition
;
8571 auto prevInFlow
= static_cast<nsGridContainerFrame
*>(GetPrevInFlow());
8572 if (MOZ_LIKELY(!prevInFlow
)) {
8573 InitImplicitNamedAreas(stylePos
);
8575 MOZ_ASSERT(prevInFlow
->HasAnyStateBits(kIsSubgridBits
) ==
8576 HasAnyStateBits(kIsSubgridBits
),
8577 "continuations should have same kIsSubgridBits");
8579 GridReflowInput
gridReflowInput(this, aReflowInput
);
8580 if (gridReflowInput
.mIter
.ItemsAreAlreadyInOrder()) {
8581 AddStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER
);
8583 RemoveStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER
);
8585 if (gridReflowInput
.mIter
.AtEnd() ||
8586 aReflowInput
.mStyleDisplay
->IsContainLayout()) {
8587 // We have no grid items, or we're layout-contained. So, we have no
8588 // baseline, and our parent should synthesize a baseline if needed.
8589 AddStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE
);
8591 RemoveStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE
);
8593 const nscoord computedBSize
= aReflowInput
.ComputedBSize();
8594 const nscoord computedISize
= aReflowInput
.ComputedISize();
8595 const WritingMode
& wm
= gridReflowInput
.mWM
;
8596 const LogicalSize
computedSize(wm
, computedISize
, computedBSize
);
8598 nscoord consumedBSize
= 0;
8600 if (MOZ_LIKELY(!prevInFlow
)) {
8602 if (MOZ_LIKELY(!IsSubgrid())) {
8603 RepeatTrackSizingInput
repeatSizing(aReflowInput
.ComputedMinSize(),
8605 aReflowInput
.ComputedMaxSize());
8606 grid
.PlaceGridItems(gridReflowInput
, repeatSizing
);
8608 auto* subgrid
= GetProperty(Subgrid::Prop());
8609 MOZ_ASSERT(subgrid
, "an ancestor forgot to call PlaceGridItems?");
8610 gridReflowInput
.mGridItems
= subgrid
->mGridItems
.Clone();
8611 gridReflowInput
.mAbsPosItems
= subgrid
->mAbsPosItems
.Clone();
8612 grid
.mGridColEnd
= subgrid
->mGridColEnd
;
8613 grid
.mGridRowEnd
= subgrid
->mGridRowEnd
;
8615 // XXX Technically incorrect: 'contain-intrinsic-block-size: none' is
8616 // treated as 0, ignoring our row sizes, when really we should use them but
8617 // *they* should be computed as if we had no children. To be fixed in bug
8619 const Maybe
<nscoord
> containBSize
=
8620 aReflowInput
.mFrame
->ContainIntrinsicBSize();
8621 const nscoord trackSizingBSize
= [&] {
8622 // This clamping only applies to auto sizes.
8623 if (containBSize
&& computedBSize
== NS_UNCONSTRAINEDSIZE
) {
8624 return NS_CSS_MINMAX(*containBSize
, aReflowInput
.ComputedMinBSize(),
8625 aReflowInput
.ComputedMaxBSize());
8627 return computedBSize
;
8629 const LogicalSize
containLogicalSize(wm
, computedISize
, trackSizingBSize
);
8630 gridReflowInput
.CalculateTrackSizes(grid
, containLogicalSize
,
8631 SizingConstraint::NoConstraint
);
8633 bSize
= *containBSize
;
8635 if (IsMasonry(eLogicalAxisBlock
)) {
8636 bSize
= computedBSize
;
8638 const auto& rowSizes
= gridReflowInput
.mRows
.mSizes
;
8639 if (MOZ_LIKELY(!IsSubgrid(eLogicalAxisBlock
))) {
8640 // Note: we can't use GridLineEdge here since we haven't calculated
8641 // the rows' mPosition yet (happens in AlignJustifyContent below).
8642 for (const auto& sz
: rowSizes
) {
8645 bSize
+= gridReflowInput
.mRows
.SumOfGridGaps();
8646 } else if (computedBSize
== NS_UNCONSTRAINEDSIZE
) {
8647 bSize
= gridReflowInput
.mRows
.GridLineEdge(
8648 rowSizes
.Length(), GridLineSide::BeforeGridGap
);
8653 consumedBSize
= CalcAndCacheConsumedBSize();
8654 gridReflowInput
.InitializeForContinuation(this, consumedBSize
);
8655 // XXX Technically incorrect: 'contain-intrinsic-block-size: none' is
8656 // treated as 0, ignoring our row sizes, when really we should use them but
8657 // *they* should be computed as if we had no children. To be fixed in bug
8659 if (Maybe
<nscoord
> containBSize
=
8660 aReflowInput
.mFrame
->ContainIntrinsicBSize()) {
8661 bSize
= *containBSize
;
8663 const uint32_t numRows
= gridReflowInput
.mRows
.mSizes
.Length();
8664 bSize
= gridReflowInput
.mRows
.GridLineEdge(numRows
,
8665 GridLineSide::AfterGridGap
);
8668 if (computedBSize
== NS_UNCONSTRAINEDSIZE
) {
8669 bSize
= NS_CSS_MINMAX(bSize
, aReflowInput
.ComputedMinBSize(),
8670 aReflowInput
.ComputedMaxBSize());
8671 } else if (aReflowInput
.ShouldApplyAutomaticMinimumOnBlockAxis()) {
8672 nscoord contentBSize
= NS_CSS_MINMAX(bSize
, aReflowInput
.ComputedMinBSize(),
8673 aReflowInput
.ComputedMaxBSize());
8674 bSize
= std::max(contentBSize
, computedBSize
);
8676 bSize
= computedBSize
;
8678 if (bSize
!= NS_UNCONSTRAINEDSIZE
) {
8679 bSize
= std::max(bSize
- consumedBSize
, 0);
8681 auto& bp
= gridReflowInput
.mBorderPadding
;
8682 LogicalRect
contentArea(wm
, bp
.IStart(wm
), bp
.BStart(wm
), computedISize
,
8686 const auto& rowSizes
= gridReflowInput
.mRows
.mSizes
;
8687 if (!IsRowSubgrid()) {
8688 // Apply 'align-content' to the grid.
8689 if (computedBSize
== NS_UNCONSTRAINEDSIZE
&&
8690 stylePos
->mRowGap
.IsLengthPercentage() &&
8691 stylePos
->mRowGap
.AsLengthPercentage().HasPercent()) {
8692 // Re-resolve the row-gap now that we know our intrinsic block-size.
8693 gridReflowInput
.mRows
.mGridGap
=
8694 nsLayoutUtils::ResolveGapToLength(stylePos
->mRowGap
, bSize
);
8696 if (!gridReflowInput
.mRows
.mIsMasonry
) {
8697 auto alignment
= stylePos
->mAlignContent
;
8698 gridReflowInput
.mRows
.AlignJustifyContent(stylePos
, alignment
, wm
,
8702 if (computedBSize
== NS_UNCONSTRAINEDSIZE
) {
8703 bSize
= gridReflowInput
.mRows
.GridLineEdge(rowSizes
.Length(),
8704 GridLineSide::BeforeGridGap
);
8705 contentArea
.BSize(wm
) = std::max(bSize
, nscoord(0));
8708 // Save the final row sizes for use by subgrids, if needed.
8709 if (HasSubgridItems() || IsSubgrid()) {
8710 StoreUsedTrackSizes(eLogicalAxisBlock
, rowSizes
);
8714 nsSize containerSize
= contentArea
.Size(wm
).GetPhysicalSize(wm
);
8715 bool repositionChildren
= false;
8716 if (containerSize
.width
== NS_UNCONSTRAINEDSIZE
&& wm
.IsVerticalRL()) {
8717 // Note that writing-mode:vertical-rl is the only case where the block
8718 // logical direction progresses in a negative physical direction, and
8719 // therefore block-dir coordinate conversion depends on knowing the width
8720 // of the coordinate space in order to translate between the logical and
8721 // physical origins.
8723 // A masonry axis size may be unconstrained, otherwise in a regular grid
8724 // our intrinsic size is always known by now. We'll re-position
8725 // the children below once our size is known.
8726 repositionChildren
= true;
8727 containerSize
.width
= 0;
8729 containerSize
.width
+= bp
.LeftRight(wm
);
8730 containerSize
.height
+= bp
.TopBottom(wm
);
8732 bSize
= ReflowChildren(gridReflowInput
, contentArea
, containerSize
,
8733 aDesiredSize
, aStatus
);
8734 bSize
= std::max(bSize
- consumedBSize
, 0);
8736 // Skip our block-end border if we're INCOMPLETE.
8737 if (!aStatus
.IsComplete() && !gridReflowInput
.mSkipSides
.BEnd() &&
8738 StyleBorder()->mBoxDecorationBreak
!= StyleBoxDecorationBreak::Clone
) {
8739 bp
.BEnd(wm
) = nscoord(0);
8742 LogicalSize
desiredSize(wm
, computedISize
+ bp
.IStartEnd(wm
),
8743 bSize
+ bp
.BStartEnd(wm
));
8744 aDesiredSize
.SetSize(wm
, desiredSize
);
8745 nsRect
frameRect(0, 0, aDesiredSize
.Width(), aDesiredSize
.Height());
8746 aDesiredSize
.mOverflowAreas
.UnionAllWith(frameRect
);
8748 if (repositionChildren
) {
8749 nsPoint
physicalDelta(aDesiredSize
.Width() - bp
.LeftRight(wm
), 0);
8750 for (const auto& item
: gridReflowInput
.mGridItems
) {
8751 auto* child
= item
.mFrame
;
8752 child
->MovePositionBy(physicalDelta
);
8753 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, child
);
8757 if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent
) {
8758 // Per spec, the grid area is included in a grid container's scrollable
8759 // overflow region [1], as well as the padding on the end-edge sides that
8760 // would satisfy the requirements of 'place-content: end' alignment [2].
8762 // Note that we include the padding from all sides of the grid area, not
8763 // just the end sides; this is fine because the grid area is relative to our
8764 // content-box origin. The inflated bounds won't go beyond our padding-box
8765 // edges on the start sides.
8767 // The margin areas of grid item boxes are also included in the scrollable
8768 // overflow region [2].
8770 // [1] https://drafts.csswg.org/css-grid-1/#overflow
8771 // [2] https://drafts.csswg.org/css-overflow-3/#scrollable
8773 // Synthesize a grid area covering all columns and rows, and compute its
8774 // rect relative to our border-box.
8776 // Note: the grid columns and rows exist only if there is an explicit grid;
8777 // or when an implicit grid is needed to place any grid items. See
8778 // nsGridContainerFrame::Grid::PlaceGridItems().
8779 const auto numCols
=
8780 static_cast<int32_t>(gridReflowInput
.mCols
.mSizes
.Length());
8781 const auto numRows
=
8782 static_cast<int32_t>(gridReflowInput
.mRows
.mSizes
.Length());
8783 if (numCols
> 0 && numRows
> 0) {
8784 const GridArea
gridArea(LineRange(0, numCols
), LineRange(0, numRows
));
8785 const LogicalRect gridAreaRect
=
8786 gridReflowInput
.ContainingBlockFor(gridArea
) +
8787 LogicalPoint(wm
, bp
.IStart(wm
), bp
.BStart(wm
));
8789 MOZ_ASSERT(bp
== aReflowInput
.ComputedLogicalPadding(wm
),
8790 "A scrolled inner frame shouldn't have any border!");
8791 const LogicalMargin
& padding
= bp
;
8792 nsRect physicalGridAreaRectWithPadding
=
8793 gridAreaRect
.GetPhysicalRect(wm
, containerSize
);
8794 physicalGridAreaRectWithPadding
.Inflate(padding
.GetPhysicalMargin(wm
));
8795 aDesiredSize
.mOverflowAreas
.UnionAllWith(physicalGridAreaRectWithPadding
);
8798 nsRect gridItemMarginBoxBounds
;
8799 for (const auto& item
: gridReflowInput
.mGridItems
) {
8800 gridItemMarginBoxBounds
=
8801 gridItemMarginBoxBounds
.Union(item
.mFrame
->GetMarginRect());
8803 aDesiredSize
.mOverflowAreas
.UnionAllWith(gridItemMarginBoxBounds
);
8806 // TODO: fix align-tracks alignment in fragments
8807 if ((IsMasonry(eLogicalAxisBlock
) && !prevInFlow
) ||
8808 IsMasonry(eLogicalAxisInline
)) {
8809 gridReflowInput
.AlignJustifyTracksInMasonryAxis(
8810 contentArea
.Size(wm
), aDesiredSize
.PhysicalSize());
8813 // Convert INCOMPLETE -> OVERFLOW_INCOMPLETE and zero bsize if we're an OC.
8814 if (HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
8815 if (!aStatus
.IsComplete()) {
8816 aStatus
.SetOverflowIncomplete();
8817 aStatus
.SetNextInFlowNeedsReflow();
8820 desiredSize
.BSize(wm
) = bSize
+ bp
.BStartEnd(wm
);
8821 aDesiredSize
.SetSize(wm
, desiredSize
);
8824 if (!gridReflowInput
.mInFragmentainer
) {
8825 MOZ_ASSERT(gridReflowInput
.mIter
.IsValid());
8826 auto sz
= frameRect
.Size();
8827 CalculateBaselines(BaselineSet::eBoth
, &gridReflowInput
.mIter
,
8828 &gridReflowInput
.mGridItems
, gridReflowInput
.mCols
, 0,
8829 gridReflowInput
.mCols
.mSizes
.Length(), wm
, sz
,
8830 bp
.IStart(wm
), bp
.IEnd(wm
), desiredSize
.ISize(wm
));
8831 CalculateBaselines(BaselineSet::eBoth
, &gridReflowInput
.mIter
,
8832 &gridReflowInput
.mGridItems
, gridReflowInput
.mRows
, 0,
8833 gridReflowInput
.mRows
.mSizes
.Length(), wm
, sz
,
8834 bp
.BStart(wm
), bp
.BEnd(wm
), desiredSize
.BSize(wm
));
8836 // Only compute 'first baseline' if this fragment contains the first track.
8837 // XXXmats maybe remove this condition? bug 1306499
8838 BaselineSet baselines
= BaselineSet::eNone
;
8839 if (gridReflowInput
.mStartRow
== 0 &&
8840 gridReflowInput
.mStartRow
!= gridReflowInput
.mNextFragmentStartRow
) {
8841 baselines
= BaselineSet::eFirst
;
8843 // Only compute 'last baseline' if this fragment contains the last track.
8844 // XXXmats maybe remove this condition? bug 1306499
8845 uint32_t len
= gridReflowInput
.mRows
.mSizes
.Length();
8846 if (gridReflowInput
.mStartRow
!= len
&&
8847 gridReflowInput
.mNextFragmentStartRow
== len
) {
8848 baselines
= BaselineSet(baselines
| BaselineSet::eLast
);
8850 Maybe
<CSSOrderAwareFrameIterator
> iter
;
8851 Maybe
<nsTArray
<GridItemInfo
>> gridItems
;
8852 if (baselines
!= BaselineSet::eNone
) {
8853 // We need to create a new iterator and GridItemInfo array because we
8854 // might have pushed some children at this point.
8855 // Even if the gridReflowInput iterator is invalid we can reuse its
8856 // state about order to optimize initialization of the new iterator.
8857 // An ordered child list can't become unordered by pushing frames.
8858 // An unordered list can become ordered in a number of cases, but we
8859 // ignore that here and guess that the child list is still unordered.
8860 // XXX this is O(n^2) in the number of items in this fragment: bug 1306705
8861 using Filter
= CSSOrderAwareFrameIterator::ChildFilter
;
8862 using Order
= CSSOrderAwareFrameIterator::OrderState
;
8863 bool ordered
= gridReflowInput
.mIter
.ItemsAreAlreadyInOrder();
8864 auto orderState
= ordered
? Order::Ordered
: Order::Unordered
;
8865 iter
.emplace(this, FrameChildListID::Principal
, Filter::SkipPlaceholders
,
8867 gridItems
.emplace();
8868 for (; !iter
->AtEnd(); iter
->Next()) {
8869 auto child
= **iter
;
8870 for (const auto& info
: gridReflowInput
.mGridItems
) {
8871 if (info
.mFrame
== child
) {
8872 gridItems
->AppendElement(info
);
8877 auto sz
= frameRect
.Size();
8878 CalculateBaselines(baselines
, iter
.ptrOr(nullptr), gridItems
.ptrOr(nullptr),
8879 gridReflowInput
.mCols
, 0,
8880 gridReflowInput
.mCols
.mSizes
.Length(), wm
, sz
,
8881 bp
.IStart(wm
), bp
.IEnd(wm
), desiredSize
.ISize(wm
));
8882 CalculateBaselines(baselines
, iter
.ptrOr(nullptr), gridItems
.ptrOr(nullptr),
8883 gridReflowInput
.mRows
, gridReflowInput
.mStartRow
,
8884 gridReflowInput
.mNextFragmentStartRow
, wm
, sz
,
8885 bp
.BStart(wm
), bp
.BEnd(wm
), desiredSize
.BSize(wm
));
8888 if (ShouldGenerateComputedInfo()) {
8889 // This state bit will never be cleared, since reflow can be called
8890 // multiple times in fragmented grids, and it's challenging to scope
8891 // the bit to only that sequence of calls. This is relatively harmless
8892 // since this bit is only set by accessing a ChromeOnly property, and
8893 // therefore can't unduly slow down normal web browsing.
8895 // Clear our GridFragmentInfo property, which might be holding a stale
8896 // dom::Grid object built from previously-computed info. This will
8897 // ensure that the next call to GetGridFragments will create a new one.
8898 if (mozilla::dom::Grid
* grid
= TakeProperty(GridFragmentInfo())) {
8899 grid
->ForgetFrame();
8902 // Now that we know column and row sizes and positions, set
8903 // the ComputedGridTrackInfo and related properties
8905 const auto* subgrid
= GetProperty(Subgrid::Prop());
8906 const auto* subgridColRange
= subgrid
&& IsSubgrid(eLogicalAxisInline
)
8907 ? &subgrid
->SubgridCols()
8910 LineNameMap
colLineNameMap(
8911 gridReflowInput
.mGridStyle
, GetImplicitNamedAreas(),
8912 gridReflowInput
.mColFunctions
, nullptr, subgridColRange
, true);
8913 uint32_t colTrackCount
= gridReflowInput
.mCols
.mSizes
.Length();
8914 nsTArray
<nscoord
> colTrackPositions(colTrackCount
);
8915 nsTArray
<nscoord
> colTrackSizes(colTrackCount
);
8916 nsTArray
<uint32_t> colTrackStates(colTrackCount
);
8917 nsTArray
<bool> colRemovedRepeatTracks(
8918 gridReflowInput
.mColFunctions
.mRemovedRepeatTracks
.Clone());
8920 for (const TrackSize
& sz
: gridReflowInput
.mCols
.mSizes
) {
8921 colTrackPositions
.AppendElement(sz
.mPosition
);
8922 colTrackSizes
.AppendElement(sz
.mBase
);
8924 ((col
>= gridReflowInput
.mColFunctions
.mRepeatAutoStart
) &&
8925 (col
< gridReflowInput
.mColFunctions
.mRepeatAutoEnd
));
8926 colTrackStates
.AppendElement(
8927 isRepeat
? (uint32_t)mozilla::dom::GridTrackState::Repeat
8928 : (uint32_t)mozilla::dom::GridTrackState::Static
);
8932 // Get the number of explicit tracks first. The order of argument evaluation
8933 // is implementation-defined. We should be OK here because colTrackSizes is
8934 // taken by rvalue, but computing the size first prevents any changes in the
8935 // argument types of the constructor from breaking this.
8936 const uint32_t numColExplicitTracks
=
8937 IsSubgrid(eLogicalAxisInline
)
8938 ? colTrackSizes
.Length()
8939 : gridReflowInput
.mColFunctions
.NumExplicitTracks();
8940 ComputedGridTrackInfo
* colInfo
= new ComputedGridTrackInfo(
8941 gridReflowInput
.mColFunctions
.mExplicitGridOffset
, numColExplicitTracks
,
8942 0, col
, std::move(colTrackPositions
), std::move(colTrackSizes
),
8943 std::move(colTrackStates
), std::move(colRemovedRepeatTracks
),
8944 gridReflowInput
.mColFunctions
.mRepeatAutoStart
,
8945 colLineNameMap
.GetResolvedLineNamesForComputedGridTrackInfo(),
8946 IsSubgrid(eLogicalAxisInline
), IsMasonry(eLogicalAxisInline
));
8947 SetProperty(GridColTrackInfo(), colInfo
);
8949 const auto* subgridRowRange
= subgrid
&& IsSubgrid(eLogicalAxisBlock
)
8950 ? &subgrid
->SubgridRows()
8952 LineNameMap
rowLineNameMap(
8953 gridReflowInput
.mGridStyle
, GetImplicitNamedAreas(),
8954 gridReflowInput
.mRowFunctions
, nullptr, subgridRowRange
, true);
8955 uint32_t rowTrackCount
= gridReflowInput
.mRows
.mSizes
.Length();
8956 nsTArray
<nscoord
> rowTrackPositions(rowTrackCount
);
8957 nsTArray
<nscoord
> rowTrackSizes(rowTrackCount
);
8958 nsTArray
<uint32_t> rowTrackStates(rowTrackCount
);
8959 nsTArray
<bool> rowRemovedRepeatTracks(
8960 gridReflowInput
.mRowFunctions
.mRemovedRepeatTracks
.Clone());
8962 for (const TrackSize
& sz
: gridReflowInput
.mRows
.mSizes
) {
8963 rowTrackPositions
.AppendElement(sz
.mPosition
);
8964 rowTrackSizes
.AppendElement(sz
.mBase
);
8966 ((row
>= gridReflowInput
.mRowFunctions
.mRepeatAutoStart
) &&
8967 (row
< gridReflowInput
.mRowFunctions
.mRepeatAutoEnd
));
8968 rowTrackStates
.AppendElement(
8969 isRepeat
? (uint32_t)mozilla::dom::GridTrackState::Repeat
8970 : (uint32_t)mozilla::dom::GridTrackState::Static
);
8974 // Get the number of explicit tracks first. The order of argument evaluation
8975 // is implementation-defined. We should be OK here because colTrackSizes is
8976 // taken by rvalue, but computing the size first prevents any changes in the
8977 // argument types of the constructor from breaking this.
8978 const uint32_t numRowExplicitTracks
=
8979 IsSubgrid(eLogicalAxisBlock
)
8980 ? rowTrackSizes
.Length()
8981 : gridReflowInput
.mRowFunctions
.NumExplicitTracks();
8982 // Row info has to accommodate fragmentation of the grid, which may happen
8983 // in later calls to Reflow. For now, presume that no more fragmentation
8985 ComputedGridTrackInfo
* rowInfo
= new ComputedGridTrackInfo(
8986 gridReflowInput
.mRowFunctions
.mExplicitGridOffset
, numRowExplicitTracks
,
8987 gridReflowInput
.mStartRow
, row
, std::move(rowTrackPositions
),
8988 std::move(rowTrackSizes
), std::move(rowTrackStates
),
8989 std::move(rowRemovedRepeatTracks
),
8990 gridReflowInput
.mRowFunctions
.mRepeatAutoStart
,
8991 rowLineNameMap
.GetResolvedLineNamesForComputedGridTrackInfo(),
8992 IsSubgrid(eLogicalAxisBlock
), IsMasonry(eLogicalAxisBlock
));
8993 SetProperty(GridRowTrackInfo(), rowInfo
);
8996 // This frame is fragmenting rows from a previous frame, so patch up
8997 // the prior GridRowTrackInfo with a new end row.
8999 // FIXME: This can be streamlined and/or removed when bug 1151204 lands.
9001 ComputedGridTrackInfo
* priorRowInfo
=
9002 prevInFlow
->GetProperty(GridRowTrackInfo());
9004 // Adjust track positions based on the first track in this fragment.
9005 if (priorRowInfo
->mPositions
.Length() >
9006 priorRowInfo
->mStartFragmentTrack
) {
9008 priorRowInfo
->mPositions
[priorRowInfo
->mStartFragmentTrack
];
9009 for (nscoord
& pos
: priorRowInfo
->mPositions
) {
9014 ComputedGridTrackInfo
* revisedPriorRowInfo
= new ComputedGridTrackInfo(
9015 priorRowInfo
->mNumLeadingImplicitTracks
,
9016 priorRowInfo
->mNumExplicitTracks
, priorRowInfo
->mStartFragmentTrack
,
9017 gridReflowInput
.mStartRow
, std::move(priorRowInfo
->mPositions
),
9018 std::move(priorRowInfo
->mSizes
), std::move(priorRowInfo
->mStates
),
9019 std::move(priorRowInfo
->mRemovedRepeatTracks
),
9020 priorRowInfo
->mRepeatFirstTrack
,
9021 std::move(priorRowInfo
->mResolvedLineNames
), priorRowInfo
->mIsSubgrid
,
9022 priorRowInfo
->mIsMasonry
);
9023 prevInFlow
->SetProperty(GridRowTrackInfo(), revisedPriorRowInfo
);
9026 // Generate the line info properties. We need to provide the number of
9027 // repeat tracks produced in the reflow. Only explicit names are assigned
9028 // to lines here; the mozilla::dom::GridLines class will later extract
9029 // implicit names from grid areas and assign them to the appropriate lines.
9031 auto& colFunctions
= gridReflowInput
.mColFunctions
;
9033 // Generate column lines first.
9034 uint32_t capacity
= gridReflowInput
.mCols
.mSizes
.Length();
9035 nsTArray
<nsTArray
<RefPtr
<nsAtom
>>> columnLineNames(capacity
);
9036 for (col
= 0; col
<= gridReflowInput
.mCols
.mSizes
.Length(); col
++) {
9037 // Offset col by the explicit grid offset, to get the original names.
9038 nsTArray
<RefPtr
<nsAtom
>> explicitNames
=
9039 colLineNameMap
.GetExplicitLineNamesAtIndex(
9040 col
- colFunctions
.mExplicitGridOffset
);
9042 columnLineNames
.EmplaceBack(std::move(explicitNames
));
9044 // Get the explicit names that follow a repeat auto declaration.
9045 nsTArray
<RefPtr
<nsAtom
>> colNamesFollowingRepeat
;
9046 nsTArray
<RefPtr
<nsAtom
>> colBeforeRepeatAuto
;
9047 nsTArray
<RefPtr
<nsAtom
>> colAfterRepeatAuto
;
9048 // Note: the following is only used for a non-subgridded axis.
9049 if (colLineNameMap
.HasRepeatAuto()) {
9050 MOZ_ASSERT(!colFunctions
.mTemplate
.IsSubgrid());
9051 // The line name list after the repeatAutoIndex holds the line names
9052 // for the first explicit line after the repeat auto declaration.
9053 uint32_t repeatAutoEnd
= colLineNameMap
.RepeatAutoStart() + 1;
9054 for (auto* list
: colLineNameMap
.ExpandedLineNames()[repeatAutoEnd
]) {
9055 for (auto& name
: list
->AsSpan()) {
9056 colNamesFollowingRepeat
.AppendElement(name
.AsAtom());
9059 auto names
= colLineNameMap
.TrackAutoRepeatLineNames();
9060 for (auto& name
: names
[0].AsSpan()) {
9061 colBeforeRepeatAuto
.AppendElement(name
.AsAtom());
9063 for (auto& name
: names
[1].AsSpan()) {
9064 colAfterRepeatAuto
.AppendElement(name
.AsAtom());
9068 ComputedGridLineInfo
* columnLineInfo
= new ComputedGridLineInfo(
9069 std::move(columnLineNames
), std::move(colBeforeRepeatAuto
),
9070 std::move(colAfterRepeatAuto
), std::move(colNamesFollowingRepeat
));
9071 SetProperty(GridColumnLineInfo(), columnLineInfo
);
9073 // Generate row lines next.
9074 auto& rowFunctions
= gridReflowInput
.mRowFunctions
;
9075 capacity
= gridReflowInput
.mRows
.mSizes
.Length();
9076 nsTArray
<nsTArray
<RefPtr
<nsAtom
>>> rowLineNames(capacity
);
9077 for (row
= 0; row
<= gridReflowInput
.mRows
.mSizes
.Length(); row
++) {
9078 // Offset row by the explicit grid offset, to get the original names.
9079 nsTArray
<RefPtr
<nsAtom
>> explicitNames
=
9080 rowLineNameMap
.GetExplicitLineNamesAtIndex(
9081 row
- rowFunctions
.mExplicitGridOffset
);
9082 rowLineNames
.EmplaceBack(std::move(explicitNames
));
9084 // Get the explicit names that follow a repeat auto declaration.
9085 nsTArray
<RefPtr
<nsAtom
>> rowNamesFollowingRepeat
;
9086 nsTArray
<RefPtr
<nsAtom
>> rowBeforeRepeatAuto
;
9087 nsTArray
<RefPtr
<nsAtom
>> rowAfterRepeatAuto
;
9088 // Note: the following is only used for a non-subgridded axis.
9089 if (rowLineNameMap
.HasRepeatAuto()) {
9090 MOZ_ASSERT(!rowFunctions
.mTemplate
.IsSubgrid());
9091 // The line name list after the repeatAutoIndex holds the line names
9092 // for the first explicit line after the repeat auto declaration.
9093 uint32_t repeatAutoEnd
= rowLineNameMap
.RepeatAutoStart() + 1;
9094 for (auto* list
: rowLineNameMap
.ExpandedLineNames()[repeatAutoEnd
]) {
9095 for (auto& name
: list
->AsSpan()) {
9096 rowNamesFollowingRepeat
.AppendElement(name
.AsAtom());
9099 auto names
= rowLineNameMap
.TrackAutoRepeatLineNames();
9100 for (auto& name
: names
[0].AsSpan()) {
9101 rowBeforeRepeatAuto
.AppendElement(name
.AsAtom());
9103 for (auto& name
: names
[1].AsSpan()) {
9104 rowAfterRepeatAuto
.AppendElement(name
.AsAtom());
9108 ComputedGridLineInfo
* rowLineInfo
= new ComputedGridLineInfo(
9109 std::move(rowLineNames
), std::move(rowBeforeRepeatAuto
),
9110 std::move(rowAfterRepeatAuto
), std::move(rowNamesFollowingRepeat
));
9111 SetProperty(GridRowLineInfo(), rowLineInfo
);
9113 // Generate area info for explicit areas. Implicit areas are handled
9115 if (!gridReflowInput
.mGridStyle
->mGridTemplateAreas
.IsNone()) {
9116 auto* areas
= new StyleOwnedSlice
<NamedArea
>(
9117 gridReflowInput
.mGridStyle
->mGridTemplateAreas
.AsAreas()->areas
);
9118 SetProperty(ExplicitNamedAreasProperty(), areas
);
9120 RemoveProperty(ExplicitNamedAreasProperty());
9125 SharedGridData
* sharedGridData
= GetProperty(SharedGridData::Prop());
9126 if (!aStatus
.IsFullyComplete()) {
9127 if (!sharedGridData
) {
9128 sharedGridData
= new SharedGridData
;
9129 SetProperty(SharedGridData::Prop(), sharedGridData
);
9131 sharedGridData
->mCols
.mSizes
= std::move(gridReflowInput
.mCols
.mSizes
);
9132 sharedGridData
->mCols
.mContentBoxSize
=
9133 gridReflowInput
.mCols
.mContentBoxSize
;
9134 sharedGridData
->mCols
.mBaselineSubtreeAlign
=
9135 gridReflowInput
.mCols
.mBaselineSubtreeAlign
;
9136 sharedGridData
->mCols
.mIsMasonry
= gridReflowInput
.mCols
.mIsMasonry
;
9137 sharedGridData
->mRows
.mSizes
= std::move(gridReflowInput
.mRows
.mSizes
);
9138 // Save the original row grid sizes and gaps so we can restore them later
9139 // in GridReflowInput::Initialize for the continuations.
9140 auto& origRowData
= sharedGridData
->mOriginalRowData
;
9141 origRowData
.ClearAndRetainStorage();
9142 origRowData
.SetCapacity(sharedGridData
->mRows
.mSizes
.Length());
9143 nscoord prevTrackEnd
= 0;
9144 for (auto& sz
: sharedGridData
->mRows
.mSizes
) {
9145 SharedGridData::RowData data
= {sz
.mBase
, sz
.mPosition
- prevTrackEnd
};
9146 origRowData
.AppendElement(data
);
9147 prevTrackEnd
= sz
.mPosition
+ sz
.mBase
;
9149 sharedGridData
->mRows
.mContentBoxSize
=
9150 gridReflowInput
.mRows
.mContentBoxSize
;
9151 sharedGridData
->mRows
.mBaselineSubtreeAlign
=
9152 gridReflowInput
.mRows
.mBaselineSubtreeAlign
;
9153 sharedGridData
->mRows
.mIsMasonry
= gridReflowInput
.mRows
.mIsMasonry
;
9154 sharedGridData
->mGridItems
= std::move(gridReflowInput
.mGridItems
);
9155 sharedGridData
->mAbsPosItems
= std::move(gridReflowInput
.mAbsPosItems
);
9157 sharedGridData
->mGenerateComputedGridInfo
= ShouldGenerateComputedInfo();
9158 } else if (sharedGridData
&& !GetNextInFlow()) {
9159 RemoveProperty(SharedGridData::Prop());
9163 FinishAndStoreOverflow(&aDesiredSize
);
9166 void nsGridContainerFrame::UpdateSubgridFrameState() {
9167 nsFrameState oldBits
= GetStateBits() & kIsSubgridBits
;
9168 nsFrameState newBits
= ComputeSelfSubgridMasonryBits() & kIsSubgridBits
;
9169 if (newBits
!= oldBits
) {
9170 RemoveStateBits(kIsSubgridBits
);
9172 RemoveProperty(Subgrid::Prop());
9174 AddStateBits(newBits
);
9179 nsFrameState
nsGridContainerFrame::ComputeSelfSubgridMasonryBits() const {
9180 // 'contain:layout/paint' makes us an "independent formatting context",
9181 // which prevents us from being a subgrid in this case (but not always).
9182 // We will also need to check our containing scroll frame for this property.
9183 // https://drafts.csswg.org/css-display-3/#establish-an-independent-formatting-context
9184 const auto* display
= StyleDisplay();
9185 const bool inhibitSubgrid
=
9186 display
->IsContainLayout() || display
->IsContainPaint();
9188 nsFrameState bits
= nsFrameState(0);
9189 const auto* pos
= StylePosition();
9191 // We can only have masonry layout in one axis.
9192 if (pos
->mGridTemplateRows
.IsMasonry()) {
9193 bits
|= NS_STATE_GRID_IS_ROW_MASONRY
;
9194 } else if (pos
->mGridTemplateColumns
.IsMasonry()) {
9195 bits
|= NS_STATE_GRID_IS_COL_MASONRY
;
9198 // Skip our scroll frame and such if we have it.
9199 // This will store the outermost frame that shares our content node:
9200 const nsIFrame
* outerFrame
= this;
9201 // ...and this will store that frame's parent:
9202 auto* parent
= GetParent();
9203 while (parent
&& parent
->GetContent() == GetContent()) {
9204 // If we find our containing frame has 'contain:layout/paint' we can't be
9205 // subgrid, for the same reasons as above. This can happen when this frame
9206 // is itself a grid item.
9207 const auto* parentDisplay
= parent
->StyleDisplay();
9208 if (parentDisplay
->IsContainLayout() || parentDisplay
->IsContainPaint()) {
9209 return nsFrameState(0);
9211 outerFrame
= parent
;
9212 parent
= parent
->GetParent();
9214 const nsGridContainerFrame
* gridParent
= do_QueryFrame(parent
);
9217 GetWritingMode().IsOrthogonalTo(parent
->GetWritingMode());
9218 // NOTE: our NS_FRAME_OUT_OF_FLOW isn't set yet so we check our style.
9220 outerFrame
->StyleDisplay()->IsAbsolutelyPositionedStyle();
9222 pos
->mGridTemplateColumns
.IsSubgrid() && !inhibitSubgrid
;
9223 // Subgridding a parent masonry axis makes us use masonry layout too,
9224 // unless our other axis is a masonry axis.
9226 parent
->HasAnyStateBits(isOrthogonal
? NS_STATE_GRID_IS_ROW_MASONRY
9227 : NS_STATE_GRID_IS_COL_MASONRY
)) {
9228 isColSubgrid
= false;
9229 if (!HasAnyStateBits(NS_STATE_GRID_IS_ROW_MASONRY
)) {
9230 bits
|= NS_STATE_GRID_IS_COL_MASONRY
;
9233 // OOF subgrids don't create tracks in the parent, so we need to check that
9234 // it has one anyway. Otherwise we refuse to subgrid that axis since we
9235 // can't place grid items inside a subgrid without at least one track.
9236 if (isColSubgrid
&& isOutOfFlow
) {
9237 auto parentAxis
= isOrthogonal
? eLogicalAxisBlock
: eLogicalAxisInline
;
9238 if (!gridParent
->WillHaveAtLeastOneTrackInAxis(parentAxis
)) {
9239 isColSubgrid
= false;
9243 bits
|= NS_STATE_GRID_IS_COL_SUBGRID
;
9246 bool isRowSubgrid
= pos
->mGridTemplateRows
.IsSubgrid() && !inhibitSubgrid
;
9248 parent
->HasAnyStateBits(isOrthogonal
? NS_STATE_GRID_IS_COL_MASONRY
9249 : NS_STATE_GRID_IS_ROW_MASONRY
)) {
9250 isRowSubgrid
= false;
9251 if (!HasAnyStateBits(NS_STATE_GRID_IS_COL_MASONRY
)) {
9252 bits
|= NS_STATE_GRID_IS_ROW_MASONRY
;
9255 if (isRowSubgrid
&& isOutOfFlow
) {
9256 auto parentAxis
= isOrthogonal
? eLogicalAxisInline
: eLogicalAxisBlock
;
9257 if (!gridParent
->WillHaveAtLeastOneTrackInAxis(parentAxis
)) {
9258 isRowSubgrid
= false;
9262 bits
|= NS_STATE_GRID_IS_ROW_SUBGRID
;
9268 bool nsGridContainerFrame::WillHaveAtLeastOneTrackInAxis(
9269 LogicalAxis aAxis
) const {
9270 if (IsSubgrid(aAxis
)) {
9271 // This is enforced by refusing to be a subgrid unless our parent has
9272 // at least one track in aAxis by ComputeSelfSubgridMasonryBits above.
9275 if (IsMasonry(aAxis
)) {
9278 const auto* pos
= StylePosition();
9279 const auto& gridTemplate
= aAxis
== eLogicalAxisBlock
9280 ? pos
->mGridTemplateRows
9281 : pos
->mGridTemplateColumns
;
9282 if (gridTemplate
.IsTrackList()) {
9285 for (nsIFrame
* child
: PrincipalChildList()) {
9286 if (!child
->IsPlaceholderFrame()) {
9287 // A grid item triggers at least one implicit track in each axis.
9291 if (!pos
->mGridTemplateAreas
.IsNone()) {
9297 void nsGridContainerFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
9298 nsIFrame
* aPrevInFlow
) {
9299 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
9301 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER
)) {
9302 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
9305 nsFrameState bits
= nsFrameState(0);
9306 if (MOZ_LIKELY(!aPrevInFlow
)) {
9307 bits
= ComputeSelfSubgridMasonryBits();
9309 bits
= aPrevInFlow
->GetStateBits() &
9310 (NS_STATE_GRID_IS_ROW_MASONRY
| NS_STATE_GRID_IS_COL_MASONRY
|
9311 kIsSubgridBits
| NS_STATE_GRID_HAS_COL_SUBGRID_ITEM
|
9312 NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM
);
9317 void nsGridContainerFrame::DidSetComputedStyle(ComputedStyle
* aOldStyle
) {
9318 nsContainerFrame::DidSetComputedStyle(aOldStyle
);
9321 return; // Init() already initialized the bits.
9323 UpdateSubgridFrameState();
9326 nscoord
nsGridContainerFrame::IntrinsicISize(gfxContext
* aRenderingContext
,
9327 IntrinsicISizeType aType
) {
9328 // Calculate the sum of column sizes under intrinsic sizing.
9329 // http://dev.w3.org/csswg/css-grid/#intrinsic-sizes
9330 NormalizeChildLists();
9331 GridReflowInput
state(this, *aRenderingContext
);
9332 InitImplicitNamedAreas(state
.mGridStyle
); // XXX optimize
9334 // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
9335 // https://drafts.csswg.org/css-grid/#auto-repeat
9336 // They're only used for auto-repeat so we skip computing them otherwise.
9337 RepeatTrackSizingInput
repeatSizing(state
.mWM
);
9338 if (!IsColSubgrid() && state
.mColFunctions
.mHasRepeatAuto
) {
9339 repeatSizing
.InitFromStyle(eLogicalAxisInline
, state
.mWM
,
9340 state
.mFrame
->Style());
9342 if ((!IsRowSubgrid() && state
.mRowFunctions
.mHasRepeatAuto
&&
9343 !(state
.mGridStyle
->mGridAutoFlow
& StyleGridAutoFlow::ROW
)) ||
9344 IsMasonry(eLogicalAxisInline
)) {
9345 // Only 'grid-auto-flow:column' can create new implicit columns, so that's
9346 // the only case where our block-size can affect the number of columns.
9347 // Masonry layout always depends on how many rows we have though.
9348 repeatSizing
.InitFromStyle(eLogicalAxisBlock
, state
.mWM
,
9349 state
.mFrame
->Style());
9353 if (MOZ_LIKELY(!IsSubgrid())) {
9354 grid
.PlaceGridItems(state
, repeatSizing
); // XXX optimize
9356 auto* subgrid
= GetProperty(Subgrid::Prop());
9357 state
.mGridItems
= subgrid
->mGridItems
.Clone();
9358 state
.mAbsPosItems
= subgrid
->mAbsPosItems
.Clone();
9359 grid
.mGridColEnd
= subgrid
->mGridColEnd
;
9360 grid
.mGridRowEnd
= subgrid
->mGridRowEnd
;
9363 auto constraint
= aType
== IntrinsicISizeType::MinISize
9364 ? SizingConstraint::MinContent
9365 : SizingConstraint::MaxContent
;
9366 if (IsMasonry(eLogicalAxisInline
)) {
9367 ReflowOutput
desiredSize(state
.mWM
);
9368 nsSize containerSize
;
9369 LogicalRect
contentArea(state
.mWM
);
9370 nsReflowStatus status
;
9371 state
.mRows
.mSizes
.SetLength(grid
.mGridRowEnd
);
9372 state
.CalculateTrackSizesForAxis(eLogicalAxisInline
, grid
,
9373 NS_UNCONSTRAINEDSIZE
, constraint
);
9374 return MasonryLayout(state
, contentArea
, constraint
, desiredSize
, status
,
9375 nullptr, containerSize
);
9378 if (grid
.mGridColEnd
== 0) {
9382 state
.CalculateTrackSizesForAxis(eLogicalAxisInline
, grid
,
9383 NS_UNCONSTRAINEDSIZE
, constraint
);
9385 if (MOZ_LIKELY(!IsSubgrid())) {
9386 return state
.mCols
.SumOfGridTracksAndGaps();
9388 const auto& last
= state
.mCols
.mSizes
.LastElement();
9389 return last
.mPosition
+ last
.mBase
;
9392 nscoord
nsGridContainerFrame::GetMinISize(gfxContext
* aRC
) {
9393 auto* f
= static_cast<nsGridContainerFrame
*>(FirstContinuation());
9395 return f
->GetMinISize(aRC
);
9398 DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize
);
9399 if (mCachedMinISize
== NS_INTRINSIC_ISIZE_UNKNOWN
) {
9400 Maybe
<nscoord
> containISize
= ContainIntrinsicISize();
9401 mCachedMinISize
= containISize
9403 : IntrinsicISize(aRC
, IntrinsicISizeType::MinISize
);
9405 return mCachedMinISize
;
9408 nscoord
nsGridContainerFrame::GetPrefISize(gfxContext
* aRC
) {
9409 auto* f
= static_cast<nsGridContainerFrame
*>(FirstContinuation());
9411 return f
->GetPrefISize(aRC
);
9414 DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize
);
9415 if (mCachedPrefISize
== NS_INTRINSIC_ISIZE_UNKNOWN
) {
9416 Maybe
<nscoord
> containISize
= ContainIntrinsicISize();
9417 mCachedPrefISize
= containISize
9419 : IntrinsicISize(aRC
, IntrinsicISizeType::PrefISize
);
9421 return mCachedPrefISize
;
9424 void nsGridContainerFrame::MarkIntrinsicISizesDirty() {
9425 mCachedMinISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
9426 mCachedPrefISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
9427 for (auto& perAxisBaseline
: mBaseline
) {
9428 for (auto& baseline
: perAxisBaseline
) {
9429 baseline
= NS_INTRINSIC_ISIZE_UNKNOWN
;
9432 nsContainerFrame::MarkIntrinsicISizesDirty();
9435 void nsGridContainerFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
9436 const nsDisplayListSet
& aLists
) {
9437 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
9438 if (GetPrevInFlow()) {
9439 DisplayOverflowContainers(aBuilder
, aLists
);
9442 // Our children are all grid-level boxes, which behave the same as
9443 // inline-blocks in painting, so their borders/backgrounds all go on
9444 // the BlockBorderBackgrounds list.
9445 typedef CSSOrderAwareFrameIterator::OrderState OrderState
;
9447 HasAnyStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER
)
9448 ? OrderState::Ordered
9449 : OrderState::Unordered
;
9450 CSSOrderAwareFrameIterator
iter(
9451 this, FrameChildListID::Principal
,
9452 CSSOrderAwareFrameIterator::ChildFilter::IncludeAll
, order
);
9453 const auto flags
= DisplayFlagsForFlexOrGridItem();
9454 for (; !iter
.AtEnd(); iter
.Next()) {
9455 nsIFrame
* child
= *iter
;
9456 BuildDisplayListForChild(aBuilder
, child
, aLists
, flags
);
9460 bool nsGridContainerFrame::DrainSelfOverflowList() {
9461 return DrainAndMergeSelfOverflowList();
9464 void nsGridContainerFrame::AppendFrames(ChildListID aListID
,
9465 nsFrameList
&& aFrameList
) {
9466 NoteNewChildren(aListID
, aFrameList
);
9467 nsContainerFrame::AppendFrames(aListID
, std::move(aFrameList
));
9470 void nsGridContainerFrame::InsertFrames(
9471 ChildListID aListID
, nsIFrame
* aPrevFrame
,
9472 const nsLineList::iterator
* aPrevFrameLine
, nsFrameList
&& aFrameList
) {
9473 NoteNewChildren(aListID
, aFrameList
);
9474 nsContainerFrame::InsertFrames(aListID
, aPrevFrame
, aPrevFrameLine
,
9475 std::move(aFrameList
));
9478 void nsGridContainerFrame::RemoveFrame(DestroyContext
& aContext
,
9479 ChildListID aListID
,
9480 nsIFrame
* aOldFrame
) {
9481 MOZ_ASSERT(aListID
== FrameChildListID::Principal
, "unexpected child list");
9484 SetDidPushItemsBitIfNeeded(aListID
, aOldFrame
);
9487 nsContainerFrame::RemoveFrame(aContext
, aListID
, aOldFrame
);
9490 StyleAlignFlags
nsGridContainerFrame::CSSAlignmentForAbsPosChild(
9491 const ReflowInput
& aChildRI
, LogicalAxis aLogicalAxis
) const {
9492 MOZ_ASSERT(aChildRI
.mFrame
->IsAbsolutelyPositioned(),
9493 "This method should only be called for abspos children");
9495 StyleAlignFlags alignment
=
9496 (aLogicalAxis
== eLogicalAxisInline
)
9497 ? aChildRI
.mStylePosition
->UsedJustifySelf(Style())._0
9498 : aChildRI
.mStylePosition
->UsedAlignSelf(Style())._0
;
9500 // Extract and strip the flag bits
9501 StyleAlignFlags alignmentFlags
= alignment
& StyleAlignFlags::FLAG_BITS
;
9502 alignment
&= ~StyleAlignFlags::FLAG_BITS
;
9504 if (alignment
== StyleAlignFlags::NORMAL
) {
9505 // "the 'normal' keyword behaves as 'start' on replaced
9506 // absolutely-positioned boxes, and behaves as 'stretch' on all other
9507 // absolutely-positioned boxes."
9508 // https://drafts.csswg.org/css-align/#align-abspos
9509 // https://drafts.csswg.org/css-align/#justify-abspos
9510 alignment
= aChildRI
.mFrame
->IsFrameOfType(nsIFrame::eReplaced
)
9511 ? StyleAlignFlags::START
9512 : StyleAlignFlags::STRETCH
;
9513 } else if (alignment
== StyleAlignFlags::FLEX_START
) {
9514 alignment
= StyleAlignFlags::START
;
9515 } else if (alignment
== StyleAlignFlags::FLEX_END
) {
9516 alignment
= StyleAlignFlags::END
;
9517 } else if (alignment
== StyleAlignFlags::LEFT
||
9518 alignment
== StyleAlignFlags::RIGHT
) {
9519 if (aLogicalAxis
== eLogicalAxisInline
) {
9520 const bool isLeft
= (alignment
== StyleAlignFlags::LEFT
);
9521 WritingMode wm
= GetWritingMode();
9522 alignment
= (isLeft
== wm
.IsBidiLTR()) ? StyleAlignFlags::START
9523 : StyleAlignFlags::END
;
9525 alignment
= StyleAlignFlags::START
;
9527 } else if (alignment
== StyleAlignFlags::BASELINE
) {
9528 alignment
= StyleAlignFlags::START
;
9529 } else if (alignment
== StyleAlignFlags::LAST_BASELINE
) {
9530 alignment
= StyleAlignFlags::END
;
9533 return (alignment
| alignmentFlags
);
9536 nscoord
nsGridContainerFrame::SynthesizeBaseline(
9537 const FindItemInGridOrderResult
& aGridOrderItem
, LogicalAxis aAxis
,
9538 BaselineSharingGroup aGroup
, const nsSize
& aCBPhysicalSize
, nscoord aCBSize
,
9539 WritingMode aCBWM
) {
9540 if (MOZ_UNLIKELY(!aGridOrderItem
.mItem
)) {
9541 // No item in this fragment - synthesize a baseline from our border-box.
9542 return ::SynthesizeBaselineFromBorderBox(aGroup
, aCBWM
, aCBSize
);
9544 auto GetBBaseline
= [](BaselineSharingGroup aGroup
, WritingMode aWM
,
9545 const nsIFrame
* aFrame
, nscoord
* aBaseline
) {
9546 return aGroup
== BaselineSharingGroup::First
9547 ? nsLayoutUtils::GetFirstLineBaseline(aWM
, aFrame
, aBaseline
)
9548 : nsLayoutUtils::GetLastLineBaseline(aWM
, aFrame
, aBaseline
);
9550 nsIFrame
* child
= aGridOrderItem
.mItem
->mFrame
;
9551 nsGridContainerFrame
* grid
= do_QueryFrame(child
);
9552 auto childWM
= child
->GetWritingMode();
9553 bool isOrthogonal
= aCBWM
.IsOrthogonalTo(childWM
);
9557 if (aAxis
== eLogicalAxisBlock
) {
9558 start
= child
->GetLogicalNormalPosition(aCBWM
, aCBPhysicalSize
).B(aCBWM
);
9559 size
= child
->BSize(aCBWM
);
9560 if (grid
&& aGridOrderItem
.mIsInEdgeTrack
) {
9561 baseline
= isOrthogonal
? grid
->GetIBaseline(aGroup
)
9562 : grid
->GetBBaseline(aGroup
);
9563 } else if (!isOrthogonal
&& aGridOrderItem
.mIsInEdgeTrack
) {
9565 ->GetNaturalBaselineBOffset(childWM
, aGroup
,
9566 BaselineExportContext::Other
)
9567 .valueOrFrom([aGroup
, child
, childWM
]() {
9568 return Baseline::SynthesizeBOffsetFromBorderBox(
9569 child
, childWM
, aGroup
);
9572 baseline
= ::SynthesizeBaselineFromBorderBox(aGroup
, childWM
, size
);
9575 start
= child
->GetLogicalNormalPosition(aCBWM
, aCBPhysicalSize
).I(aCBWM
);
9576 size
= child
->ISize(aCBWM
);
9577 if (grid
&& aGridOrderItem
.mIsInEdgeTrack
) {
9578 baseline
= isOrthogonal
? grid
->GetBBaseline(aGroup
)
9579 : grid
->GetIBaseline(aGroup
);
9580 } else if (isOrthogonal
&& aGridOrderItem
.mIsInEdgeTrack
&&
9581 GetBBaseline(aGroup
, childWM
, child
, &baseline
)) {
9582 if (aGroup
== BaselineSharingGroup::Last
) {
9583 baseline
= size
- baseline
; // convert to distance from border-box end
9586 baseline
= ::SynthesizeBaselineFromBorderBox(aGroup
, childWM
, size
);
9589 return aGroup
== BaselineSharingGroup::First
9591 : aCBSize
- start
- size
+ baseline
;
9594 void nsGridContainerFrame::CalculateBaselines(
9595 BaselineSet aBaselineSet
, CSSOrderAwareFrameIterator
* aIter
,
9596 const nsTArray
<GridItemInfo
>* aGridItems
, const Tracks
& aTracks
,
9597 uint32_t aFragmentStartTrack
, uint32_t aFirstExcludedTrack
, WritingMode aWM
,
9598 const nsSize
& aCBPhysicalSize
, nscoord aCBBorderPaddingStart
,
9599 nscoord aCBBorderPaddingEnd
, nscoord aCBSize
) {
9600 const auto axis
= aTracks
.mAxis
;
9601 auto firstBaseline
= aTracks
.mBaseline
[BaselineSharingGroup::First
];
9602 if (!(aBaselineSet
& BaselineSet::eFirst
)) {
9603 mBaseline
[axis
][BaselineSharingGroup::First
] =
9604 ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::First
, aWM
,
9606 } else if (firstBaseline
== NS_INTRINSIC_ISIZE_UNKNOWN
) {
9607 FindItemInGridOrderResult gridOrderFirstItem
= FindFirstItemInGridOrder(
9608 *aIter
, *aGridItems
,
9609 axis
== eLogicalAxisBlock
? &GridArea::mRows
: &GridArea::mCols
,
9610 axis
== eLogicalAxisBlock
? &GridArea::mCols
: &GridArea::mRows
,
9611 aFragmentStartTrack
);
9612 mBaseline
[axis
][BaselineSharingGroup::First
] = SynthesizeBaseline(
9613 gridOrderFirstItem
, axis
, BaselineSharingGroup::First
, aCBPhysicalSize
,
9616 // We have a 'first baseline' group in the start track in this fragment.
9617 // Convert it from track to grid container border-box coordinates.
9618 MOZ_ASSERT(!aGridItems
->IsEmpty());
9619 nscoord gapBeforeStartTrack
=
9620 aFragmentStartTrack
== 0
9621 ? aTracks
.GridLineEdge(aFragmentStartTrack
,
9622 GridLineSide::AfterGridGap
)
9623 : nscoord(0); // no content gap at start of fragment
9624 mBaseline
[axis
][BaselineSharingGroup::First
] =
9625 aCBBorderPaddingStart
+ gapBeforeStartTrack
+ firstBaseline
;
9628 auto lastBaseline
= aTracks
.mBaseline
[BaselineSharingGroup::Last
];
9629 if (!(aBaselineSet
& BaselineSet::eLast
)) {
9630 mBaseline
[axis
][BaselineSharingGroup::Last
] =
9631 ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::Last
, aWM
,
9633 } else if (lastBaseline
== NS_INTRINSIC_ISIZE_UNKNOWN
) {
9634 // For finding items for the 'last baseline' we need to create a reverse
9635 // iterator ('aIter' is the forward iterator from the GridReflowInput).
9636 using Iter
= ReverseCSSOrderAwareFrameIterator
;
9637 auto orderState
= aIter
->ItemsAreAlreadyInOrder()
9638 ? Iter::OrderState::Ordered
9639 : Iter::OrderState::Unordered
;
9640 Iter
iter(this, FrameChildListID::Principal
,
9641 Iter::ChildFilter::SkipPlaceholders
, orderState
);
9642 iter
.SetItemCount(aGridItems
->Length());
9643 FindItemInGridOrderResult gridOrderLastItem
= FindLastItemInGridOrder(
9645 axis
== eLogicalAxisBlock
? &GridArea::mRows
: &GridArea::mCols
,
9646 axis
== eLogicalAxisBlock
? &GridArea::mCols
: &GridArea::mRows
,
9647 aFragmentStartTrack
, aFirstExcludedTrack
);
9648 mBaseline
[axis
][BaselineSharingGroup::Last
] =
9649 SynthesizeBaseline(gridOrderLastItem
, axis
, BaselineSharingGroup::Last
,
9650 aCBPhysicalSize
, aCBSize
, aWM
);
9652 // We have a 'last baseline' group in the end track in this fragment.
9653 // Convert it from track to grid container border-box coordinates.
9654 MOZ_ASSERT(!aGridItems
->IsEmpty());
9655 auto borderBoxStartToEndOfEndTrack
=
9656 aCBBorderPaddingStart
+
9657 aTracks
.GridLineEdge(aFirstExcludedTrack
, GridLineSide::BeforeGridGap
) -
9658 aTracks
.GridLineEdge(aFragmentStartTrack
, GridLineSide::BeforeGridGap
);
9659 mBaseline
[axis
][BaselineSharingGroup::Last
] =
9660 (aCBSize
- borderBoxStartToEndOfEndTrack
) + lastBaseline
;
9664 #ifdef DEBUG_FRAME_DUMP
9665 nsresult
nsGridContainerFrame::GetFrameName(nsAString
& aResult
) const {
9666 return MakeFrameName(u
"GridContainer"_ns
, aResult
);
9669 void nsGridContainerFrame::ExtraContainerFrameInfo(nsACString
& aTo
) const {
9670 if (const void* const subgrid
= GetProperty(Subgrid::Prop())) {
9671 aTo
+= nsPrintfCString(" [subgrid=%p]", subgrid
);
9677 /* static */ nsGridContainerFrame::FindItemInGridOrderResult
9678 nsGridContainerFrame::FindFirstItemInGridOrder(
9679 CSSOrderAwareFrameIterator
& aIter
, const nsTArray
<GridItemInfo
>& aGridItems
,
9680 LineRange
GridArea::*aMajor
, LineRange
GridArea::*aMinor
,
9681 uint32_t aFragmentStartTrack
) {
9682 FindItemInGridOrderResult result
= {nullptr, false};
9683 uint32_t minMajor
= kTranslatedMaxLine
+ 1;
9684 uint32_t minMinor
= kTranslatedMaxLine
+ 1;
9686 for (; !aIter
.AtEnd(); aIter
.Next()) {
9687 const GridItemInfo
& item
= aGridItems
[aIter
.ItemIndex()];
9688 if ((item
.mArea
.*aMajor
).mEnd
<= aFragmentStartTrack
) {
9689 continue; // item doesn't span any track in this fragment
9691 uint32_t major
= (item
.mArea
.*aMajor
).mStart
;
9692 uint32_t minor
= (item
.mArea
.*aMinor
).mStart
;
9693 if (major
< minMajor
|| (major
== minMajor
&& minor
< minMinor
)) {
9696 result
.mItem
= &item
;
9697 result
.mIsInEdgeTrack
= major
== 0U;
9703 /* static */ nsGridContainerFrame::FindItemInGridOrderResult
9704 nsGridContainerFrame::FindLastItemInGridOrder(
9705 ReverseCSSOrderAwareFrameIterator
& aIter
,
9706 const nsTArray
<GridItemInfo
>& aGridItems
, LineRange
GridArea::*aMajor
,
9707 LineRange
GridArea::*aMinor
, uint32_t aFragmentStartTrack
,
9708 uint32_t aFirstExcludedTrack
) {
9709 FindItemInGridOrderResult result
= {nullptr, false};
9710 int32_t maxMajor
= -1;
9711 int32_t maxMinor
= -1;
9713 int32_t lastMajorTrack
= int32_t(aFirstExcludedTrack
) - 1;
9714 for (; !aIter
.AtEnd(); aIter
.Next()) {
9715 const GridItemInfo
& item
= aGridItems
[aIter
.ItemIndex()];
9716 // Subtract 1 from the end line to get the item's last track index.
9717 int32_t major
= (item
.mArea
.*aMajor
).mEnd
- 1;
9718 // Currently, this method is only called with aFirstExcludedTrack ==
9719 // the first track in the next fragment, so we take the opportunity
9720 // to assert this item really belongs to this fragment.
9721 MOZ_ASSERT((item
.mArea
.*aMajor
).mStart
< aFirstExcludedTrack
,
9722 "found an item that belongs to some later fragment");
9723 if (major
< int32_t(aFragmentStartTrack
)) {
9724 continue; // item doesn't span any track in this fragment
9726 int32_t minor
= (item
.mArea
.*aMinor
).mEnd
- 1;
9727 MOZ_ASSERT(minor
>= 0 && major
>= 0, "grid item must have span >= 1");
9728 if (major
> maxMajor
|| (major
== maxMajor
&& minor
> maxMinor
)) {
9731 result
.mItem
= &item
;
9732 result
.mIsInEdgeTrack
= major
== lastMajorTrack
;
9738 nsGridContainerFrame::UsedTrackSizes
* nsGridContainerFrame::GetUsedTrackSizes()
9740 return GetProperty(UsedTrackSizes::Prop());
9743 void nsGridContainerFrame::StoreUsedTrackSizes(
9744 LogicalAxis aAxis
, const nsTArray
<TrackSize
>& aSizes
) {
9745 auto* uts
= GetUsedTrackSizes();
9747 uts
= new UsedTrackSizes();
9748 SetProperty(UsedTrackSizes::Prop(), uts
);
9750 uts
->mSizes
[aAxis
] = aSizes
.Clone();
9751 uts
->mCanResolveLineRangeSize
[aAxis
] = true;
9752 // XXX is resetting these bits necessary?
9753 for (auto& sz
: uts
->mSizes
[aAxis
]) {
9754 sz
.mState
&= ~(TrackSize::eFrozen
| TrackSize::eSkipGrowUnlimited
|
9755 TrackSize::eInfinitelyGrowable
);
9760 void nsGridContainerFrame::SetInitialChildList(ChildListID aListID
,
9761 nsFrameList
&& aChildList
) {
9762 ChildListIDs supportedLists
= {FrameChildListID::Principal
};
9763 // We don't handle the FrameChildListID::Backdrop frames in any way, but it
9764 // only contains a placeholder for ::backdrop which is OK to not reflow (for
9766 supportedLists
+= FrameChildListID::Backdrop
;
9767 MOZ_ASSERT(supportedLists
.contains(aListID
), "unexpected child list");
9769 return nsContainerFrame::SetInitialChildList(aListID
, std::move(aChildList
));
9772 void nsGridContainerFrame::TrackSize::DumpStateBits(StateBits aState
) {
9774 if (aState
& eAutoMinSizing
) {
9775 printf("auto-min ");
9776 } else if (aState
& eMinContentMinSizing
) {
9777 printf("min-content ");
9778 } else if (aState
& eMaxContentMinSizing
) {
9779 printf("max-content ");
9782 if (aState
& eAutoMaxSizing
) {
9784 } else if (aState
& eMinContentMaxSizing
) {
9785 printf("min-content ");
9786 } else if (aState
& eMaxContentMaxSizing
) {
9787 printf("max-content ");
9788 } else if (aState
& eFlexMaxSizing
) {
9791 if (aState
& eFrozen
) {
9794 if (aState
& eModified
) {
9795 printf("modified ");
9797 if (aState
& eBreakBefore
) {
9798 printf("break-before ");
9802 void nsGridContainerFrame::TrackSize::Dump() const {
9803 printf("mPosition=%d mBase=%d mLimit=%d ", mPosition
, mBase
, mLimit
);
9804 DumpStateBits(mState
);
9809 nsGridContainerFrame
* nsGridContainerFrame::GetGridContainerFrame(
9811 nsGridContainerFrame
* gridFrame
= nullptr;
9814 nsIFrame
* inner
= aFrame
;
9815 if (MOZ_UNLIKELY(aFrame
->IsFieldSetFrame())) {
9816 inner
= static_cast<nsFieldSetFrame
*>(aFrame
)->GetInner();
9818 // Since "Get" methods like GetInner and GetContentInsertionFrame can
9819 // return null, we check the return values before dereferencing. Our
9820 // calling pattern makes this unlikely, but we're being careful.
9821 nsIFrame
* insertionFrame
=
9822 inner
? inner
->GetContentInsertionFrame() : nullptr;
9823 nsIFrame
* possibleGridFrame
= insertionFrame
? insertionFrame
: aFrame
;
9824 gridFrame
= possibleGridFrame
->IsGridContainerFrame()
9825 ? static_cast<nsGridContainerFrame
*>(possibleGridFrame
)
9831 nsGridContainerFrame
* nsGridContainerFrame::GetGridFrameWithComputedInfo(
9833 nsGridContainerFrame
* gridFrame
= GetGridContainerFrame(aFrame
);
9838 auto HasComputedInfo
= [](const nsGridContainerFrame
& aFrame
) -> bool {
9839 return aFrame
.HasProperty(GridColTrackInfo()) &&
9840 aFrame
.HasProperty(GridRowTrackInfo()) &&
9841 aFrame
.HasProperty(GridColumnLineInfo()) &&
9842 aFrame
.HasProperty(GridRowLineInfo());
9845 if (HasComputedInfo(*gridFrame
)) {
9849 // Trigger a reflow that generates additional grid property data.
9850 // Hold onto aFrame while we do this, in case reflow destroys it.
9851 AutoWeakFrame
weakFrameRef(gridFrame
);
9853 RefPtr
<mozilla::PresShell
> presShell
= gridFrame
->PresShell();
9854 gridFrame
->SetShouldGenerateComputedInfo(true);
9855 presShell
->FrameNeedsReflow(gridFrame
, IntrinsicDirty::None
,
9857 presShell
->FlushPendingNotifications(FlushType::Layout
);
9859 // If the weakFrameRef is no longer valid, then we must bail out.
9860 if (!weakFrameRef
.IsAlive()) {
9864 // This can happen if for some reason we ended up not reflowing, like in print
9865 // preview under some circumstances.
9866 if (MOZ_UNLIKELY(!HasComputedInfo(*gridFrame
))) {
9873 // TODO: This is a rather dumb implementation of nsILineIterator, but it's
9874 // better than our pre-existing behavior. Ideally, we should probably use the
9875 // grid information to return a meaningful number of lines etc.
9876 bool nsGridContainerFrame::IsLineIteratorFlowRTL() { return false; }
9878 int32_t nsGridContainerFrame::GetNumLines() const {
9879 return mFrames
.GetLength();
9882 Result
<nsILineIterator::LineInfo
, nsresult
> nsGridContainerFrame::GetLine(
9883 int32_t aLineNumber
) {
9884 if (aLineNumber
< 0 || aLineNumber
>= GetNumLines()) {
9885 return Err(NS_ERROR_FAILURE
);
9888 nsIFrame
* f
= mFrames
.FrameAt(aLineNumber
);
9889 rv
.mLineBounds
= f
->GetRect();
9890 rv
.mFirstFrameOnLine
= f
;
9891 rv
.mNumFramesOnLine
= 1;
9895 int32_t nsGridContainerFrame::FindLineContaining(nsIFrame
* aFrame
,
9896 int32_t aStartLine
) {
9897 const int32_t index
= mFrames
.IndexOf(aFrame
);
9901 if (index
< aStartLine
) {
9908 nsGridContainerFrame::CheckLineOrder(int32_t aLine
, bool* aIsReordered
,
9909 nsIFrame
** aFirstVisual
,
9910 nsIFrame
** aLastVisual
) {
9911 *aIsReordered
= false;
9912 *aFirstVisual
= nullptr;
9913 *aLastVisual
= nullptr;
9918 nsGridContainerFrame::FindFrameAt(int32_t aLineNumber
, nsPoint aPos
,
9919 nsIFrame
** aFrameFound
,
9920 bool* aPosIsBeforeFirstFrame
,
9921 bool* aPosIsAfterLastFrame
) {
9922 const auto wm
= GetWritingMode();
9923 const LogicalPoint
pos(wm
, aPos
, GetSize());
9925 *aFrameFound
= nullptr;
9926 *aPosIsBeforeFirstFrame
= true;
9927 *aPosIsAfterLastFrame
= false;
9929 nsIFrame
* f
= mFrames
.FrameAt(aLineNumber
);
9934 auto rect
= f
->GetLogicalRect(wm
, GetSize());
9936 *aPosIsBeforeFirstFrame
= pos
.I(wm
) < rect
.IStart(wm
);
9937 *aPosIsAfterLastFrame
= pos
.I(wm
) > rect
.IEnd(wm
);