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