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