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