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 <algorithm>  // for std::stable_sort
12 #include <functional>
13 #include <limits>
14 #include "gfxContext.h"
15 #include "mozilla/CSSAlignUtils.h"
16 #include "mozilla/CSSOrderAwareFrameIterator.h"
17 #include "mozilla/dom/GridBinding.h"
18 #include "mozilla/Maybe.h"
19 #include "mozilla/PodOperations.h"  // for PodZero
20 #include "mozilla/Poison.h"
21 #include "nsAbsoluteContainingBlock.h"
22 #include "nsAlgorithm.h"  // for clamped()
23 #include "nsCSSAnonBoxes.h"
24 #include "nsCSSFrameConstructor.h"
25 #include "nsDataHashtable.h"
26 #include "nsDisplayList.h"
27 #include "nsHashKeys.h"
28 #include "nsIFrameInlines.h"
29 #include "nsPresContext.h"
30 #include "nsReadableUtils.h"
31 #ifdef MOZ_OLD_STYLE
32 #include "nsRuleNode.h"
33 #endif
34 #include "nsStyleContext.h"
35 #include "nsTableWrapperFrame.h"
36 
37 using namespace mozilla;
38 
39 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
40 typedef nsGridContainerFrame::TrackSize TrackSize;
41 const uint32_t nsGridContainerFrame::kTranslatedMaxLine =
42     uint32_t(nsStyleGridLine::kMaxLine - nsStyleGridLine::kMinLine);
43 const uint32_t nsGridContainerFrame::kAutoLine = kTranslatedMaxLine + 3457U;
44 typedef nsTHashtable<nsPtrHashKey<nsIFrame>> FrameHashtable;
45 typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags;
46 typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
47 
48 // https://drafts.csswg.org/css-sizing/#constraints
49 enum class SizingConstraint {
50   eMinContent,   // sizing under min-content constraint
51   eMaxContent,   // sizing under max-content constraint
52   eNoConstraint  // no constraint, used during Reflow
53 };
54 
ReparentFrame(nsIFrame * aFrame,nsContainerFrame * aOldParent,nsContainerFrame * aNewParent)55 static void ReparentFrame(nsIFrame* aFrame, nsContainerFrame* aOldParent,
56                           nsContainerFrame* aNewParent) {
57   NS_ASSERTION(aOldParent == aFrame->GetParent(),
58                "Parent not consistent with expectations");
59 
60   aFrame->SetParent(aNewParent);
61 
62   // When pushing and pulling frames we need to check for whether any
63   // views need to be reparented
64   nsContainerFrame::ReparentFrameView(aFrame, aOldParent, aNewParent);
65 }
66 
ReparentFrames(nsFrameList & aFrameList,nsContainerFrame * aOldParent,nsContainerFrame * aNewParent)67 static void ReparentFrames(nsFrameList& aFrameList,
68                            nsContainerFrame* aOldParent,
69                            nsContainerFrame* aNewParent) {
70   for (auto f : aFrameList) {
71     ReparentFrame(f, aOldParent, aNewParent);
72   }
73 }
74 
ClampToCSSMaxBSize(nscoord aSize,const ReflowInput * aReflowInput)75 static nscoord ClampToCSSMaxBSize(nscoord aSize,
76                                   const ReflowInput* aReflowInput) {
77   auto maxSize = aReflowInput->ComputedMaxBSize();
78   if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
79     MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
80     aSize = std::min(aSize, maxSize);
81   }
82   return aSize;
83 }
84 
85 // Same as above and set aStatus INCOMPLETE if aSize wasn't clamped.
86 // (If we clamp aSize it means our size is less than the break point,
87 // i.e. we're effectively breaking in our overflow, so we should leave
88 // aStatus as is (it will likely be set to OVERFLOW_INCOMPLETE later)).
ClampToCSSMaxBSize(nscoord aSize,const ReflowInput * aReflowInput,nsReflowStatus * aStatus)89 static nscoord ClampToCSSMaxBSize(nscoord aSize,
90                                   const ReflowInput* aReflowInput,
91                                   nsReflowStatus* aStatus) {
92   auto maxSize = aReflowInput->ComputedMaxBSize();
93   if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
94     MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
95     if (aSize < maxSize) {
96       aStatus->SetIncomplete();
97     } else {
98       aSize = maxSize;
99     }
100   } else {
101     aStatus->SetIncomplete();
102   }
103   return aSize;
104 }
105 
IsPercentOfIndefiniteSize(const nsStyleCoord & aCoord,nscoord aPercentBasis)106 static bool IsPercentOfIndefiniteSize(const nsStyleCoord& aCoord,
107                                       nscoord aPercentBasis) {
108   return aPercentBasis == NS_UNCONSTRAINEDSIZE && aCoord.HasPercent();
109 }
110 
ResolveToDefiniteSize(const nsStyleCoord & aCoord,nscoord aPercentBasis)111 static nscoord ResolveToDefiniteSize(const nsStyleCoord& aCoord,
112                                      nscoord aPercentBasis) {
113   MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
114   if (::IsPercentOfIndefiniteSize(aCoord, aPercentBasis)) {
115     return nscoord(0);
116   }
117   return std::max(nscoord(0), aCoord.ComputeCoordPercentCalc(aPercentBasis));
118 }
119 
GetPercentSizeParts(const nsStyleCoord & aCoord,nscoord * aLength,float * aPercent)120 static bool GetPercentSizeParts(const nsStyleCoord& aCoord, nscoord* aLength,
121                                 float* aPercent) {
122   switch (aCoord.GetUnit()) {
123     case eStyleUnit_Percent:
124       *aLength = 0;
125       *aPercent = aCoord.GetPercentValue();
126       return true;
127     case eStyleUnit_Calc: {
128       nsStyleCoord::Calc* calc = aCoord.GetCalcValue();
129       *aLength = calc->mLength;
130       *aPercent = calc->mPercent;
131       return true;
132     }
133     default:
134       return false;
135   }
136 }
137 
ResolvePercentSizeParts(const nsStyleCoord & aCoord,nscoord aPercentBasis,nscoord * aLength,float * aPercent)138 static void ResolvePercentSizeParts(const nsStyleCoord& aCoord,
139                                     nscoord aPercentBasis, nscoord* aLength,
140                                     float* aPercent) {
141   MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
142   if (aPercentBasis != NS_UNCONSTRAINEDSIZE) {
143     *aLength =
144         std::max(nscoord(0), aCoord.ComputeCoordPercentCalc(aPercentBasis));
145     *aPercent = 0.0f;
146     return;
147   }
148   if (!GetPercentSizeParts(aCoord, aLength, aPercent)) {
149     *aLength = aCoord.ToLength();
150     *aPercent = 0.0f;
151   }
152 }
153 
154 // Synthesize a baseline from a border box.  For an alphabetical baseline
155 // this is the end edge of the border box.  For a central baseline it's
156 // the center of the border box.
157 // https://drafts.csswg.org/css-align-3/#synthesize-baselines
158 // For a 'first baseline' the measure is from the border-box start edge and
159 // for a 'last baseline' the measure is from the border-box end edge.
SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup,WritingMode aWM,nscoord aBorderBoxSize)160 static nscoord SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup,
161                                                WritingMode aWM,
162                                                nscoord aBorderBoxSize) {
163   if (aGroup == BaselineSharingGroup::eFirst) {
164     return aWM.IsAlphabeticalBaseline() ? aBorderBoxSize : aBorderBoxSize / 2;
165   }
166   MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
167   // Round up for central baseline offset, to be consistent with eFirst.
168   return aWM.IsAlphabeticalBaseline()
169              ? 0
170              : (aBorderBoxSize / 2) + (aBorderBoxSize % 2);
171 }
172 
173 enum class GridLineSide {
174   eBeforeGridGap,
175   eAfterGridGap,
176 };
177 
178 struct nsGridContainerFrame::TrackSize {
179   enum StateBits : uint16_t {
180     eAutoMinSizing = 0x1,
181     eMinContentMinSizing = 0x2,
182     eMaxContentMinSizing = 0x4,
183     eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing,
184     eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing,
185     eModified = 0x8,
186     eAutoMaxSizing = 0x10,
187     eMinContentMaxSizing = 0x20,
188     eMaxContentMaxSizing = 0x40,
189     eAutoOrMaxContentMaxSizing = eAutoMaxSizing | eMaxContentMaxSizing,
190     eIntrinsicMaxSizing = eAutoOrMaxContentMaxSizing | eMinContentMaxSizing,
191     eFlexMaxSizing = 0x80,
192     eFrozen = 0x100,
193     eSkipGrowUnlimited1 = 0x200,
194     eSkipGrowUnlimited2 = 0x400,
195     eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2,
196     eBreakBefore = 0x800,
197     eFitContent = 0x1000,
198     eInfinitelyGrowable = 0x2000,
199   };
200 
201   StateBits Initialize(nscoord aPercentageBasis, const nsStyleCoord& aMinCoord,
202                        const nsStyleCoord& aMaxCoord);
IsFrozennsGridContainerFrame::TrackSize203   bool IsFrozen() const { return mState & eFrozen; }
204 #ifdef DEBUG
205   void Dump() const;
206 #endif
207 
IsMinContentnsGridContainerFrame::TrackSize208   static bool IsMinContent(const nsStyleCoord& aCoord) {
209     return aCoord.GetUnit() == eStyleUnit_Enumerated &&
210            aCoord.GetEnumValue<StyleGridTrackBreadth>() ==
211                StyleGridTrackBreadth::MinContent;
212   }
IsDefiniteMaxSizingnsGridContainerFrame::TrackSize213   static bool IsDefiniteMaxSizing(StateBits aStateBits) {
214     return (aStateBits & (eIntrinsicMaxSizing | eFlexMaxSizing)) == 0;
215   }
216 
217   nscoord mBase;
218   nscoord mLimit;
219   nscoord mPosition;  // zero until we apply 'align/justify-content'
220   // mBaselineSubtreeSize is the size of a baseline-aligned subtree within
221   // this track.  One subtree per baseline-sharing group (per track).
222   nscoord mBaselineSubtreeSize[2];
223   StateBits mState;
224 };
225 
226 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits)
227 
228 namespace mozilla {
229 template <>
230 struct IsPod<nsGridContainerFrame::TrackSize> : TrueType {};
231 }  // namespace mozilla
232 
Initialize(nscoord aPercentageBasis,const nsStyleCoord & aMinCoord,const nsStyleCoord & aMaxCoord)233 TrackSize::StateBits nsGridContainerFrame::TrackSize::Initialize(
234     nscoord aPercentageBasis, const nsStyleCoord& aMinCoord,
235     const nsStyleCoord& aMaxCoord) {
236   MOZ_ASSERT(mBase == 0 && mLimit == 0 && mState == 0,
237              "track size data is expected to be initialized to zero");
238   auto minSizeUnit = aMinCoord.GetUnit();
239   auto maxSizeUnit = aMaxCoord.GetUnit();
240   if (minSizeUnit == eStyleUnit_None) {
241     // This track is sized using fit-content(size) (represented in style system
242     // with minCoord=None,maxCoord=size).  In layout, fit-content(size) behaves
243     // as minmax(auto, max-content), with 'size' as an additional upper-bound.
244     mState = eFitContent;
245     minSizeUnit = eStyleUnit_Auto;
246     maxSizeUnit = eStyleUnit_Enumerated;  // triggers max-content sizing below
247   }
248   if (::IsPercentOfIndefiniteSize(aMinCoord, aPercentageBasis)) {
249     // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-percentage
250     // "If the inline or block size of the grid container is indefinite,
251     //  <percentage> values relative to that size are treated as 'auto'."
252     minSizeUnit = eStyleUnit_Auto;
253   }
254   if (::IsPercentOfIndefiniteSize(aMaxCoord, aPercentageBasis)) {
255     maxSizeUnit = eStyleUnit_Auto;
256   }
257   // http://dev.w3.org/csswg/css-grid/#algo-init
258   switch (minSizeUnit) {
259     case eStyleUnit_Auto:
260       mState |= eAutoMinSizing;
261       break;
262     case eStyleUnit_Enumerated:
263       mState |=
264           IsMinContent(aMinCoord) ? eMinContentMinSizing : eMaxContentMinSizing;
265       break;
266     default:
267       MOZ_ASSERT(minSizeUnit != eStyleUnit_FlexFraction,
268                  "<flex> min-sizing is invalid as a track size");
269       mBase = ::ResolveToDefiniteSize(aMinCoord, aPercentageBasis);
270   }
271   switch (maxSizeUnit) {
272     case eStyleUnit_Auto:
273       mState |= eAutoMaxSizing;
274       mLimit = NS_UNCONSTRAINEDSIZE;
275       break;
276     case eStyleUnit_Enumerated:
277       mState |=
278           IsMinContent(aMaxCoord) ? eMinContentMaxSizing : eMaxContentMaxSizing;
279       mLimit = NS_UNCONSTRAINEDSIZE;
280       break;
281     case eStyleUnit_FlexFraction:
282       mState |= eFlexMaxSizing;
283       mLimit = mBase;
284       break;
285     default:
286       mLimit = ::ResolveToDefiniteSize(aMaxCoord, aPercentageBasis);
287       if (mLimit < mBase) {
288         mLimit = mBase;
289       }
290   }
291 
292   mBaselineSubtreeSize[BaselineSharingGroup::eFirst] = nscoord(0);
293   mBaselineSubtreeSize[BaselineSharingGroup::eLast] = nscoord(0);
294   return mState;
295 }
296 
297 /**
298  * Is aFrame1 a prev-continuation of aFrame2?
299  */
IsPrevContinuationOf(nsIFrame * aFrame1,nsIFrame * aFrame2)300 static bool IsPrevContinuationOf(nsIFrame* aFrame1, nsIFrame* aFrame2) {
301   nsIFrame* prev = aFrame2;
302   while ((prev = prev->GetPrevContinuation())) {
303     if (prev == aFrame1) {
304       return true;
305     }
306   }
307   return false;
308 }
309 
310 /**
311  * Moves all frames from aSrc into aDest such that the resulting aDest
312  * is still sorted in document content order and continuation order.
313  * Precondition: both |aSrc| and |aDest| must be sorted to begin with.
314  * @param aCommonAncestor a hint for nsLayoutUtils::CompareTreePosition
315  */
MergeSortedFrameLists(nsFrameList & aDest,nsFrameList & aSrc,nsIContent * aCommonAncestor)316 static void MergeSortedFrameLists(nsFrameList& aDest, nsFrameList& aSrc,
317                                   nsIContent* aCommonAncestor) {
318   nsIFrame* dest = aDest.FirstChild();
319   for (nsIFrame* src = aSrc.FirstChild(); src;) {
320     if (!dest) {
321       aDest.AppendFrames(nullptr, aSrc);
322       break;
323     }
324     nsIContent* srcContent = src->GetContent();
325     nsIContent* destContent = dest->GetContent();
326     int32_t result = nsLayoutUtils::CompareTreePosition(srcContent, destContent,
327                                                         aCommonAncestor);
328     if (MOZ_UNLIKELY(result == 0)) {
329       // NOTE: we get here when comparing ::before/::after for the same element.
330       if (MOZ_UNLIKELY(srcContent->IsGeneratedContentContainerForBefore())) {
331         if (MOZ_LIKELY(!destContent->IsGeneratedContentContainerForBefore()) ||
332             ::IsPrevContinuationOf(src, dest)) {
333           result = -1;
334         }
335       } else if (MOZ_UNLIKELY(
336                      srcContent->IsGeneratedContentContainerForAfter())) {
337         if (MOZ_UNLIKELY(destContent->IsGeneratedContentContainerForAfter()) &&
338             ::IsPrevContinuationOf(src, dest)) {
339           result = -1;
340         }
341       } else if (::IsPrevContinuationOf(src, dest)) {
342         result = -1;
343       }
344     }
345     if (result < 0) {
346       // src should come before dest
347       nsIFrame* next = src->GetNextSibling();
348       aSrc.RemoveFrame(src);
349       aDest.InsertFrame(nullptr, dest->GetPrevSibling(), src);
350       src = next;
351     } else {
352       dest = dest->GetNextSibling();
353     }
354   }
355   MOZ_ASSERT(aSrc.IsEmpty());
356 }
357 
MergeSortedFrameListsFor(nsFrameList & aDest,nsFrameList & aSrc,nsContainerFrame * aParent)358 static void MergeSortedFrameListsFor(nsFrameList& aDest, nsFrameList& aSrc,
359                                      nsContainerFrame* aParent) {
360   MergeSortedFrameLists(aDest, aSrc, aParent->GetContent());
361 }
362 
363 /**
364  * A LineRange can be definite or auto - when it's definite it represents
365  * a consecutive set of tracks between a starting line and an ending line.
366  * Before it's definite it can also represent an auto position with a span,
367  * where mStart == kAutoLine and mEnd is the (non-zero positive) span.
368  * For normal-flow items, the invariant mStart < mEnd holds when both
369  * lines are definite.
370  *
371  * For abs.pos. grid items, mStart and mEnd may both be kAutoLine, meaning
372  * "attach this side to the grid container containing block edge".
373  * Additionally, mStart <= mEnd holds when both are definite (non-kAutoLine),
374  * i.e. the invariant is slightly relaxed compared to normal flow items.
375  */
376 struct nsGridContainerFrame::LineRange {
LineRangensGridContainerFrame::LineRange377   LineRange(int32_t aStart, int32_t aEnd)
378       : mUntranslatedStart(aStart), mUntranslatedEnd(aEnd) {
379 #ifdef DEBUG
380     if (!IsAutoAuto()) {
381       if (IsAuto()) {
382         MOZ_ASSERT(aEnd >= nsStyleGridLine::kMinLine &&
383                        aEnd <= nsStyleGridLine::kMaxLine,
384                    "invalid span");
385       } else {
386         MOZ_ASSERT(aStart >= nsStyleGridLine::kMinLine &&
387                        aStart <= nsStyleGridLine::kMaxLine,
388                    "invalid start line");
389         MOZ_ASSERT(
390             aEnd == int32_t(kAutoLine) || (aEnd >= nsStyleGridLine::kMinLine &&
391                                            aEnd <= nsStyleGridLine::kMaxLine),
392             "invalid end line");
393       }
394     }
395 #endif
396   }
IsAutoAutonsGridContainerFrame::LineRange397   bool IsAutoAuto() const { return mStart == kAutoLine && mEnd == kAutoLine; }
IsAutonsGridContainerFrame::LineRange398   bool IsAuto() const { return mStart == kAutoLine; }
IsDefinitensGridContainerFrame::LineRange399   bool IsDefinite() const { return mStart != kAutoLine; }
ExtentnsGridContainerFrame::LineRange400   uint32_t Extent() const {
401     MOZ_ASSERT(mEnd != kAutoLine, "Extent is undefined for abs.pos. 'auto'");
402     if (IsAuto()) {
403       MOZ_ASSERT(mEnd >= 1 && mEnd < uint32_t(nsStyleGridLine::kMaxLine),
404                  "invalid span");
405       return mEnd;
406     }
407     return mEnd - mStart;
408   }
409   /**
410    * Resolve this auto range to start at aStart, making it definite.
411    * Precondition: this range IsAuto()
412    */
ResolveAutoPositionnsGridContainerFrame::LineRange413   void ResolveAutoPosition(uint32_t aStart, uint32_t aExplicitGridOffset) {
414     MOZ_ASSERT(IsAuto(), "Why call me?");
415     mStart = aStart;
416     mEnd += aStart;
417     // Clamping to where kMaxLine is in the explicit grid, per
418     // http://dev.w3.org/csswg/css-grid/#overlarge-grids :
419     uint32_t translatedMax = aExplicitGridOffset + nsStyleGridLine::kMaxLine;
420     if (MOZ_UNLIKELY(mStart >= translatedMax)) {
421       mEnd = translatedMax;
422       mStart = mEnd - 1;
423     } else if (MOZ_UNLIKELY(mEnd > translatedMax)) {
424       mEnd = translatedMax;
425     }
426   }
427   /**
428    * Translate the lines to account for (empty) removed tracks.  This method
429    * is only for grid items and should only be called after placement.
430    * aNumRemovedTracks contains a count for each line in the grid how many
431    * tracks were removed between the start of the grid and that line.
432    */
AdjustForRemovedTracksnsGridContainerFrame::LineRange433   void AdjustForRemovedTracks(const nsTArray<uint32_t>& aNumRemovedTracks) {
434     MOZ_ASSERT(mStart != kAutoLine, "invalid resolved line for a grid item");
435     MOZ_ASSERT(mEnd != kAutoLine, "invalid resolved line for a grid item");
436     uint32_t numRemovedTracks = aNumRemovedTracks[mStart];
437     MOZ_ASSERT(numRemovedTracks == aNumRemovedTracks[mEnd],
438                "tracks that a grid item spans can't be removed");
439     mStart -= numRemovedTracks;
440     mEnd -= numRemovedTracks;
441   }
442   /**
443    * Translate the lines to account for (empty) removed tracks.  This method
444    * is only for abs.pos. children and should only be called after placement.
445    * Same as for in-flow items, but we don't touch 'auto' lines here and we
446    * also need to adjust areas that span into the removed tracks.
447    */
AdjustAbsPosForRemovedTracksnsGridContainerFrame::LineRange448   void AdjustAbsPosForRemovedTracks(
449       const nsTArray<uint32_t>& aNumRemovedTracks) {
450     if (mStart != nsGridContainerFrame::kAutoLine) {
451       mStart -= aNumRemovedTracks[mStart];
452     }
453     if (mEnd != nsGridContainerFrame::kAutoLine) {
454       MOZ_ASSERT(mStart == nsGridContainerFrame::kAutoLine || mEnd > mStart,
455                  "invalid line range");
456       mEnd -= aNumRemovedTracks[mEnd];
457     }
458   }
459   /**
460    * Return the contribution of this line range for step 2 in
461    * http://dev.w3.org/csswg/css-grid/#auto-placement-algo
462    */
HypotheticalEndnsGridContainerFrame::LineRange463   uint32_t HypotheticalEnd() const { return mEnd; }
464   /**
465    * Given an array of track sizes, return the starting position and length
466    * of the tracks in this line range.
467    */
468   void ToPositionAndLength(const nsTArray<TrackSize>& aTrackSizes,
469                            nscoord* aPos, nscoord* aLength) const;
470   /**
471    * Given an array of track sizes, return the length of the tracks in this
472    * line range.
473    */
474   nscoord ToLength(const nsTArray<TrackSize>& aTrackSizes) const;
475   /**
476    * Given an array of track sizes and a grid origin coordinate, adjust the
477    * abs.pos. containing block along an axis given by aPos and aLength.
478    * aPos and aLength should already be initialized to the grid container
479    * containing block for this axis before calling this method.
480    */
481   void ToPositionAndLengthForAbsPos(const Tracks& aTracks, nscoord aGridOrigin,
482                                     nscoord* aPos, nscoord* aLength) const;
483 
484   /**
485    * @note We'll use the signed member while resolving definite positions
486    * to line numbers (1-based), which may become negative for implicit lines
487    * to the top/left of the explicit grid.  PlaceGridItems() then translates
488    * the whole grid to a 0,0 origin and we'll use the unsigned member from
489    * there on.
490    */
491   union {
492     uint32_t mStart;
493     int32_t mUntranslatedStart;
494   };
495   union {
496     uint32_t mEnd;
497     int32_t mUntranslatedEnd;
498   };
499 
500  protected:
LineRangensGridContainerFrame::LineRange501   LineRange() {}
502 };
503 
504 /**
505  * Helper class to construct a LineRange from translated lines.
506  * The ctor only accepts translated definite line numbers.
507  */
508 struct nsGridContainerFrame::TranslatedLineRange : public LineRange {
TranslatedLineRangensGridContainerFrame::TranslatedLineRange509   TranslatedLineRange(uint32_t aStart, uint32_t aEnd) {
510     MOZ_ASSERT(aStart < aEnd && aEnd <= kTranslatedMaxLine);
511     mStart = aStart;
512     mEnd = aEnd;
513   }
514 };
515 
516 /**
517  * A GridArea is the area in the grid for a grid item.
518  * The area is represented by two LineRanges, both of which can be auto
519  * (@see LineRange) in intermediate steps while the item is being placed.
520  * @see PlaceGridItems
521  */
522 struct nsGridContainerFrame::GridArea {
GridAreansGridContainerFrame::GridArea523   GridArea(const LineRange& aCols, const LineRange& aRows)
524       : mCols(aCols), mRows(aRows) {}
IsDefinitensGridContainerFrame::GridArea525   bool IsDefinite() const { return mCols.IsDefinite() && mRows.IsDefinite(); }
526   LineRange mCols;
527   LineRange mRows;
528 };
529 
530 struct nsGridContainerFrame::GridItemInfo {
531   /**
532    * Item state per axis.
533    */
534   enum StateBits : uint8_t {
535     eIsFlexing = 0x1,      // does the item span a flex track?
536     eFirstBaseline = 0x2,  // participate in 'first baseline' alignment?
537     // ditto 'last baseline', mutually exclusive w. eFirstBaseline
538     eLastBaseline = 0x4,
539     eIsBaselineAligned = eFirstBaseline | eLastBaseline,
540     // One of e[Self|Content]Baseline is set when eIsBaselineAligned is true
541     eSelfBaseline = 0x8,  // is it *-self:[last ]baseline alignment?
542     // Ditto *-content:[last ]baseline. Mutually exclusive w. eSelfBaseline.
543     eContentBaseline = 0x10,
544     eAllBaselineBits = eIsBaselineAligned | eSelfBaseline | eContentBaseline,
545     // Should apply Automatic Minimum Size per:
546     // https://drafts.csswg.org/css-grid/#min-size-auto
547     eApplyAutoMinSize = 0x20,
548     // Clamp per https://drafts.csswg.org/css-grid/#min-size-auto
549     eClampMarginBoxMinSize = 0x40,
550   };
551 
GridItemInfonsGridContainerFrame::GridItemInfo552   explicit GridItemInfo(nsIFrame* aFrame, const GridArea& aArea)
553       : mFrame(aFrame), mArea(aArea) {
554     mState[eLogicalAxisBlock] = StateBits(0);
555     mState[eLogicalAxisInline] = StateBits(0);
556     mBaselineOffset[eLogicalAxisBlock] = nscoord(0);
557     mBaselineOffset[eLogicalAxisInline] = nscoord(0);
558   }
559 
560   /**
561    * If the item is [align|justify]-self:[last ]baseline aligned in the given
562    * axis then set aBaselineOffset to the baseline offset and return aAlign.
563    * Otherwise, return a fallback alignment.
564    */
GetSelfBaselinensGridContainerFrame::GridItemInfo565   uint8_t GetSelfBaseline(uint8_t aAlign, LogicalAxis aAxis,
566                           nscoord* aBaselineOffset) const {
567     MOZ_ASSERT(aAlign == NS_STYLE_ALIGN_BASELINE ||
568                aAlign == NS_STYLE_ALIGN_LAST_BASELINE);
569     if (!(mState[aAxis] & eSelfBaseline)) {
570       return aAlign == NS_STYLE_ALIGN_BASELINE ? NS_STYLE_ALIGN_SELF_START
571                                                : NS_STYLE_ALIGN_SELF_END;
572     }
573     *aBaselineOffset = mBaselineOffset[aAxis];
574     return aAlign;
575   }
576 
577   // Return true if we should apply Automatic Minimum Size to this item.
578   // https://drafts.csswg.org/css-grid/#min-size-auto
579   // @note the caller should also check that the item spans at least one track
580   // that has a min track sizing function that is 'auto' before applying it.
ShouldApplyAutoMinSizensGridContainerFrame::GridItemInfo581   bool ShouldApplyAutoMinSize(WritingMode aContainerWM,
582                               LogicalAxis aContainerAxis,
583                               nscoord aPercentageBasis) const {
584     const auto* pos =
585         mFrame->IsTableWrapperFrame()
586             ? mFrame->PrincipalChildList().FirstChild()->StylePosition()
587             : mFrame->StylePosition();
588     const auto& size = aContainerAxis == eLogicalAxisInline
589                            ? pos->ISize(aContainerWM)
590                            : pos->BSize(aContainerWM);
591     // NOTE: if we have a definite size then our automatic minimum size
592     // can't affect our size.  Excluding these simplifies applying
593     // the clamping in the right cases later.
594     if (size.GetUnit() != eStyleUnit_Auto &&
595         !::IsPercentOfIndefiniteSize(size, aPercentageBasis)) {
596       return false;
597     }
598     const auto& minSize = aContainerAxis == eLogicalAxisInline
599                               ? pos->MinISize(aContainerWM)
600                               : pos->MinBSize(aContainerWM);
601     return minSize.GetUnit() == eStyleUnit_Auto &&
602            mFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE;
603   }
604 
605 #ifdef DEBUG
606   void Dump() const;
607 #endif
608 
IsStartRowLessThannsGridContainerFrame::GridItemInfo609   static bool IsStartRowLessThan(const GridItemInfo* a, const GridItemInfo* b) {
610     return a->mArea.mRows.mStart < b->mArea.mRows.mStart;
611   }
612 
613   nsIFrame* const mFrame;
614   GridArea mArea;
615   // Offset from the margin edge to the baseline (LogicalAxis index).  It's from
616   // the start edge when eFirstBaseline is set, end edge otherwise. It's mutable
617   // since we update the value fairly late (just before reflowing the item).
618   mutable nscoord mBaselineOffset[2];
619   mutable StateBits mState[2];  // state bits per axis (LogicalAxis index)
620   static_assert(mozilla::eLogicalAxisBlock == 0, "unexpected index value");
621   static_assert(mozilla::eLogicalAxisInline == 1, "unexpected index value");
622 };
623 
624 using GridItemInfo = nsGridContainerFrame::GridItemInfo;
625 using ItemState = GridItemInfo::StateBits;
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ItemState)626 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ItemState)
627 
628 #ifdef DEBUG
629 void nsGridContainerFrame::GridItemInfo::Dump() const {
630   auto Dump1 = [this](const char* aMsg, LogicalAxis aAxis) {
631     auto state = mState[aAxis];
632     if (!state) {
633       return;
634     }
635     printf("%s", aMsg);
636     if (state & ItemState::eIsFlexing) {
637       printf("flexing ");
638     }
639     if (state & ItemState::eApplyAutoMinSize) {
640       printf("auto-min-size ");
641     }
642     if (state & ItemState::eClampMarginBoxMinSize) {
643       printf("clamp ");
644     }
645     if (state & ItemState::eFirstBaseline) {
646       printf("first baseline %s-alignment ",
647              (state & ItemState::eSelfBaseline) ? "self" : "content");
648     }
649     if (state & ItemState::eLastBaseline) {
650       printf("last baseline %s-alignment ",
651              (state & ItemState::eSelfBaseline) ? "self" : "content");
652     }
653     if (state & ItemState::eIsBaselineAligned) {
654       printf("%.2fpx", NSAppUnitsToFloatPixels(mBaselineOffset[aAxis],
655                                                AppUnitsPerCSSPixel()));
656     }
657     printf("\n");
658   };
659   printf("grid-row: %d %d\n", mArea.mRows.mStart, mArea.mRows.mEnd);
660   Dump1("  grid block-axis: ", eLogicalAxisBlock);
661   printf("grid-column: %d %d\n", mArea.mCols.mStart, mArea.mCols.mEnd);
662   Dump1("  grid inline-axis: ", eLogicalAxisInline);
663 }
664 #endif
665 
666 /**
667  * Utility class to find line names.  It provides an interface to lookup line
668  * names with a dynamic number of repeat(auto-fill/fit) tracks taken into
669  * account.
670  */
671 class MOZ_STACK_CLASS nsGridContainerFrame::LineNameMap {
672  public:
673   /**
674    * Create a LineNameMap.
675    * @param aGridTemplate is the grid-template-rows/columns data for this axis
676    * @param aNumRepeatTracks the number of actual tracks associated with
677    *   a repeat(auto-fill/fit) track (zero or more), or zero if there is no
678    *   specified repeat(auto-fill/fit) track
679    */
LineNameMap(const nsStyleGridTemplate & aGridTemplate,uint32_t aNumRepeatTracks)680   LineNameMap(const nsStyleGridTemplate& aGridTemplate,
681               uint32_t aNumRepeatTracks)
682       : mLineNameLists(aGridTemplate.mLineNameLists),
683         mRepeatAutoLineNameListBefore(
684             aGridTemplate.mRepeatAutoLineNameListBefore),
685         mRepeatAutoLineNameListAfter(
686             aGridTemplate.mRepeatAutoLineNameListAfter),
687         mRepeatAutoStart(
688             aGridTemplate.HasRepeatAuto() ? aGridTemplate.mRepeatAutoIndex : 0),
689         mRepeatAutoEnd(mRepeatAutoStart + aNumRepeatTracks),
690         mRepeatEndDelta(
691             aGridTemplate.HasRepeatAuto() ? int32_t(aNumRepeatTracks) - 1 : 0),
692         mTemplateLinesEnd(mLineNameLists.Length() + mRepeatEndDelta),
693         mHasRepeatAuto(aGridTemplate.HasRepeatAuto()) {
694     MOZ_ASSERT(mHasRepeatAuto || aNumRepeatTracks == 0);
695     MOZ_ASSERT(mRepeatAutoStart <= mLineNameLists.Length());
696     MOZ_ASSERT(!mHasRepeatAuto || mLineNameLists.Length() >= 2);
697   }
698 
699   /**
700    * Find the aNth occurrence of aName, searching forward if aNth is positive,
701    * and in reverse if aNth is negative (aNth == 0 is invalid), starting from
702    * aFromIndex (not inclusive), and return a 1-based line number.
703    * Also take into account there is an unconditional match at aImplicitLine
704    * unless it's zero.
705    * Return zero if aNth occurrences can't be found.  In that case, aNth has
706    * been decremented with the number of occurrences that were found (if any).
707    *
708    * E.g. to search for "A 2" forward from the start of the grid: aName is "A"
709    * aNth is 2 and aFromIndex is zero.  To search for "A -2", aNth is -2 and
710    * aFromIndex is ExplicitGridEnd + 1 (which is the line "before" the last
711    * line when we're searching in reverse).  For "span A 2", aNth is 2 when
712    * used on a grid-[row|column]-end property and -2 for a *-start property,
713    * and aFromIndex is the line (which we should skip) on the opposite property.
714    */
FindNamedLine(const nsString & aName,int32_t * aNth,uint32_t aFromIndex,uint32_t aImplicitLine) const715   uint32_t FindNamedLine(const nsString& aName, int32_t* aNth,
716                          uint32_t aFromIndex, uint32_t aImplicitLine) const {
717     MOZ_ASSERT(aNth && *aNth != 0);
718     if (*aNth > 0) {
719       return FindLine(aName, aNth, aFromIndex, aImplicitLine);
720     }
721     int32_t nth = -*aNth;
722     int32_t line = RFindLine(aName, &nth, aFromIndex, aImplicitLine);
723     *aNth = -nth;
724     return line;
725   }
726 
727  private:
728   /**
729    * @see FindNamedLine, this function searches forward.
730    */
FindLine(const nsString & aName,int32_t * aNth,uint32_t aFromIndex,uint32_t aImplicitLine) const731   uint32_t FindLine(const nsString& aName, int32_t* aNth, uint32_t aFromIndex,
732                     uint32_t aImplicitLine) const {
733     MOZ_ASSERT(aNth && *aNth > 0);
734     int32_t nth = *aNth;
735     const uint32_t end = mTemplateLinesEnd;
736     uint32_t line;
737     uint32_t i = aFromIndex;
738     for (; i < end; i = line) {
739       line = i + 1;
740       if (line == aImplicitLine || Contains(i, aName)) {
741         if (--nth == 0) {
742           return line;
743         }
744       }
745     }
746     if (aImplicitLine > i) {
747       // aImplicitLine is after the lines we searched above so it's last.
748       // (grid-template-areas has more tracks than grid-template-[rows|columns])
749       if (--nth == 0) {
750         return aImplicitLine;
751       }
752     }
753     MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
754     *aNth = nth;
755     return 0;
756   }
757 
758   /**
759    * @see FindNamedLine, this function searches in reverse.
760    */
RFindLine(const nsString & aName,int32_t * aNth,uint32_t aFromIndex,uint32_t aImplicitLine) const761   uint32_t RFindLine(const nsString& aName, int32_t* aNth, uint32_t aFromIndex,
762                      uint32_t aImplicitLine) const {
763     MOZ_ASSERT(aNth && *aNth > 0);
764     if (MOZ_UNLIKELY(aFromIndex == 0)) {
765       return 0;  // There are no named lines beyond the start of the explicit
766                  // grid.
767     }
768     --aFromIndex;  // (shift aFromIndex so we can treat it as inclusive)
769     int32_t nth = *aNth;
770     // The implicit line may be beyond the explicit grid so we match
771     // this line first if it's within the mTemplateLinesEnd..aFromIndex range.
772     const uint32_t end = mTemplateLinesEnd;
773     if (aImplicitLine > end && aImplicitLine < aFromIndex) {
774       if (--nth == 0) {
775         return aImplicitLine;
776       }
777     }
778     for (uint32_t i = std::min(aFromIndex, end); i; --i) {
779       if (i == aImplicitLine || Contains(i - 1, aName)) {
780         if (--nth == 0) {
781           return i;
782         }
783       }
784     }
785     MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
786     *aNth = nth;
787     return 0;
788   }
789 
790   // Return true if aName exists at aIndex.
Contains(uint32_t aIndex,const nsString & aName) const791   bool Contains(uint32_t aIndex, const nsString& aName) const {
792     if (!mHasRepeatAuto) {
793       return mLineNameLists[aIndex].Contains(aName);
794     }
795     if (aIndex < mRepeatAutoEnd && aIndex >= mRepeatAutoStart &&
796         mRepeatAutoLineNameListBefore.Contains(aName)) {
797       return true;
798     }
799     if (aIndex <= mRepeatAutoEnd && aIndex > mRepeatAutoStart &&
800         mRepeatAutoLineNameListAfter.Contains(aName)) {
801       return true;
802     }
803     if (aIndex <= mRepeatAutoStart) {
804       return mLineNameLists[aIndex].Contains(aName) ||
805              (aIndex == mRepeatAutoEnd &&
806               mLineNameLists[aIndex + 1].Contains(aName));
807     }
808     return aIndex >= mRepeatAutoEnd &&
809            mLineNameLists[aIndex - mRepeatEndDelta].Contains(aName);
810   }
811 
812   // Some style data references, for easy access.
813   const nsTArray<nsTArray<nsString>>& mLineNameLists;
814   const nsTArray<nsString>& mRepeatAutoLineNameListBefore;
815   const nsTArray<nsString>& mRepeatAutoLineNameListAfter;
816   // The index of the repeat(auto-fill/fit) track, or zero if there is none.
817   const uint32_t mRepeatAutoStart;
818   // The (hypothetical) index of the last such repeat() track.
819   const uint32_t mRepeatAutoEnd;
820   // The difference between mTemplateLinesEnd and mLineNameLists.Length().
821   const int32_t mRepeatEndDelta;
822   // The end of the line name lists with repeat(auto-fill/fit) tracks accounted
823   // for.  It is equal to mLineNameLists.Length() when a repeat() track
824   // generates one track (making mRepeatEndDelta == 0).
825   const uint32_t mTemplateLinesEnd;
826   // True if there is a specified repeat(auto-fill/fit) track.
827   const bool mHasRepeatAuto;
828 };
829 
830 /**
831  * Encapsulates CSS track-sizing functions.
832  */
833 struct nsGridContainerFrame::TrackSizingFunctions {
TrackSizingFunctionsnsGridContainerFrame::TrackSizingFunctions834   TrackSizingFunctions(const nsStyleGridTemplate& aGridTemplate,
835                        const nsStyleCoord& aAutoMinSizing,
836                        const nsStyleCoord& aAutoMaxSizing)
837       : mMinSizingFunctions(aGridTemplate.mMinTrackSizingFunctions),
838         mMaxSizingFunctions(aGridTemplate.mMaxTrackSizingFunctions),
839         mAutoMinSizing(aAutoMinSizing),
840         mAutoMaxSizing(aAutoMaxSizing),
841         mExplicitGridOffset(0),
842         mRepeatAutoStart(
843             aGridTemplate.HasRepeatAuto() ? aGridTemplate.mRepeatAutoIndex : 0),
844         mRepeatAutoEnd(mRepeatAutoStart),
845         mRepeatEndDelta(0),
846         mHasRepeatAuto(aGridTemplate.HasRepeatAuto()) {
847     MOZ_ASSERT(mMinSizingFunctions.Length() == mMaxSizingFunctions.Length());
848     MOZ_ASSERT(!mHasRepeatAuto ||
849                (mMinSizingFunctions.Length() >= 1 &&
850                 mRepeatAutoStart < mMinSizingFunctions.Length()));
851   }
852 
853   /**
854    * Initialize the number of auto-fill/fit tracks to use and return that.
855    * (zero if no auto-fill/fit track was specified)
856    */
InitRepeatTracksnsGridContainerFrame::TrackSizingFunctions857   uint32_t InitRepeatTracks(const nsStyleCoord& aGridGap, nscoord aMinSize,
858                             nscoord aSize, nscoord aMaxSize) {
859     uint32_t repeatTracks =
860         CalculateRepeatFillCount(aGridGap, aMinSize, aSize, aMaxSize);
861     SetNumRepeatTracks(repeatTracks);
862     // Blank out the removed flags for each of these tracks.
863     mRemovedRepeatTracks.SetLength(repeatTracks);
864     for (auto& track : mRemovedRepeatTracks) {
865       track = false;
866     }
867     return repeatTracks;
868   }
869 
CalculateRepeatFillCountnsGridContainerFrame::TrackSizingFunctions870   uint32_t CalculateRepeatFillCount(const nsStyleCoord& aGridGap,
871                                     nscoord aMinSize, nscoord aSize,
872                                     nscoord aMaxSize) const {
873     if (!mHasRepeatAuto) {
874       return 0;
875     }
876     // Spec quotes are from https://drafts.csswg.org/css-grid/#repeat-notation
877     const uint32_t numTracks = mMinSizingFunctions.Length();
878     MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track");
879     nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize;
880     if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) {
881       // "Otherwise, the specified track list repeats only once."
882       return 1;
883     }
884     nscoord repeatTrackSize = 0;
885     // Note that the repeat() track size is included in |sum| in this loop.
886     nscoord sum = 0;
887     const nscoord percentBasis = aSize;
888     for (uint32_t i = 0; i < numTracks; ++i) {
889       // "treating each track as its max track sizing function if that is
890       // definite or as its minimum track sizing function otherwise"
891       // https://drafts.csswg.org/css-grid/#valdef-repeat-auto-fill
892       const auto& maxCoord = mMaxSizingFunctions[i];
893       const auto* coord = &maxCoord;
894       if (!coord->IsCoordPercentCalcUnit()) {
895         coord = &mMinSizingFunctions[i];
896         if (!coord->IsCoordPercentCalcUnit()) {
897           return 1;
898         }
899       }
900       nscoord trackSize = ::ResolveToDefiniteSize(*coord, percentBasis);
901       if (i == mRepeatAutoStart) {
902         if (percentBasis != NS_UNCONSTRAINEDSIZE) {
903           // Use a minimum 1px for the repeat() track-size.
904           if (trackSize < AppUnitsPerCSSPixel()) {
905             trackSize = AppUnitsPerCSSPixel();
906           }
907         }
908         repeatTrackSize = trackSize;
909       }
910       sum += trackSize;
911     }
912     nscoord gridGap;
913     float percentSum = 0.0f;
914     float gridGapPercent;
915     ResolvePercentSizeParts(aGridGap, percentBasis, &gridGap, &gridGapPercent);
916     if (numTracks > 1) {
917       // Add grid-gaps for all the tracks including the repeat() track.
918       sum += gridGap * (numTracks - 1);
919       percentSum = gridGapPercent * (numTracks - 1);
920     }
921     // Calculate the max number of tracks that fits without overflow.
922     nscoord available = maxFill != NS_UNCONSTRAINEDSIZE ? maxFill : aMinSize;
923     nscoord size = nsLayoutUtils::AddPercents(sum, percentSum);
924     if (available - size < 0) {
925       // "if any number of repetitions would overflow, then 1 repetition"
926       return 1;
927     }
928     uint32_t numRepeatTracks = 1;
929     bool exactFit = false;
930     while (true) {
931       sum += gridGap + repeatTrackSize;
932       percentSum += gridGapPercent;
933       nscoord newSize = nsLayoutUtils::AddPercents(sum, percentSum);
934       if (newSize <= size) {
935         // Adding more repeat-tracks won't make forward progress.
936         return numRepeatTracks;
937       }
938       size = newSize;
939       nscoord remaining = available - size;
940       exactFit = remaining == 0;
941       if (remaining >= 0) {
942         ++numRepeatTracks;
943       }
944       if (remaining <= 0) {
945         break;
946       }
947     }
948 
949     if (!exactFit && maxFill == NS_UNCONSTRAINEDSIZE) {
950       // "Otherwise, if the grid container has a definite min size in
951       // the relevant axis, the number of repetitions is the largest possible
952       // positive integer that fulfills that minimum requirement."
953       ++numRepeatTracks;  // one more to ensure the grid is at least min-size
954     }
955     // Clamp the number of repeat tracks so that the last line <= kMaxLine.
956     // (note that |numTracks| already includes one repeat() track)
957     const uint32_t maxRepeatTracks = nsStyleGridLine::kMaxLine - numTracks;
958     return std::min(numRepeatTracks, maxRepeatTracks);
959   }
960 
961   /**
962    * Compute the explicit grid end line number (in a zero-based grid).
963    * @param aGridTemplateAreasEnd 'grid-template-areas' end line in this axis
964    */
ComputeExplicitGridEndnsGridContainerFrame::TrackSizingFunctions965   uint32_t ComputeExplicitGridEnd(uint32_t aGridTemplateAreasEnd) {
966     uint32_t end = NumExplicitTracks() + 1;
967     end = std::max(end, aGridTemplateAreasEnd);
968     end = std::min(end, uint32_t(nsStyleGridLine::kMaxLine));
969     return end;
970   }
971 
MinSizingFornsGridContainerFrame::TrackSizingFunctions972   const nsStyleCoord& MinSizingFor(uint32_t aTrackIndex) const {
973     if (MOZ_UNLIKELY(aTrackIndex < mExplicitGridOffset)) {
974       return mAutoMinSizing;
975     }
976     uint32_t index = aTrackIndex - mExplicitGridOffset;
977     if (index >= mRepeatAutoStart) {
978       if (index < mRepeatAutoEnd) {
979         return mMinSizingFunctions[mRepeatAutoStart];
980       }
981       index -= mRepeatEndDelta;
982     }
983     return index < mMinSizingFunctions.Length() ? mMinSizingFunctions[index]
984                                                 : mAutoMinSizing;
985   }
MaxSizingFornsGridContainerFrame::TrackSizingFunctions986   const nsStyleCoord& MaxSizingFor(uint32_t aTrackIndex) const {
987     if (MOZ_UNLIKELY(aTrackIndex < mExplicitGridOffset)) {
988       return mAutoMaxSizing;
989     }
990     uint32_t index = aTrackIndex - mExplicitGridOffset;
991     if (index >= mRepeatAutoStart) {
992       if (index < mRepeatAutoEnd) {
993         return mMaxSizingFunctions[mRepeatAutoStart];
994       }
995       index -= mRepeatEndDelta;
996     }
997     return index < mMaxSizingFunctions.Length() ? mMaxSizingFunctions[index]
998                                                 : mAutoMaxSizing;
999   }
NumExplicitTracksnsGridContainerFrame::TrackSizingFunctions1000   uint32_t NumExplicitTracks() const {
1001     return mMinSizingFunctions.Length() + mRepeatEndDelta;
1002   }
NumRepeatTracksnsGridContainerFrame::TrackSizingFunctions1003   uint32_t NumRepeatTracks() const { return mRepeatAutoEnd - mRepeatAutoStart; }
SetNumRepeatTracksnsGridContainerFrame::TrackSizingFunctions1004   void SetNumRepeatTracks(uint32_t aNumRepeatTracks) {
1005     MOZ_ASSERT(mHasRepeatAuto || aNumRepeatTracks == 0);
1006     mRepeatAutoEnd = mRepeatAutoStart + aNumRepeatTracks;
1007     mRepeatEndDelta = mHasRepeatAuto ? int32_t(aNumRepeatTracks) - 1 : 0;
1008   }
1009 
1010   // Some style data references, for easy access.
1011   const nsTArray<nsStyleCoord>& mMinSizingFunctions;
1012   const nsTArray<nsStyleCoord>& mMaxSizingFunctions;
1013   const nsStyleCoord& mAutoMinSizing;
1014   const nsStyleCoord& mAutoMaxSizing;
1015   // Offset from the start of the implicit grid to the first explicit track.
1016   uint32_t mExplicitGridOffset;
1017   // The index of the repeat(auto-fill/fit) track, or zero if there is none.
1018   // Relative to mExplicitGridOffset (repeat tracks are explicit by definition).
1019   const uint32_t mRepeatAutoStart;
1020   // The (hypothetical) index of the last such repeat() track.
1021   uint32_t mRepeatAutoEnd;
1022   // The difference between mExplicitGridEnd and mMinSizingFunctions.Length().
1023   int32_t mRepeatEndDelta;
1024   // True if there is a specified repeat(auto-fill/fit) track.
1025   const bool mHasRepeatAuto;
1026   // True if this track (relative to mRepeatAutoStart) is a removed auto-fit.
1027   // Indexed relative to mExplicitGridOffset + mRepeatAutoStart.
1028   nsTArray<bool> mRemovedRepeatTracks;
1029 };
1030 
1031 /**
1032  * State for the tracks in one dimension.
1033  */
1034 struct nsGridContainerFrame::Tracks {
TracksnsGridContainerFrame::Tracks1035   explicit Tracks(LogicalAxis aAxis)
1036       : mStateUnion(TrackSize::StateBits(0)),
1037         mAxis(aAxis),
1038         mCanResolveLineRangeSize(false) {
1039     mBaselineSubtreeAlign[BaselineSharingGroup::eFirst] = NS_STYLE_ALIGN_AUTO;
1040     mBaselineSubtreeAlign[BaselineSharingGroup::eLast] = NS_STYLE_ALIGN_AUTO;
1041     mBaseline[BaselineSharingGroup::eFirst] = NS_INTRINSIC_WIDTH_UNKNOWN;
1042     mBaseline[BaselineSharingGroup::eLast] = NS_INTRINSIC_WIDTH_UNKNOWN;
1043   }
1044 
1045   void Initialize(const TrackSizingFunctions& aFunctions,
1046                   const nsStyleCoord& aGridGap, uint32_t aNumTracks,
1047                   nscoord aContentBoxSize);
1048 
1049   /**
1050    * Return the union of the state bits for the tracks in aRange.
1051    */
1052   TrackSize::StateBits StateBitsForRange(const LineRange& aRange) const;
1053 
1054   // Some data we collect for aligning baseline-aligned items.
1055   struct ItemBaselineData {
1056     uint32_t mBaselineTrack;
1057     nscoord mBaseline;
1058     nscoord mSize;
1059     GridItemInfo* mGridItem;
IsBaselineTrackLessThannsGridContainerFrame::Tracks::ItemBaselineData1060     static bool IsBaselineTrackLessThan(const ItemBaselineData& a,
1061                                         const ItemBaselineData& b) {
1062       return a.mBaselineTrack < b.mBaselineTrack;
1063     }
1064   };
1065 
1066   /**
1067    * Calculate baseline offsets for the given set of items.
1068    * Helper for InitialzeItemBaselines.
1069    */
1070   void CalculateItemBaselines(nsTArray<ItemBaselineData>& aBaselineItems,
1071                               BaselineSharingGroup aBaselineGroup);
1072 
1073   /**
1074    * Initialize grid item baseline state and offsets.
1075    */
1076   void InitializeItemBaselines(GridReflowInput& aState,
1077                                nsTArray<GridItemInfo>& aGridItems);
1078 
1079   /**
1080    * Apply the additional alignment needed to align the baseline-aligned subtree
1081    * the item belongs to within its baseline track.
1082    */
1083   void AlignBaselineSubtree(const GridItemInfo& aGridItem) const;
1084 
1085   enum class TrackSizingPhase {
1086     eIntrinsicMinimums,
1087     eContentBasedMinimums,
1088     eMaxContentMinimums,
1089     eIntrinsicMaximums,
1090     eMaxContentMaximums,
1091   };
1092 
1093   // Some data we collect on each item for Step 2 of the Track Sizing Algorithm
1094   // in ResolveIntrinsicSize below.
1095   struct Step2ItemData final {
1096     uint32_t mSpan;
1097     TrackSize::StateBits mState;
1098     LineRange mLineRange;
1099     nscoord mMinSize;
1100     nscoord mMinContentContribution;
1101     nscoord mMaxContentContribution;
1102     nsIFrame* mFrame;
IsSpanLessThannsGridContainerFrame::Tracks::Step2ItemData1103     static bool IsSpanLessThan(const Step2ItemData& a, const Step2ItemData& b) {
1104       return a.mSpan < b.mSpan;
1105     }
1106 
1107     template <TrackSizingPhase phase>
SizeContributionForPhasensGridContainerFrame::Tracks::Step2ItemData1108     nscoord SizeContributionForPhase() const {
1109       switch (phase) {
1110         case TrackSizingPhase::eIntrinsicMinimums:
1111         case TrackSizingPhase::eIntrinsicMaximums:
1112           return mMinSize;
1113         case TrackSizingPhase::eContentBasedMinimums:
1114           return mMinContentContribution;
1115         case TrackSizingPhase::eMaxContentMinimums:
1116         case TrackSizingPhase::eMaxContentMaximums:
1117           return mMaxContentContribution;
1118       }
1119       MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
1120     }
1121   };
1122 
1123   using FitContentClamper =
1124       std::function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
1125 
1126   // Helper method for ResolveIntrinsicSize.
1127   template <TrackSizingPhase phase>
1128   bool GrowSizeForSpanningItems(nsTArray<Step2ItemData>::iterator aIter,
1129                                 const nsTArray<Step2ItemData>::iterator aEnd,
1130                                 nsTArray<uint32_t>& aTracks,
1131                                 nsTArray<TrackSize>& aPlan,
1132                                 nsTArray<TrackSize>& aItemPlan,
1133                                 TrackSize::StateBits aSelector,
1134                                 const FitContentClamper& aClamper = nullptr,
1135                                 bool aNeedInfinitelyGrowableFlag = false);
1136   /**
1137    * Resolve Intrinsic Track Sizes.
1138    * http://dev.w3.org/csswg/css-grid/#algo-content
1139    */
1140   void ResolveIntrinsicSize(GridReflowInput& aState,
1141                             nsTArray<GridItemInfo>& aGridItems,
1142                             const TrackSizingFunctions& aFunctions,
1143                             LineRange GridArea::*aRange,
1144                             nscoord aPercentageBasis,
1145                             SizingConstraint aConstraint);
1146 
1147   /**
1148    * Helper for ResolveIntrinsicSize.  It implements step 1 "size tracks to fit
1149    * non-spanning items" in the spec.  Return true if the track has a <flex>
1150    * max-sizing function, false otherwise.
1151    */
1152   bool ResolveIntrinsicSizeStep1(GridReflowInput& aState,
1153                                  const TrackSizingFunctions& aFunctions,
1154                                  nscoord aPercentageBasis,
1155                                  SizingConstraint aConstraint,
1156                                  const LineRange& aRange,
1157                                  const GridItemInfo& aGridItem);
1158 
1159   // Helper method that returns the track size to use in §11.5.1.2
1160   // https://drafts.csswg.org/css-grid/#extra-space
1161   template <TrackSizingPhase phase>
StartSizeInDistributionnsGridContainerFrame::Tracks1162   static nscoord StartSizeInDistribution(const TrackSize& aSize) {
1163     switch (phase) {
1164       case TrackSizingPhase::eIntrinsicMinimums:
1165       case TrackSizingPhase::eContentBasedMinimums:
1166       case TrackSizingPhase::eMaxContentMinimums:
1167         return aSize.mBase;
1168       case TrackSizingPhase::eIntrinsicMaximums:
1169       case TrackSizingPhase::eMaxContentMaximums:
1170         if (aSize.mLimit == NS_UNCONSTRAINEDSIZE) {
1171           return aSize.mBase;
1172         }
1173         return aSize.mLimit;
1174     }
1175     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
1176   }
1177 
1178   /**
1179    * Collect the tracks which are growable (matching aSelector) into
1180    * aGrowableTracks, and return the amount of space that can be used
1181    * to grow those tracks.  This method implements CSS Grid §11.5.1.2.
1182    * https://drafts.csswg.org/css-grid/#extra-space
1183    */
1184   template <TrackSizingPhase phase>
CollectGrowablensGridContainerFrame::Tracks1185   nscoord CollectGrowable(nscoord aAvailableSpace, const LineRange& aRange,
1186                           TrackSize::StateBits aSelector,
1187                           nsTArray<uint32_t>& aGrowableTracks) const {
1188     MOZ_ASSERT(aAvailableSpace > 0, "why call me?");
1189     nscoord space = aAvailableSpace - mGridGap * (aRange.Extent() - 1);
1190     const uint32_t start = aRange.mStart;
1191     const uint32_t end = aRange.mEnd;
1192     for (uint32_t i = start; i < end; ++i) {
1193       const TrackSize& sz = mSizes[i];
1194       space -= StartSizeInDistribution<phase>(sz);
1195       if (space <= 0) {
1196         return 0;
1197       }
1198       if (sz.mState & aSelector) {
1199         aGrowableTracks.AppendElement(i);
1200       }
1201     }
1202     return aGrowableTracks.IsEmpty() ? 0 : space;
1203   }
1204 
1205   template <TrackSizingPhase phase>
InitializeItemPlannsGridContainerFrame::Tracks1206   void InitializeItemPlan(nsTArray<TrackSize>& aItemPlan,
1207                           const nsTArray<uint32_t>& aTracks) const {
1208     for (uint32_t track : aTracks) {
1209       auto& plan = aItemPlan[track];
1210       const TrackSize& sz = mSizes[track];
1211       plan.mBase = StartSizeInDistribution<phase>(sz);
1212       bool unlimited = sz.mState & TrackSize::eInfinitelyGrowable;
1213       plan.mLimit = unlimited ? NS_UNCONSTRAINEDSIZE : sz.mLimit;
1214       plan.mState = sz.mState;
1215     }
1216   }
1217 
1218   template <TrackSizingPhase phase>
InitializePlannsGridContainerFrame::Tracks1219   void InitializePlan(nsTArray<TrackSize>& aPlan) const {
1220     for (size_t i = 0, len = aPlan.Length(); i < len; ++i) {
1221       auto& plan = aPlan[i];
1222       const auto& sz = mSizes[i];
1223       plan.mBase = StartSizeInDistribution<phase>(sz);
1224       MOZ_ASSERT(phase == TrackSizingPhase::eMaxContentMaximums ||
1225                      !(sz.mState & TrackSize::eInfinitelyGrowable),
1226                  "forgot to reset the eInfinitelyGrowable bit?");
1227       plan.mState = sz.mState;
1228     }
1229   }
1230 
1231   template <TrackSizingPhase phase>
CopyPlanToSizensGridContainerFrame::Tracks1232   void CopyPlanToSize(const nsTArray<TrackSize>& aPlan,
1233                       bool aNeedInfinitelyGrowableFlag = false) {
1234     for (size_t i = 0, len = mSizes.Length(); i < len; ++i) {
1235       const auto& plan = aPlan[i];
1236       MOZ_ASSERT(plan.mBase >= 0);
1237       auto& sz = mSizes[i];
1238       switch (phase) {
1239         case TrackSizingPhase::eIntrinsicMinimums:
1240         case TrackSizingPhase::eContentBasedMinimums:
1241         case TrackSizingPhase::eMaxContentMinimums:
1242           sz.mBase = plan.mBase;
1243           break;
1244         case TrackSizingPhase::eIntrinsicMaximums:
1245           if (plan.mState & TrackSize::eModified) {
1246             if (sz.mLimit == NS_UNCONSTRAINEDSIZE &&
1247                 aNeedInfinitelyGrowableFlag) {
1248               sz.mState |= TrackSize::eInfinitelyGrowable;
1249             }
1250             sz.mLimit = plan.mBase;
1251           }
1252           break;
1253         case TrackSizingPhase::eMaxContentMaximums:
1254           if (plan.mState & TrackSize::eModified) {
1255             sz.mLimit = plan.mBase;
1256           }
1257           sz.mState &= ~TrackSize::eInfinitelyGrowable;
1258           break;
1259       }
1260     }
1261   }
1262 
1263   /**
1264    * Grow the planned size for tracks in aGrowableTracks up to their limit
1265    * and then freeze them (all aGrowableTracks must be unfrozen on entry).
1266    * Subtract the space added from aAvailableSpace and return that.
1267    */
GrowTracksToLimitnsGridContainerFrame::Tracks1268   nscoord GrowTracksToLimit(nscoord aAvailableSpace, nsTArray<TrackSize>& aPlan,
1269                             const nsTArray<uint32_t>& aGrowableTracks,
1270                             const FitContentClamper& aFitContentClamper) const {
1271     MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0);
1272     nscoord space = aAvailableSpace;
1273     uint32_t numGrowable = aGrowableTracks.Length();
1274     while (true) {
1275       nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
1276       for (uint32_t track : aGrowableTracks) {
1277         TrackSize& sz = aPlan[track];
1278         if (sz.IsFrozen()) {
1279           continue;
1280         }
1281         nscoord newBase = sz.mBase + spacePerTrack;
1282         nscoord limit = sz.mLimit;
1283         if (MOZ_UNLIKELY((sz.mState & TrackSize::eFitContent) &&
1284                          aFitContentClamper)) {
1285           // Clamp the limit to the fit-content() size, for §12.5.2 step 5/6.
1286           aFitContentClamper(track, sz.mBase, &limit);
1287         }
1288         if (newBase > limit) {
1289           nscoord consumed = limit - sz.mBase;
1290           if (consumed > 0) {
1291             space -= consumed;
1292             sz.mBase = limit;
1293           }
1294           sz.mState |= TrackSize::eFrozen;
1295           if (--numGrowable == 0) {
1296             return space;
1297           }
1298         } else {
1299           sz.mBase = newBase;
1300           space -= spacePerTrack;
1301         }
1302         MOZ_ASSERT(space >= 0);
1303         if (space == 0) {
1304           return 0;
1305         }
1306       }
1307     }
1308     MOZ_ASSERT_UNREACHABLE("we don't exit the loop above except by return");
1309     return 0;
1310   }
1311 
1312   /**
1313    * Helper for GrowSelectedTracksUnlimited.  For the set of tracks (S) that
1314    * match aMinSizingSelector: if a track in S doesn't match aMaxSizingSelector
1315    * then mark it with aSkipFlag.  If all tracks in S were marked then unmark
1316    * them.  Return aNumGrowable minus the number of tracks marked.  It is
1317    * assumed that aPlan have no aSkipFlag set for tracks in aGrowableTracks
1318    * on entry to this method.
1319    */
MarkExcludedTracksnsGridContainerFrame::Tracks1320   static uint32_t MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
1321                                      uint32_t aNumGrowable,
1322                                      const nsTArray<uint32_t>& aGrowableTracks,
1323                                      TrackSize::StateBits aMinSizingSelector,
1324                                      TrackSize::StateBits aMaxSizingSelector,
1325                                      TrackSize::StateBits aSkipFlag) {
1326     bool foundOneSelected = false;
1327     bool foundOneGrowable = false;
1328     uint32_t numGrowable = aNumGrowable;
1329     for (uint32_t track : aGrowableTracks) {
1330       TrackSize& sz = aPlan[track];
1331       const auto state = sz.mState;
1332       if (state & aMinSizingSelector) {
1333         foundOneSelected = true;
1334         if (state & aMaxSizingSelector) {
1335           foundOneGrowable = true;
1336           continue;
1337         }
1338         sz.mState |= aSkipFlag;
1339         MOZ_ASSERT(numGrowable != 0);
1340         --numGrowable;
1341       }
1342     }
1343     // 12.5 "if there are no such tracks, then all affected tracks"
1344     if (foundOneSelected && !foundOneGrowable) {
1345       for (uint32_t track : aGrowableTracks) {
1346         aPlan[track].mState &= ~aSkipFlag;
1347       }
1348       numGrowable = aNumGrowable;
1349     }
1350     return numGrowable;
1351   }
1352 
1353   /**
1354    * Mark all tracks in aGrowableTracks with an eSkipGrowUnlimited bit if
1355    * they *shouldn't* grow unlimited in §11.5.1.2.3 "Distribute space beyond
1356    * growth limits" https://drafts.csswg.org/css-grid/#extra-space
1357    * Return the number of tracks that are still growable.
1358    */
1359   template <TrackSizingPhase phase>
MarkExcludedTracksnsGridContainerFrame::Tracks1360   static uint32_t MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
1361                                      const nsTArray<uint32_t>& aGrowableTracks,
1362                                      TrackSize::StateBits aSelector) {
1363     uint32_t numGrowable = aGrowableTracks.Length();
1364     if (phase == TrackSizingPhase::eIntrinsicMaximums ||
1365         phase == TrackSizingPhase::eMaxContentMaximums) {
1366       // "when handling any intrinsic growth limit: all affected tracks"
1367       return numGrowable;
1368     }
1369     MOZ_ASSERT(aSelector == (aSelector & TrackSize::eIntrinsicMinSizing) &&
1370                    (aSelector & TrackSize::eMaxContentMinSizing),
1371                "Should only get here for track sizing steps 2.1 to 2.3");
1372     // Note that eMaxContentMinSizing is always included. We do those first:
1373     numGrowable = MarkExcludedTracks(
1374         aPlan, numGrowable, aGrowableTracks, TrackSize::eMaxContentMinSizing,
1375         TrackSize::eMaxContentMaxSizing, TrackSize::eSkipGrowUnlimited1);
1376     // Now mark min-content/auto min-sizing tracks if requested.
1377     auto minOrAutoSelector = aSelector & ~TrackSize::eMaxContentMinSizing;
1378     if (minOrAutoSelector) {
1379       numGrowable = MarkExcludedTracks(
1380           aPlan, numGrowable, aGrowableTracks, minOrAutoSelector,
1381           TrackSize::eIntrinsicMaxSizing, TrackSize::eSkipGrowUnlimited2);
1382     }
1383     return numGrowable;
1384   }
1385 
1386   /**
1387    * Increase the planned size for tracks in aGrowableTracks that aren't
1388    * marked with a eSkipGrowUnlimited flag beyond their limit.
1389    * This implements the "Distribute space beyond growth limits" step in
1390    * https://drafts.csswg.org/css-grid/#distribute-extra-space
1391    */
GrowSelectedTracksUnlimitednsGridContainerFrame::Tracks1392   void GrowSelectedTracksUnlimited(
1393       nscoord aAvailableSpace, nsTArray<TrackSize>& aPlan,
1394       const nsTArray<uint32_t>& aGrowableTracks, uint32_t aNumGrowable,
1395       const FitContentClamper& aFitContentClamper) const {
1396     MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0 &&
1397                aNumGrowable <= aGrowableTracks.Length());
1398     nscoord space = aAvailableSpace;
1399     DebugOnly<bool> didClamp = false;
1400     while (aNumGrowable) {
1401       nscoord spacePerTrack = std::max<nscoord>(space / aNumGrowable, 1);
1402       for (uint32_t track : aGrowableTracks) {
1403         TrackSize& sz = aPlan[track];
1404         if (sz.mState & TrackSize::eSkipGrowUnlimited) {
1405           continue;  // an excluded track
1406         }
1407         nscoord delta = spacePerTrack;
1408         nscoord newBase = sz.mBase + delta;
1409         if (MOZ_UNLIKELY((sz.mState & TrackSize::eFitContent) &&
1410                          aFitContentClamper)) {
1411           // Clamp newBase to the fit-content() size, for §12.5.2 step 5/6.
1412           if (aFitContentClamper(track, sz.mBase, &newBase)) {
1413             didClamp = true;
1414             delta = newBase - sz.mBase;
1415             MOZ_ASSERT(delta >= 0, "track size shouldn't shrink");
1416             sz.mState |= TrackSize::eSkipGrowUnlimited1;
1417             --aNumGrowable;
1418           }
1419         }
1420         sz.mBase = newBase;
1421         space -= delta;
1422         MOZ_ASSERT(space >= 0);
1423         if (space == 0) {
1424           return;
1425         }
1426       }
1427     }
1428     MOZ_ASSERT(didClamp,
1429                "we don't exit the loop above except by return, "
1430                "unless we clamped some track's size");
1431   }
1432 
1433   /**
1434    * Distribute aAvailableSpace to the planned base size for aGrowableTracks
1435    * up to their limits, then distribute the remaining space beyond the limits.
1436    */
1437   template <TrackSizingPhase phase>
DistributeToTrackSizesnsGridContainerFrame::Tracks1438   void DistributeToTrackSizes(nscoord aAvailableSpace,
1439                               nsTArray<TrackSize>& aPlan,
1440                               nsTArray<TrackSize>& aItemPlan,
1441                               nsTArray<uint32_t>& aGrowableTracks,
1442                               TrackSize::StateBits aSelector,
1443                               const FitContentClamper& aFitContentClamper) {
1444     InitializeItemPlan<phase>(aItemPlan, aGrowableTracks);
1445     nscoord space = GrowTracksToLimit(aAvailableSpace, aItemPlan,
1446                                       aGrowableTracks, aFitContentClamper);
1447     if (space > 0) {
1448       uint32_t numGrowable =
1449           MarkExcludedTracks<phase>(aItemPlan, aGrowableTracks, aSelector);
1450       GrowSelectedTracksUnlimited(space, aItemPlan, aGrowableTracks,
1451                                   numGrowable, aFitContentClamper);
1452     }
1453     for (uint32_t track : aGrowableTracks) {
1454       nscoord& plannedSize = aPlan[track].mBase;
1455       nscoord itemIncurredSize = aItemPlan[track].mBase;
1456       if (plannedSize < itemIncurredSize) {
1457         plannedSize = itemIncurredSize;
1458       }
1459     }
1460   }
1461 
1462   /**
1463    * Distribute aAvailableSize to the tracks.  This implements 12.6 at:
1464    * http://dev.w3.org/csswg/css-grid/#algo-grow-tracks
1465    */
DistributeFreeSpacensGridContainerFrame::Tracks1466   void DistributeFreeSpace(nscoord aAvailableSize) {
1467     const uint32_t numTracks = mSizes.Length();
1468     if (MOZ_UNLIKELY(numTracks == 0 || aAvailableSize <= 0)) {
1469       return;
1470     }
1471     if (aAvailableSize == NS_UNCONSTRAINEDSIZE) {
1472       for (TrackSize& sz : mSizes) {
1473         sz.mBase = sz.mLimit;
1474       }
1475     } else {
1476       // Compute free space and count growable tracks.
1477       nscoord space = aAvailableSize;
1478       uint32_t numGrowable = numTracks;
1479       for (const TrackSize& sz : mSizes) {
1480         space -= sz.mBase;
1481         MOZ_ASSERT(sz.mBase <= sz.mLimit);
1482         if (sz.mBase == sz.mLimit) {
1483           --numGrowable;
1484         }
1485       }
1486       // Distribute the free space evenly to the growable tracks. If not exactly
1487       // divisable the remainder is added to the leading tracks.
1488       while (space > 0 && numGrowable) {
1489         nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
1490         for (uint32_t i = 0; i < numTracks && space > 0; ++i) {
1491           TrackSize& sz = mSizes[i];
1492           if (sz.mBase == sz.mLimit) {
1493             continue;
1494           }
1495           nscoord newBase = sz.mBase + spacePerTrack;
1496           if (newBase >= sz.mLimit) {
1497             space -= sz.mLimit - sz.mBase;
1498             sz.mBase = sz.mLimit;
1499             --numGrowable;
1500           } else {
1501             space -= spacePerTrack;
1502             sz.mBase = newBase;
1503           }
1504         }
1505       }
1506     }
1507   }
1508 
1509   /**
1510    * Implements "12.7.1. Find the Size of an 'fr'".
1511    * http://dev.w3.org/csswg/css-grid/#algo-find-fr-size
1512    * (The returned value is a 'nscoord' divided by a factor - a floating type
1513    * is used to avoid intermediary rounding errors.)
1514    */
1515   float FindFrUnitSize(const LineRange& aRange,
1516                        const nsTArray<uint32_t>& aFlexTracks,
1517                        const TrackSizingFunctions& aFunctions,
1518                        nscoord aSpaceToFill) const;
1519 
1520   /**
1521    * Implements the "find the used flex fraction" part of StretchFlexibleTracks.
1522    * (The returned value is a 'nscoord' divided by a factor - a floating type
1523    * is used to avoid intermediary rounding errors.)
1524    */
1525   float FindUsedFlexFraction(GridReflowInput& aState,
1526                              nsTArray<GridItemInfo>& aGridItems,
1527                              const nsTArray<uint32_t>& aFlexTracks,
1528                              const TrackSizingFunctions& aFunctions,
1529                              nscoord aAvailableSize) const;
1530 
1531   /**
1532    * Implements "12.7. Stretch Flexible Tracks"
1533    * http://dev.w3.org/csswg/css-grid/#algo-flex-tracks
1534    */
1535   void StretchFlexibleTracks(GridReflowInput& aState,
1536                              nsTArray<GridItemInfo>& aGridItems,
1537                              const TrackSizingFunctions& aFunctions,
1538                              nscoord aAvailableSize);
1539 
1540   /**
1541    * Implements "12.3. Track Sizing Algorithm"
1542    * http://dev.w3.org/csswg/css-grid/#algo-track-sizing
1543    */
1544   void CalculateSizes(GridReflowInput& aState,
1545                       nsTArray<GridItemInfo>& aGridItems,
1546                       const TrackSizingFunctions& aFunctions,
1547                       nscoord aContentSize, LineRange GridArea::*aRange,
1548                       SizingConstraint aConstraint);
1549 
1550   /**
1551    * Apply 'align/justify-content', whichever is relevant for this axis.
1552    * https://drafts.csswg.org/css-align-3/#propdef-align-content
1553    */
1554   void AlignJustifyContent(const nsStylePosition* aStyle, WritingMode aWM,
1555                            const LogicalSize& aContainerSize);
1556 
1557   /**
1558    * Return the intrinsic size by back-computing percentages as:
1559    * IntrinsicSize = SumOfCoordSizes / (1 - SumOfPercentages).
1560    */
1561   nscoord BackComputedIntrinsicSize(const TrackSizingFunctions& aFunctions,
1562                                     const nsStyleCoord& aGridGap) const;
1563 
GridLineEdgensGridContainerFrame::Tracks1564   nscoord GridLineEdge(uint32_t aLine, GridLineSide aSide) const {
1565     if (MOZ_UNLIKELY(mSizes.IsEmpty())) {
1566       // https://drafts.csswg.org/css-grid/#grid-definition
1567       // "... the explicit grid still contains one grid line in each axis."
1568       MOZ_ASSERT(aLine == 0, "We should only resolve line 1 in an empty grid");
1569       return nscoord(0);
1570     }
1571     MOZ_ASSERT(aLine <= mSizes.Length(), "mSizes is too small");
1572     if (aSide == GridLineSide::eBeforeGridGap) {
1573       if (aLine == 0) {
1574         return nscoord(0);
1575       }
1576       const TrackSize& sz = mSizes[aLine - 1];
1577       return sz.mPosition + sz.mBase;
1578     }
1579     if (aLine == mSizes.Length()) {
1580       return mContentBoxSize;
1581     }
1582     return mSizes[aLine].mPosition;
1583   }
1584 
SumOfGridGapsnsGridContainerFrame::Tracks1585   nscoord SumOfGridGaps() const {
1586     auto len = mSizes.Length();
1587     return MOZ_LIKELY(len > 1) ? (len - 1) * mGridGap : 0;
1588   }
1589 
1590   /**
1591    * Break before aRow, i.e. set the eBreakBefore flag on aRow and set the grid
1592    * gap before aRow to zero (and shift all rows after it by the removed gap).
1593    */
BreakBeforeRownsGridContainerFrame::Tracks1594   void BreakBeforeRow(uint32_t aRow) {
1595     MOZ_ASSERT(mAxis == eLogicalAxisBlock,
1596                "Should only be fragmenting in the block axis (between rows)");
1597     nscoord prevRowEndPos = 0;
1598     if (aRow != 0) {
1599       auto& prevSz = mSizes[aRow - 1];
1600       prevRowEndPos = prevSz.mPosition + prevSz.mBase;
1601     }
1602     auto& sz = mSizes[aRow];
1603     const nscoord gap = sz.mPosition - prevRowEndPos;
1604     sz.mState |= TrackSize::eBreakBefore;
1605     if (gap != 0) {
1606       for (uint32_t i = aRow, len = mSizes.Length(); i < len; ++i) {
1607         mSizes[i].mPosition -= gap;
1608       }
1609     }
1610   }
1611 
1612   /**
1613    * Set the size of aRow to aSize and adjust the position of all rows after it.
1614    */
ResizeRownsGridContainerFrame::Tracks1615   void ResizeRow(uint32_t aRow, nscoord aNewSize) {
1616     MOZ_ASSERT(mAxis == eLogicalAxisBlock,
1617                "Should only be fragmenting in the block axis (between rows)");
1618     MOZ_ASSERT(aNewSize >= 0);
1619     auto& sz = mSizes[aRow];
1620     nscoord delta = aNewSize - sz.mBase;
1621     NS_WARNING_ASSERTION(delta != nscoord(0), "Useless call to ResizeRow");
1622     sz.mBase = aNewSize;
1623     const uint32_t numRows = mSizes.Length();
1624     for (uint32_t r = aRow + 1; r < numRows; ++r) {
1625       mSizes[r].mPosition += delta;
1626     }
1627   }
1628 
ResolveSizensGridContainerFrame::Tracks1629   nscoord ResolveSize(const LineRange& aRange) const {
1630     MOZ_ASSERT(mCanResolveLineRangeSize);
1631     MOZ_ASSERT(aRange.Extent() > 0, "grid items cover at least one track");
1632     nscoord pos, size;
1633     aRange.ToPositionAndLength(mSizes, &pos, &size);
1634     return size;
1635   }
1636 
GetExplicitLineNamesAtIndexnsGridContainerFrame::Tracks1637   nsTArray<nsString> GetExplicitLineNamesAtIndex(
1638       const nsStyleGridTemplate& aGridTemplate,
1639       const TrackSizingFunctions& aFunctions, uint32_t aIndex) {
1640     nsTArray<nsString> lineNames;
1641 
1642     bool hasRepeatAuto = aGridTemplate.HasRepeatAuto();
1643     const nsTArray<nsTArray<nsString>>& lineNameLists(
1644         aGridTemplate.mLineNameLists);
1645 
1646     if (!hasRepeatAuto) {
1647       if (aIndex < lineNameLists.Length()) {
1648         lineNames.AppendElements(lineNameLists[aIndex]);
1649       }
1650     } else {
1651       const uint32_t repeatTrackCount = aFunctions.NumRepeatTracks();
1652       const uint32_t repeatAutoStart = aGridTemplate.mRepeatAutoIndex;
1653       const uint32_t repeatAutoEnd = (repeatAutoStart + repeatTrackCount);
1654       const int32_t repeatEndDelta = int32_t(repeatTrackCount - 1);
1655 
1656       if (aIndex <= repeatAutoStart) {
1657         if (aIndex < lineNameLists.Length()) {
1658           lineNames.AppendElements(lineNameLists[aIndex]);
1659         }
1660       }
1661       if (aIndex <= repeatAutoEnd && aIndex > repeatAutoStart) {
1662         lineNames.AppendElements(aGridTemplate.mRepeatAutoLineNameListAfter);
1663       }
1664       if (aIndex < repeatAutoEnd && aIndex >= repeatAutoStart) {
1665         lineNames.AppendElements(aGridTemplate.mRepeatAutoLineNameListBefore);
1666       }
1667       if (aIndex > repeatAutoEnd && aIndex > repeatAutoStart) {
1668         uint32_t i = aIndex - repeatEndDelta;
1669         if (i < lineNameLists.Length()) {
1670           lineNames.AppendElements(lineNameLists[i]);
1671         }
1672       }
1673     }
1674 
1675     return lineNames;
1676   }
1677 
1678 #ifdef DEBUG
DumpnsGridContainerFrame::Tracks1679   void Dump() const {
1680     for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
1681       printf("  %d: ", i);
1682       mSizes[i].Dump();
1683       printf("\n");
1684     }
1685   }
1686 #endif
1687 
1688   AutoTArray<TrackSize, 32> mSizes;
1689   nscoord mContentBoxSize;
1690   nscoord mGridGap;
1691   // The first(last)-baseline for the first(last) track in this axis.
1692   nscoord mBaseline[2];  // index by BaselineSharingGroup
1693   // The union of the track min/max-sizing state bits in this axis.
1694   TrackSize::StateBits mStateUnion;
1695   LogicalAxis mAxis;
1696   // Used for aligning a baseline-aligned subtree of items.  The only possible
1697   // values are NS_STYLE_ALIGN_{START,END,CENTER,AUTO}.  AUTO means there are
1698   // no baseline-aligned items in any track in that axis.
1699   // There is one alignment value for each BaselineSharingGroup.
1700   uint8_t mBaselineSubtreeAlign[2];
1701   // True if track positions and sizes are final in this axis.
1702   bool mCanResolveLineRangeSize;
1703 };
1704 
1705 /**
1706  * Grid data shared by all continuations, owned by the first-in-flow.
1707  * The data is initialized from the first-in-flow's GridReflowInput at
1708  * the end of its reflow.  Fragmentation will modify mRows.mSizes -
1709  * the mPosition to remove the row gap at the break boundary, the mState
1710  * by setting the eBreakBefore flag, and mBase is modified when we decide
1711  * to grow a row.  mOriginalRowData is setup by the first-in-flow and
1712  * not modified after that.  It's used for undoing the changes to mRows.
1713  * mCols, mGridItems, mAbsPosItems are used for initializing the grid
1714  * reflow state for continuations, see GridReflowInput::Initialize below.
1715  */
1716 struct nsGridContainerFrame::SharedGridData {
SharedGridDatansGridContainerFrame::SharedGridData1717   SharedGridData()
1718       : mCols(eLogicalAxisInline),
1719         mRows(eLogicalAxisBlock),
1720         mGenerateComputedGridInfo(false) {}
1721   Tracks mCols;
1722   Tracks mRows;
1723   struct RowData {
1724     nscoord mBase;  // the original track size
1725     nscoord mGap;   // the original gap before a track
1726   };
1727   nsTArray<RowData> mOriginalRowData;
1728   nsTArray<GridItemInfo> mGridItems;
1729   nsTArray<GridItemInfo> mAbsPosItems;
1730   bool mGenerateComputedGridInfo;
1731 
1732   /**
1733    * Only set on the first-in-flow.  Continuations will Initialize() their
1734    * GridReflowInput from it.
1735    */
1736   NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, SharedGridData)
1737 };
1738 
1739 struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput {
GridReflowInputnsGridContainerFrame::GridReflowInput1740   GridReflowInput(nsGridContainerFrame* aFrame, const ReflowInput& aRI)
1741       : GridReflowInput(aFrame, *aRI.mRenderingContext, &aRI,
1742                         aRI.mStylePosition, aRI.GetWritingMode()) {}
GridReflowInputnsGridContainerFrame::GridReflowInput1743   GridReflowInput(nsGridContainerFrame* aFrame, gfxContext& aRC)
1744       : GridReflowInput(aFrame, aRC, nullptr, aFrame->StylePosition(),
1745                         aFrame->GetWritingMode()) {}
1746 
1747   /**
1748    * Initialize our track sizes and grid item info using the shared
1749    * state from aGridContainerFrame first-in-flow.
1750    */
InitializeForContinuationnsGridContainerFrame::GridReflowInput1751   void InitializeForContinuation(nsGridContainerFrame* aGridContainerFrame,
1752                                  nscoord aConsumedBSize) {
1753     MOZ_ASSERT(aGridContainerFrame->GetPrevInFlow(),
1754                "don't call this on the first-in-flow");
1755     MOZ_ASSERT(mGridItems.IsEmpty() && mAbsPosItems.IsEmpty(),
1756                "shouldn't have any item data yet");
1757 
1758     // Get the SharedGridData from the first-in-flow. Also calculate the number
1759     // of fragments before this so that we can figure out our start row below.
1760     uint32_t fragment = 0;
1761     nsIFrame* firstInFlow = aGridContainerFrame;
1762     for (auto pif = aGridContainerFrame->GetPrevInFlow(); pif;
1763          pif = pif->GetPrevInFlow()) {
1764       ++fragment;
1765       firstInFlow = pif;
1766     }
1767     mSharedGridData = firstInFlow->GetProperty(SharedGridData::Prop());
1768     MOZ_ASSERT(mSharedGridData, "first-in-flow must have SharedGridData");
1769 
1770     // Find the start row for this fragment and undo breaks after that row
1771     // since the breaks might be different from the last reflow.
1772     auto& rowSizes = mSharedGridData->mRows.mSizes;
1773     const uint32_t numRows = rowSizes.Length();
1774     mStartRow = numRows;
1775     for (uint32_t row = 0, breakCount = 0; row < numRows; ++row) {
1776       if (rowSizes[row].mState & TrackSize::eBreakBefore) {
1777         if (fragment == ++breakCount) {
1778           mStartRow = row;
1779           mFragBStart = rowSizes[row].mPosition;
1780           // Restore the original size for |row| and grid gaps / state after it.
1781           const auto& origRowData = mSharedGridData->mOriginalRowData;
1782           rowSizes[row].mBase = origRowData[row].mBase;
1783           nscoord prevEndPos = rowSizes[row].mPosition + rowSizes[row].mBase;
1784           while (++row < numRows) {
1785             auto& sz = rowSizes[row];
1786             const auto& orig = origRowData[row];
1787             sz.mPosition = prevEndPos + orig.mGap;
1788             sz.mBase = orig.mBase;
1789             sz.mState &= ~TrackSize::eBreakBefore;
1790             prevEndPos = sz.mPosition + sz.mBase;
1791           }
1792           break;
1793         }
1794       }
1795     }
1796     if (mStartRow == numRows) {
1797       // All of the grid's rows fit inside of previous grid-container fragments.
1798       mFragBStart = aConsumedBSize;
1799     }
1800 
1801     // Copy the shared track state.
1802     // XXX consider temporarily swapping the array elements instead and swapping
1803     // XXX them back after we're done reflowing, for better performance.
1804     // XXX (bug 1252002)
1805     mCols = mSharedGridData->mCols;
1806     mRows = mSharedGridData->mRows;
1807 
1808     // Copy item data from each child's first-in-flow data in mSharedGridData.
1809     // XXX NOTE: This is O(n^2) in the number of items. (bug 1252186)
1810     mIter.Reset();
1811     for (; !mIter.AtEnd(); mIter.Next()) {
1812       nsIFrame* child = *mIter;
1813       nsIFrame* childFirstInFlow = child->FirstInFlow();
1814       DebugOnly<size_t> len = mGridItems.Length();
1815       for (auto& itemInfo : mSharedGridData->mGridItems) {
1816         if (itemInfo.mFrame == childFirstInFlow) {
1817           auto item =
1818               mGridItems.AppendElement(GridItemInfo(child, itemInfo.mArea));
1819           // Copy the item's baseline data so that the item's last fragment can
1820           // do 'last baseline' alignment if necessary.
1821           item->mState[0] |= itemInfo.mState[0] & ItemState::eAllBaselineBits;
1822           item->mState[1] |= itemInfo.mState[1] & ItemState::eAllBaselineBits;
1823           item->mBaselineOffset[0] = itemInfo.mBaselineOffset[0];
1824           item->mBaselineOffset[1] = itemInfo.mBaselineOffset[1];
1825           break;
1826         }
1827       }
1828       MOZ_ASSERT(mGridItems.Length() == len + 1, "can't find GridItemInfo");
1829     }
1830 
1831     // XXX NOTE: This is O(n^2) in the number of abs.pos. items. (bug 1252186)
1832     nsFrameList absPosChildren(aGridContainerFrame->GetChildList(
1833         aGridContainerFrame->GetAbsoluteListID()));
1834     for (auto f : absPosChildren) {
1835       nsIFrame* childFirstInFlow = f->FirstInFlow();
1836       DebugOnly<size_t> len = mAbsPosItems.Length();
1837       for (auto& itemInfo : mSharedGridData->mAbsPosItems) {
1838         if (itemInfo.mFrame == childFirstInFlow) {
1839           mAbsPosItems.AppendElement(GridItemInfo(f, itemInfo.mArea));
1840           break;
1841         }
1842       }
1843       MOZ_ASSERT(mAbsPosItems.Length() == len + 1, "can't find GridItemInfo");
1844     }
1845 
1846     // Copy in the computed grid info state bit
1847     if (mSharedGridData->mGenerateComputedGridInfo) {
1848       aGridContainerFrame->AddStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
1849     }
1850   }
1851 
1852   /**
1853    * Calculate our track sizes.  If the given aContentBox block-axis size is
1854    * unconstrained, it is assigned to the resulting intrinsic block-axis size.
1855    */
1856   void CalculateTrackSizes(const Grid& aGrid, LogicalSize& aContentBox,
1857                            SizingConstraint aConstraint);
1858 
1859   /**
1860    * Return the percentage basis for a grid item in its writing-mode.
1861    * If aAxis is eLogicalAxisInline then we return NS_UNCONSTRAINEDSIZE in
1862    * both axes since we know all track sizes are indefinite at this point
1863    * (we calculate column sizes before row sizes).  Otherwise, assert that
1864    * column sizes are known and calculate the size for aGridItem.mArea.mCols
1865    * and use NS_UNCONSTRAINEDSIZE in the other axis.
1866    * @param aAxis the axis we're currently calculating track sizes for
1867    */
1868   LogicalSize PercentageBasisFor(LogicalAxis aAxis,
1869                                  const GridItemInfo& aGridItem) const;
1870 
1871   /**
1872    * Return the containing block for a grid item occupying aArea.
1873    */
1874   LogicalRect ContainingBlockFor(const GridArea& aArea) const;
1875 
1876   /**
1877    * Return the containing block for an abs.pos. grid item occupying aArea.
1878    * Any 'auto' lines in the grid area will be aligned with grid container
1879    * containing block on that side.
1880    * @param aGridOrigin the origin of the grid
1881    * @param aGridCB the grid container containing block (its padding area)
1882    */
1883   LogicalRect ContainingBlockForAbsPos(const GridArea& aArea,
1884                                        const LogicalPoint& aGridOrigin,
1885                                        const LogicalRect& aGridCB) const;
1886 
1887   CSSOrderAwareFrameIterator mIter;
1888   const nsStylePosition* const mGridStyle;
1889   Tracks mCols;
1890   Tracks mRows;
1891   TrackSizingFunctions mColFunctions;
1892   TrackSizingFunctions mRowFunctions;
1893   /**
1894    * Info about each (normal flow) grid item.
1895    */
1896   nsTArray<GridItemInfo> mGridItems;
1897   /**
1898    * Info about each grid-aligned abs.pos. child.
1899    */
1900   nsTArray<GridItemInfo> mAbsPosItems;
1901 
1902   /**
1903    * @note mReflowInput may be null when using the 2nd ctor above. In this case
1904    * we'll construct a dummy parent reflow state if we need it to calculate
1905    * min/max-content contributions when sizing tracks.
1906    */
1907   const ReflowInput* const mReflowInput;
1908   gfxContext& mRenderingContext;
1909   nsGridContainerFrame* const mFrame;
1910   SharedGridData* mSharedGridData;  // [weak] owned by mFrame's first-in-flow.
1911   /** Computed border+padding with mSkipSides applied. */
1912   LogicalMargin mBorderPadding;
1913   /**
1914    * BStart of this fragment in "grid space" (i.e. the concatenation of content
1915    * areas of all fragments).  Equal to mRows.mSizes[mStartRow].mPosition,
1916    * or, if this fragment starts after the last row, the ConsumedBSize().
1917    */
1918   nscoord mFragBStart;
1919   /** The start row for this fragment. */
1920   uint32_t mStartRow;
1921   /**
1922    * The start row for the next fragment, if any.  If mNextFragmentStartRow ==
1923    * mStartRow then there are no rows in this fragment.
1924    */
1925   uint32_t mNextFragmentStartRow;
1926   /** Our tentative ApplySkipSides bits. */
1927   LogicalSides mSkipSides;
1928   const WritingMode mWM;
1929   /** Initialized lazily, when we find the fragmentainer. */
1930   bool mInFragmentainer;
1931 
1932  private:
GridReflowInputnsGridContainerFrame::GridReflowInput1933   GridReflowInput(nsGridContainerFrame* aFrame, gfxContext& aRenderingContext,
1934                   const ReflowInput* aReflowInput,
1935                   const nsStylePosition* aGridStyle, const WritingMode& aWM)
1936       : mIter(aFrame, kPrincipalList),
1937         mGridStyle(aGridStyle),
1938         mCols(eLogicalAxisInline),
1939         mRows(eLogicalAxisBlock),
1940         mColFunctions(mGridStyle->GridTemplateColumns(),
1941                       mGridStyle->mGridAutoColumnsMin,
1942                       mGridStyle->mGridAutoColumnsMax),
1943         mRowFunctions(mGridStyle->GridTemplateRows(),
1944                       mGridStyle->mGridAutoRowsMin,
1945                       mGridStyle->mGridAutoRowsMax),
1946         mReflowInput(aReflowInput),
1947         mRenderingContext(aRenderingContext),
1948         mFrame(aFrame),
1949         mSharedGridData(nullptr),
1950         mBorderPadding(aWM),
1951         mFragBStart(0),
1952         mStartRow(0),
1953         mNextFragmentStartRow(0),
1954         mWM(aWM),
1955         mInFragmentainer(false) {
1956     MOZ_ASSERT(!aReflowInput || aReflowInput->mFrame == mFrame);
1957     if (aReflowInput) {
1958       mBorderPadding = aReflowInput->ComputedLogicalBorderPadding();
1959       mSkipSides = aFrame->PreReflowBlockLevelLogicalSkipSides();
1960       mBorderPadding.ApplySkipSides(mSkipSides);
1961     }
1962   }
1963 };
1964 
1965 using GridReflowInput = nsGridContainerFrame::GridReflowInput;
1966 
1967 /**
1968  * The Grid implements grid item placement and the state of the grid -
1969  * the size of the explicit/implicit grid, which cells are occupied etc.
1970  */
1971 struct MOZ_STACK_CLASS nsGridContainerFrame::Grid {
1972   /**
1973    * Place all child frames into the grid and expand the (implicit) grid as
1974    * needed.  The allocated GridAreas are stored in the GridAreaProperty
1975    * frame property on the child frame.
1976    * @param aComputedMinSize the container's min-size - used to determine
1977    *   the number of repeat(auto-fill/fit) tracks.
1978    * @param aComputedSize the container's size - used to determine
1979    *   the number of repeat(auto-fill/fit) tracks.
1980    * @param aComputedMaxSize the container's max-size - used to determine
1981    *   the number of repeat(auto-fill/fit) tracks.
1982    */
1983   void PlaceGridItems(GridReflowInput& aState,
1984                       const LogicalSize& aComputedMinSize,
1985                       const LogicalSize& aComputedSize,
1986                       const LogicalSize& aComputedMaxSize);
1987 
1988   /**
1989    * As above but for an abs.pos. child.  Any 'auto' lines will be represented
1990    * by kAutoLine in the LineRange result.
1991    * @param aGridStart the first line in the final, but untranslated grid
1992    * @param aGridEnd the last line in the final, but untranslated grid
1993    */
1994   LineRange ResolveAbsPosLineRange(
1995       const nsStyleGridLine& aStart, const nsStyleGridLine& aEnd,
1996       const LineNameMap& aNameMap, uint32_t GridNamedArea::*aAreaStart,
1997       uint32_t GridNamedArea::*aAreaEnd, uint32_t aExplicitGridEnd,
1998       int32_t aGridStart, int32_t aGridEnd, const nsStylePosition* aStyle);
1999 
2000   /**
2001    * Return a GridArea for abs.pos. item with non-auto lines placed at
2002    * a definite line (1-based) with placement errors resolved.  One or both
2003    * positions may still be 'auto'.
2004    * @param aChild the abs.pos. grid item to place
2005    * @param aStyle the StylePosition() for the grid container
2006    */
2007   GridArea PlaceAbsPos(nsIFrame* aChild, const LineNameMap& aColLineNameMap,
2008                        const LineNameMap& aRowLineNameMap,
2009                        const nsStylePosition* aStyle);
2010 
2011   /**
2012    * Find the first column in row aLockedRow starting at aStartCol where aArea
2013    * could be placed without overlapping other items.  The returned column may
2014    * cause aArea to overflow the current implicit grid bounds if placed there.
2015    */
2016   uint32_t FindAutoCol(uint32_t aStartCol, uint32_t aLockedRow,
2017                        const GridArea* aArea) const;
2018 
2019   /**
2020    * Place aArea in the first column (in row aArea->mRows.mStart) starting at
2021    * aStartCol without overlapping other items.  The resulting aArea may
2022    * overflow the current implicit grid bounds.
2023    * Pre-condition: aArea->mRows.IsDefinite() is true.
2024    * Post-condition: aArea->IsDefinite() is true.
2025    */
2026   void PlaceAutoCol(uint32_t aStartCol, GridArea* aArea) const;
2027 
2028   /**
2029    * Find the first row in column aLockedCol starting at aStartRow where aArea
2030    * could be placed without overlapping other items.  The returned row may
2031    * cause aArea to overflow the current implicit grid bounds if placed there.
2032    */
2033   uint32_t FindAutoRow(uint32_t aLockedCol, uint32_t aStartRow,
2034                        const GridArea* aArea) const;
2035 
2036   /**
2037    * Place aArea in the first row (in column aArea->mCols.mStart) starting at
2038    * aStartRow without overlapping other items. The resulting aArea may
2039    * overflow the current implicit grid bounds.
2040    * Pre-condition: aArea->mCols.IsDefinite() is true.
2041    * Post-condition: aArea->IsDefinite() is true.
2042    */
2043   void PlaceAutoRow(uint32_t aStartRow, GridArea* aArea) const;
2044 
2045   /**
2046    * Place aArea in the first column starting at aStartCol,aStartRow without
2047    * causing it to overlap other items or overflow mGridColEnd.
2048    * If there's no such column in aStartRow, continue in position 1,aStartRow+1.
2049    * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
2050    * Post-condition: aArea->IsDefinite() is true.
2051    */
2052   void PlaceAutoAutoInRowOrder(uint32_t aStartCol, uint32_t aStartRow,
2053                                GridArea* aArea) const;
2054 
2055   /**
2056    * Place aArea in the first row starting at aStartCol,aStartRow without
2057    * causing it to overlap other items or overflow mGridRowEnd.
2058    * If there's no such row in aStartCol, continue in position aStartCol+1,1.
2059    * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
2060    * Post-condition: aArea->IsDefinite() is true.
2061    */
2062   void PlaceAutoAutoInColOrder(uint32_t aStartCol, uint32_t aStartRow,
2063                                GridArea* aArea) const;
2064 
2065   /**
2066    * Return aLine if it's inside the aMin..aMax range (inclusive),
2067    * otherwise return kAutoLine.
2068    */
AutoIfOutsidensGridContainerFrame::Grid2069   static int32_t AutoIfOutside(int32_t aLine, int32_t aMin, int32_t aMax) {
2070     MOZ_ASSERT(aMin <= aMax);
2071     if (aLine < aMin || aLine > aMax) {
2072       return kAutoLine;
2073     }
2074     return aLine;
2075   }
2076 
2077   /**
2078    * Inflate the implicit grid to include aArea.
2079    * @param aArea may be definite or auto
2080    */
InflateGridFornsGridContainerFrame::Grid2081   void InflateGridFor(const GridArea& aArea) {
2082     mGridColEnd = std::max(mGridColEnd, aArea.mCols.HypotheticalEnd());
2083     mGridRowEnd = std::max(mGridRowEnd, aArea.mRows.HypotheticalEnd());
2084     MOZ_ASSERT(mGridColEnd <= kTranslatedMaxLine &&
2085                mGridRowEnd <= kTranslatedMaxLine);
2086   }
2087 
2088   enum LineRangeSide { eLineRangeSideStart, eLineRangeSideEnd };
2089   /**
2090    * Return a line number for (non-auto) aLine, per:
2091    * http://dev.w3.org/csswg/css-grid/#line-placement
2092    * @param aLine style data for the line (must be non-auto)
2093    * @param aNth a number of lines to find from aFromIndex, negative if the
2094    *             search should be in reverse order.  In the case aLine has
2095    *             a specified line name, it's permitted to pass in zero which
2096    *             will be treated as one.
2097    * @param aFromIndex the zero-based index to start counting from
2098    * @param aLineNameList the explicit named lines
2099    * @param aAreaStart a pointer to GridNamedArea::mColumnStart/mRowStart
2100    * @param aAreaEnd a pointer to GridNamedArea::mColumnEnd/mRowEnd
2101    * @param aExplicitGridEnd the last line in the explicit grid
2102    * @param aEdge indicates whether we are resolving a start or end line
2103    * @param aStyle the StylePosition() for the grid container
2104    * @return a definite line (1-based), clamped to the kMinLine..kMaxLine range
2105    */
2106   int32_t ResolveLine(const nsStyleGridLine& aLine, int32_t aNth,
2107                       uint32_t aFromIndex, const LineNameMap& aNameMap,
2108                       uint32_t GridNamedArea::*aAreaStart,
2109                       uint32_t GridNamedArea::*aAreaEnd,
2110                       uint32_t aExplicitGridEnd, LineRangeSide aSide,
2111                       const nsStylePosition* aStyle);
2112 
2113   /**
2114    * Helper method for ResolveLineRange.
2115    * @see ResolveLineRange
2116    * @return a pair (start,end) of lines
2117    */
2118   typedef std::pair<int32_t, int32_t> LinePair;
2119   LinePair ResolveLineRangeHelper(const nsStyleGridLine& aStart,
2120                                   const nsStyleGridLine& aEnd,
2121                                   const LineNameMap& aNameMap,
2122                                   uint32_t GridNamedArea::*aAreaStart,
2123                                   uint32_t GridNamedArea::*aAreaEnd,
2124                                   uint32_t aExplicitGridEnd,
2125                                   const nsStylePosition* aStyle);
2126 
2127   /**
2128    * Return a LineRange based on the given style data. Non-auto lines
2129    * are resolved to a definite line number (1-based) per:
2130    * http://dev.w3.org/csswg/css-grid/#line-placement
2131    * with placement errors corrected per:
2132    * http://dev.w3.org/csswg/css-grid/#grid-placement-errors
2133    * @param aStyle the StylePosition() for the grid container
2134    * @param aStart style data for the start line
2135    * @param aEnd style data for the end line
2136    * @param aLineNameList the explicit named lines
2137    * @param aAreaStart a pointer to GridNamedArea::mColumnStart/mRowStart
2138    * @param aAreaEnd a pointer to GridNamedArea::mColumnEnd/mRowEnd
2139    * @param aExplicitGridEnd the last line in the explicit grid
2140    * @param aStyle the StylePosition() for the grid container
2141    */
2142   LineRange ResolveLineRange(const nsStyleGridLine& aStart,
2143                              const nsStyleGridLine& aEnd,
2144                              const LineNameMap& aNameMap,
2145                              uint32_t GridNamedArea::*aAreaStart,
2146                              uint32_t GridNamedArea::*aAreaEnd,
2147                              uint32_t aExplicitGridEnd,
2148                              const nsStylePosition* aStyle);
2149 
2150   /**
2151    * Return a GridArea with non-auto lines placed at a definite line (1-based)
2152    * with placement errors resolved.  One or both positions may still
2153    * be 'auto'.
2154    * @param aChild the grid item
2155    * @param aStyle the StylePosition() for the grid container
2156    */
2157   GridArea PlaceDefinite(nsIFrame* aChild, const LineNameMap& aColLineNameMap,
2158                          const LineNameMap& aRowLineNameMap,
2159                          const nsStylePosition* aStyle);
2160 
HasImplicitNamedAreansGridContainerFrame::Grid2161   bool HasImplicitNamedArea(const nsString& aName) const {
2162     return mAreas && mAreas->Contains(aName);
2163   }
2164 
2165   /**
2166    * A convenience method to lookup a name in 'grid-template-areas'.
2167    * @param aStyle the StylePosition() for the grid container
2168    * @return null if not found
2169    */
FindNamedAreansGridContainerFrame::Grid2170   static const css::GridNamedArea* FindNamedArea(
2171       const nsAString& aName, const nsStylePosition* aStyle) {
2172     if (!aStyle->mGridTemplateAreas) {
2173       return nullptr;
2174     }
2175     const nsTArray<css::GridNamedArea>& areas =
2176         aStyle->mGridTemplateAreas->mNamedAreas;
2177     size_t len = areas.Length();
2178     for (size_t i = 0; i < len; ++i) {
2179       const css::GridNamedArea& area = areas[i];
2180       if (area.mName == aName) {
2181         return &area;
2182       }
2183     }
2184     return nullptr;
2185   }
2186 
2187   // Return true if aString ends in aSuffix and has at least one character
2188   // before the suffix. Assign aIndex to where the suffix starts.
IsNameWithSuffixnsGridContainerFrame::Grid2189   static bool IsNameWithSuffix(const nsString& aString, const nsString& aSuffix,
2190                                uint32_t* aIndex) {
2191     if (StringEndsWith(aString, aSuffix)) {
2192       *aIndex = aString.Length() - aSuffix.Length();
2193       return *aIndex != 0;
2194     }
2195     return false;
2196   }
2197 
IsNameWithEndSuffixnsGridContainerFrame::Grid2198   static bool IsNameWithEndSuffix(const nsString& aString, uint32_t* aIndex) {
2199     return IsNameWithSuffix(aString, NS_LITERAL_STRING("-end"), aIndex);
2200   }
2201 
IsNameWithStartSuffixnsGridContainerFrame::Grid2202   static bool IsNameWithStartSuffix(const nsString& aString, uint32_t* aIndex) {
2203     return IsNameWithSuffix(aString, NS_LITERAL_STRING("-start"), aIndex);
2204   }
2205 
2206   /**
2207    * A CellMap holds state for each cell in the grid.
2208    * It's row major.  It's sparse in the sense that it only has enough rows to
2209    * cover the last row that has a grid item.  Each row only has enough entries
2210    * to cover columns that are occupied *on that row*, i.e. it's not a full
2211    * matrix covering the entire implicit grid.  An absent Cell means that it's
2212    * unoccupied by any grid item.
2213    */
2214   struct CellMap {
2215     struct Cell {
CellnsGridContainerFrame::Grid::CellMap::Cell2216       Cell() : mIsOccupied(false) {}
2217       bool mIsOccupied : 1;
2218     };
2219 
FillnsGridContainerFrame::Grid::CellMap2220     void Fill(const GridArea& aGridArea) {
2221       MOZ_ASSERT(aGridArea.IsDefinite());
2222       MOZ_ASSERT(aGridArea.mRows.mStart < aGridArea.mRows.mEnd);
2223       MOZ_ASSERT(aGridArea.mCols.mStart < aGridArea.mCols.mEnd);
2224       const auto numRows = aGridArea.mRows.mEnd;
2225       const auto numCols = aGridArea.mCols.mEnd;
2226       mCells.EnsureLengthAtLeast(numRows);
2227       for (auto i = aGridArea.mRows.mStart; i < numRows; ++i) {
2228         nsTArray<Cell>& cellsInRow = mCells[i];
2229         cellsInRow.EnsureLengthAtLeast(numCols);
2230         for (auto j = aGridArea.mCols.mStart; j < numCols; ++j) {
2231           cellsInRow[j].mIsOccupied = true;
2232         }
2233       }
2234     }
2235 
IsEmptyColnsGridContainerFrame::Grid::CellMap2236     uint32_t IsEmptyCol(uint32_t aCol) const {
2237       for (auto& row : mCells) {
2238         if (aCol < row.Length() && row[aCol].mIsOccupied) {
2239           return false;
2240         }
2241       }
2242       return true;
2243     }
IsEmptyRownsGridContainerFrame::Grid::CellMap2244     uint32_t IsEmptyRow(uint32_t aRow) const {
2245       if (aRow >= mCells.Length()) {
2246         return true;
2247       }
2248       for (const Cell& cell : mCells[aRow]) {
2249         if (cell.mIsOccupied) {
2250           return false;
2251         }
2252       }
2253       return true;
2254     }
2255 #ifdef DEBUG
DumpnsGridContainerFrame::Grid::CellMap2256     void Dump() const {
2257       const size_t numRows = mCells.Length();
2258       for (size_t i = 0; i < numRows; ++i) {
2259         const nsTArray<Cell>& cellsInRow = mCells[i];
2260         const size_t numCols = cellsInRow.Length();
2261         printf("%lu:\t", (unsigned long)i + 1);
2262         for (size_t j = 0; j < numCols; ++j) {
2263           printf(cellsInRow[j].mIsOccupied ? "X " : ". ");
2264         }
2265         printf("\n");
2266       }
2267     }
2268 #endif
2269 
2270     nsTArray<nsTArray<Cell>> mCells;
2271   };
2272 
2273   /**
2274    * State for each cell in the grid.
2275    */
2276   CellMap mCellMap;
2277   /**
2278    * @see HasImplicitNamedArea.
2279    */
2280   ImplicitNamedAreas* mAreas;
2281   /**
2282    * The last column grid line (1-based) in the explicit grid.
2283    * (i.e. the number of explicit columns + 1)
2284    */
2285   uint32_t mExplicitGridColEnd;
2286   /**
2287    * The last row grid line (1-based) in the explicit grid.
2288    * (i.e. the number of explicit rows + 1)
2289    */
2290   uint32_t mExplicitGridRowEnd;
2291   // Same for the implicit grid, except these become zero-based after
2292   // resolving definite lines.
2293   uint32_t mGridColEnd;
2294   uint32_t mGridRowEnd;
2295 
2296   /**
2297    * Offsets from the start of the implicit grid to the start of the translated
2298    * explicit grid.  They are zero if there are no implicit lines before 1,1.
2299    * e.g. "grid-column: span 3 / 1" makes mExplicitGridOffsetCol = 3 and the
2300    * corresponding GridArea::mCols will be 0 / 3 in the zero-based translated
2301    * grid.
2302    */
2303   uint32_t mExplicitGridOffsetCol;
2304   uint32_t mExplicitGridOffsetRow;
2305 };
2306 
CalculateTrackSizes(const Grid & aGrid,LogicalSize & aContentBox,SizingConstraint aConstraint)2307 void nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
2308     const Grid& aGrid, LogicalSize& aContentBox, SizingConstraint aConstraint) {
2309   mCols.Initialize(mColFunctions, mGridStyle->mGridColumnGap, aGrid.mGridColEnd,
2310                    aContentBox.ISize(mWM));
2311   mRows.Initialize(mRowFunctions, mGridStyle->mGridRowGap, aGrid.mGridRowEnd,
2312                    aContentBox.BSize(mWM));
2313 
2314   mCols.CalculateSizes(*this, mGridItems, mColFunctions, aContentBox.ISize(mWM),
2315                        &GridArea::mCols, aConstraint);
2316   mCols.AlignJustifyContent(mGridStyle, mWM, aContentBox);
2317   // Column positions and sizes are now final.
2318   mCols.mCanResolveLineRangeSize = true;
2319 
2320   mRows.CalculateSizes(*this, mGridItems, mRowFunctions, aContentBox.BSize(mWM),
2321                        &GridArea::mRows, aConstraint);
2322   if (aContentBox.BSize(mWM) == NS_AUTOHEIGHT) {
2323     aContentBox.BSize(mWM) =
2324         mRows.BackComputedIntrinsicSize(mRowFunctions, mGridStyle->mGridRowGap);
2325     mRows.mGridGap = ::ResolveToDefiniteSize(mGridStyle->mGridRowGap,
2326                                              aContentBox.BSize(mWM));
2327   }
2328 }
2329 
2330 /**
2331  * (XXX share this utility function with nsFlexContainerFrame at some point)
2332  *
2333  * Helper for BuildDisplayList, to implement this special-case for grid
2334  * items from the spec:
2335  *   The painting order of grid items is exactly the same as inline blocks,
2336  *   except that [...] 'z-index' values other than 'auto' create a stacking
2337  *   context even if 'position' is 'static'.
2338  * http://dev.w3.org/csswg/css-grid/#z-order
2339  */
GetDisplayFlagsForGridItem(nsIFrame * aFrame)2340 static uint32_t GetDisplayFlagsForGridItem(nsIFrame* aFrame) {
2341   const nsStylePosition* pos = aFrame->StylePosition();
2342   if (pos->mZIndex.GetUnit() == eStyleUnit_Integer) {
2343     return nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT;
2344   }
2345   return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT;
2346 }
2347 
2348 // Align an item's margin box in its aAxis inside aCBSize.
AlignJustifySelf(uint8_t aAlignment,LogicalAxis aAxis,AlignJustifyFlags aFlags,nscoord aBaselineAdjust,nscoord aCBSize,const ReflowInput & aRI,const LogicalSize & aChildSize,LogicalPoint * aPos)2349 static void AlignJustifySelf(uint8_t aAlignment, LogicalAxis aAxis,
2350                              AlignJustifyFlags aFlags, nscoord aBaselineAdjust,
2351                              nscoord aCBSize, const ReflowInput& aRI,
2352                              const LogicalSize& aChildSize,
2353                              LogicalPoint* aPos) {
2354   MOZ_ASSERT(aAlignment != NS_STYLE_ALIGN_AUTO,
2355              "unexpected 'auto' "
2356              "computed value for normal flow grid item");
2357 
2358   // NOTE: this is the resulting frame offset (border box).
2359   nscoord offset = CSSAlignUtils::AlignJustifySelf(
2360       aAlignment, aAxis, aFlags, aBaselineAdjust, aCBSize, aRI, aChildSize);
2361 
2362   // Set the position (aPos) for the requested alignment.
2363   if (offset != 0) {
2364     WritingMode wm = aRI.GetWritingMode();
2365     nscoord& pos = aAxis == eLogicalAxisBlock ? aPos->B(wm) : aPos->I(wm);
2366     pos += MOZ_LIKELY(aFlags & AlignJustifyFlags::eSameSide) ? offset : -offset;
2367   }
2368 }
2369 
AlignSelf(const nsGridContainerFrame::GridItemInfo & aGridItem,uint8_t aAlignSelf,nscoord aCBSize,const WritingMode aCBWM,const ReflowInput & aRI,const LogicalSize & aSize,LogicalPoint * aPos)2370 static void AlignSelf(const nsGridContainerFrame::GridItemInfo& aGridItem,
2371                       uint8_t aAlignSelf, nscoord aCBSize,
2372                       const WritingMode aCBWM, const ReflowInput& aRI,
2373                       const LogicalSize& aSize, LogicalPoint* aPos) {
2374   auto alignSelf = aAlignSelf;
2375 
2376   AlignJustifyFlags flags = AlignJustifyFlags::eNoFlags;
2377   if (alignSelf & NS_STYLE_ALIGN_SAFE) {
2378     flags |= AlignJustifyFlags::eOverflowSafe;
2379   }
2380   alignSelf &= ~NS_STYLE_ALIGN_FLAG_BITS;
2381 
2382   WritingMode childWM = aRI.GetWritingMode();
2383   if (aCBWM.ParallelAxisStartsOnSameSide(eLogicalAxisBlock, childWM)) {
2384     flags |= AlignJustifyFlags::eSameSide;
2385   }
2386 
2387   // Grid's 'align-self' axis is never parallel to the container's inline axis.
2388   if (alignSelf == NS_STYLE_ALIGN_LEFT || alignSelf == NS_STYLE_ALIGN_RIGHT) {
2389     alignSelf = NS_STYLE_ALIGN_START;
2390   }
2391   if (MOZ_LIKELY(alignSelf == NS_STYLE_ALIGN_NORMAL)) {
2392     alignSelf = NS_STYLE_ALIGN_STRETCH;
2393   }
2394 
2395   nscoord baselineAdjust = 0;
2396   if (alignSelf == NS_STYLE_ALIGN_BASELINE ||
2397       alignSelf == NS_STYLE_ALIGN_LAST_BASELINE) {
2398     alignSelf = aGridItem.GetSelfBaseline(alignSelf, eLogicalAxisBlock,
2399                                           &baselineAdjust);
2400   }
2401 
2402   bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
2403   LogicalAxis axis = isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock;
2404   AlignJustifySelf(alignSelf, axis, flags, baselineAdjust, aCBSize, aRI, aSize,
2405                    aPos);
2406 }
2407 
JustifySelf(const nsGridContainerFrame::GridItemInfo & aGridItem,uint8_t aJustifySelf,nscoord aCBSize,const WritingMode aCBWM,const ReflowInput & aRI,const LogicalSize & aSize,LogicalPoint * aPos)2408 static void JustifySelf(const nsGridContainerFrame::GridItemInfo& aGridItem,
2409                         uint8_t aJustifySelf, nscoord aCBSize,
2410                         const WritingMode aCBWM, const ReflowInput& aRI,
2411                         const LogicalSize& aSize, LogicalPoint* aPos) {
2412   auto justifySelf = aJustifySelf;
2413 
2414   AlignJustifyFlags flags = AlignJustifyFlags::eNoFlags;
2415   if (justifySelf & NS_STYLE_JUSTIFY_SAFE) {
2416     flags |= AlignJustifyFlags::eOverflowSafe;
2417   }
2418   justifySelf &= ~NS_STYLE_JUSTIFY_FLAG_BITS;
2419 
2420   WritingMode childWM = aRI.GetWritingMode();
2421   if (aCBWM.ParallelAxisStartsOnSameSide(eLogicalAxisInline, childWM)) {
2422     flags |= AlignJustifyFlags::eSameSide;
2423   }
2424 
2425   if (MOZ_LIKELY(justifySelf == NS_STYLE_ALIGN_NORMAL)) {
2426     justifySelf = NS_STYLE_ALIGN_STRETCH;
2427   }
2428 
2429   nscoord baselineAdjust = 0;
2430   // Grid's 'justify-self' axis is always parallel to the container's inline
2431   // axis, so justify-self:left|right always applies.
2432   switch (justifySelf) {
2433     case NS_STYLE_JUSTIFY_LEFT:
2434       justifySelf =
2435           aCBWM.IsBidiLTR() ? NS_STYLE_JUSTIFY_START : NS_STYLE_JUSTIFY_END;
2436       break;
2437     case NS_STYLE_JUSTIFY_RIGHT:
2438       justifySelf =
2439           aCBWM.IsBidiLTR() ? NS_STYLE_JUSTIFY_END : NS_STYLE_JUSTIFY_START;
2440       break;
2441     case NS_STYLE_JUSTIFY_BASELINE:
2442     case NS_STYLE_JUSTIFY_LAST_BASELINE:
2443       justifySelf = aGridItem.GetSelfBaseline(justifySelf, eLogicalAxisInline,
2444                                               &baselineAdjust);
2445       break;
2446   }
2447 
2448   bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
2449   LogicalAxis axis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
2450   AlignJustifySelf(justifySelf, axis, flags, baselineAdjust, aCBSize, aRI,
2451                    aSize, aPos);
2452 }
2453 
GetAlignJustifyValue(uint16_t aAlignment,const WritingMode aWM,const bool aIsAlign,bool * aOverflowSafe)2454 static uint16_t GetAlignJustifyValue(uint16_t aAlignment, const WritingMode aWM,
2455                                      const bool aIsAlign, bool* aOverflowSafe) {
2456   *aOverflowSafe = aAlignment & NS_STYLE_ALIGN_SAFE;
2457   aAlignment &= (NS_STYLE_ALIGN_ALL_BITS & ~NS_STYLE_ALIGN_FLAG_BITS);
2458 
2459   // Map some alignment values to 'start' / 'end'.
2460   switch (aAlignment) {
2461     case NS_STYLE_ALIGN_LEFT:
2462     case NS_STYLE_ALIGN_RIGHT: {
2463       if (aIsAlign) {
2464         // Grid's 'align-content' axis is never parallel to the inline axis.
2465         return NS_STYLE_ALIGN_START;
2466       }
2467       bool isStart = aWM.IsBidiLTR() == (aAlignment == NS_STYLE_ALIGN_LEFT);
2468       return isStart ? NS_STYLE_ALIGN_START : NS_STYLE_ALIGN_END;
2469     }
2470     case NS_STYLE_ALIGN_FLEX_START:  // same as 'start' for Grid
2471       return NS_STYLE_ALIGN_START;
2472     case NS_STYLE_ALIGN_FLEX_END:  // same as 'end' for Grid
2473       return NS_STYLE_ALIGN_END;
2474   }
2475   return aAlignment;
2476 }
2477 
GetAlignJustifyFallbackIfAny(uint16_t aAlignment,const WritingMode aWM,const bool aIsAlign,bool * aOverflowSafe)2478 static uint16_t GetAlignJustifyFallbackIfAny(uint16_t aAlignment,
2479                                              const WritingMode aWM,
2480                                              const bool aIsAlign,
2481                                              bool* aOverflowSafe) {
2482   uint16_t fallback = aAlignment >> NS_STYLE_ALIGN_ALL_SHIFT;
2483   if (fallback) {
2484     return GetAlignJustifyValue(fallback, aWM, aIsAlign, aOverflowSafe);
2485   }
2486   // https://drafts.csswg.org/css-align-3/#fallback-alignment
2487   switch (aAlignment) {
2488     case NS_STYLE_ALIGN_STRETCH:
2489     case NS_STYLE_ALIGN_SPACE_BETWEEN:
2490       return NS_STYLE_ALIGN_START;
2491     case NS_STYLE_ALIGN_SPACE_AROUND:
2492     case NS_STYLE_ALIGN_SPACE_EVENLY:
2493       return NS_STYLE_ALIGN_CENTER;
2494   }
2495   return 0;
2496 }
2497 
2498 //----------------------------------------------------------------------
2499 
2500 // Frame class boilerplate
2501 // =======================
2502 
2503 NS_QUERYFRAME_HEAD(nsGridContainerFrame)
NS_QUERYFRAME_ENTRY(nsGridContainerFrame)2504 NS_QUERYFRAME_ENTRY(nsGridContainerFrame)
2505 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
2506 
2507 NS_IMPL_FRAMEARENA_HELPERS(nsGridContainerFrame)
2508 
2509 nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell,
2510                                            nsStyleContext* aContext) {
2511   return new (aPresShell) nsGridContainerFrame(aContext);
2512 }
2513 
2514 //----------------------------------------------------------------------
2515 
2516 // nsGridContainerFrame Method Implementations
2517 // ===========================================
2518 
GridItemCB(nsIFrame * aChild)2519 /*static*/ const nsRect& nsGridContainerFrame::GridItemCB(nsIFrame* aChild) {
2520   MOZ_ASSERT((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
2521              aChild->IsAbsolutelyPositioned());
2522   nsRect* cb = aChild->GetProperty(GridItemContainingBlockRect());
2523   MOZ_ASSERT(cb,
2524              "this method must only be called on grid items, and the grid "
2525              "container should've reflowed this item by now and set up cb");
2526   return *cb;
2527 }
2528 
AddImplicitNamedAreas(const nsTArray<nsTArray<nsString>> & aLineNameLists)2529 void nsGridContainerFrame::AddImplicitNamedAreas(
2530     const nsTArray<nsTArray<nsString>>& aLineNameLists) {
2531   // http://dev.w3.org/csswg/css-grid/#implicit-named-areas
2532   // Note: recording these names for fast lookup later is just an optimization.
2533   const uint32_t len =
2534       std::min(aLineNameLists.Length(), size_t(nsStyleGridLine::kMaxLine));
2535   nsTHashtable<nsStringHashKey> currentStarts;
2536   ImplicitNamedAreas* areas = GetImplicitNamedAreas();
2537   for (uint32_t i = 0; i < len; ++i) {
2538     for (const nsString& name : aLineNameLists[i]) {
2539       uint32_t indexOfSuffix;
2540       if (Grid::IsNameWithStartSuffix(name, &indexOfSuffix) ||
2541           Grid::IsNameWithEndSuffix(name, &indexOfSuffix)) {
2542         // Extract the name that was found earlier.
2543         nsDependentSubstring areaName(name, 0, indexOfSuffix);
2544 
2545         // Lazily create the ImplicitNamedAreas.
2546         if (!areas) {
2547           areas = new ImplicitNamedAreas;
2548           SetProperty(ImplicitNamedAreasProperty(), areas);
2549         }
2550 
2551         mozilla::css::GridNamedArea area;
2552         if (!areas->Get(areaName, &area)) {
2553           // Not found, so prep the newly-seen area with a name and empty
2554           // boundary information, which will get filled in later.
2555           area.mName = areaName;
2556           area.mRowStart = 0;
2557           area.mRowEnd = 0;
2558           area.mColumnStart = 0;
2559           area.mColumnEnd = 0;
2560 
2561           areas->Put(areaName, area);
2562         }
2563       }
2564     }
2565   }
2566 }
2567 
InitImplicitNamedAreas(const nsStylePosition * aStyle)2568 void nsGridContainerFrame::InitImplicitNamedAreas(
2569     const nsStylePosition* aStyle) {
2570   ImplicitNamedAreas* areas = GetImplicitNamedAreas();
2571   if (areas) {
2572     // Clear it, but reuse the hashtable itself for now.  We'll remove it
2573     // below if it isn't needed anymore.
2574     areas->Clear();
2575   }
2576   AddImplicitNamedAreas(aStyle->GridTemplateColumns().mLineNameLists);
2577   AddImplicitNamedAreas(aStyle->GridTemplateRows().mLineNameLists);
2578   if (areas && areas->Count() == 0) {
2579     DeleteProperty(ImplicitNamedAreasProperty());
2580   }
2581 }
2582 
ResolveLine(const nsStyleGridLine & aLine,int32_t aNth,uint32_t aFromIndex,const LineNameMap & aNameMap,uint32_t GridNamedArea::* aAreaStart,uint32_t GridNamedArea::* aAreaEnd,uint32_t aExplicitGridEnd,LineRangeSide aSide,const nsStylePosition * aStyle)2583 int32_t nsGridContainerFrame::Grid::ResolveLine(
2584     const nsStyleGridLine& aLine, int32_t aNth, uint32_t aFromIndex,
2585     const LineNameMap& aNameMap, uint32_t GridNamedArea::*aAreaStart,
2586     uint32_t GridNamedArea::*aAreaEnd, uint32_t aExplicitGridEnd,
2587     LineRangeSide aSide, const nsStylePosition* aStyle) {
2588   MOZ_ASSERT(!aLine.IsAuto());
2589   int32_t line = 0;
2590   if (aLine.mLineName.IsEmpty()) {
2591     MOZ_ASSERT(aNth != 0, "css-grid 9.2: <integer> must not be zero.");
2592     line = int32_t(aFromIndex) + aNth;
2593   } else {
2594     if (aNth == 0) {
2595       // <integer> was omitted; treat it as 1.
2596       aNth = 1;
2597     }
2598     bool isNameOnly = !aLine.mHasSpan && aLine.mInteger == 0;
2599     if (isNameOnly) {
2600       const GridNamedArea* area = FindNamedArea(aLine.mLineName, aStyle);
2601       if (area || HasImplicitNamedArea(aLine.mLineName)) {
2602         // The given name is a named area - look for explicit lines named
2603         // <name>-start/-end depending on which side we're resolving.
2604         // http://dev.w3.org/csswg/css-grid/#grid-placement-slot
2605         uint32_t implicitLine = 0;
2606         nsAutoString lineName(aLine.mLineName);
2607         if (aSide == eLineRangeSideStart) {
2608           lineName.AppendLiteral("-start");
2609           implicitLine = area ? area->*aAreaStart : 0;
2610         } else {
2611           lineName.AppendLiteral("-end");
2612           implicitLine = area ? area->*aAreaEnd : 0;
2613         }
2614         line =
2615             aNameMap.FindNamedLine(lineName, &aNth, aFromIndex, implicitLine);
2616       }
2617     }
2618 
2619     if (line == 0) {
2620       // If mLineName ends in -start/-end, try the prefix as a named area.
2621       uint32_t implicitLine = 0;
2622       uint32_t index;
2623       auto GridNamedArea::*areaEdge = aAreaStart;
2624       bool found = IsNameWithStartSuffix(aLine.mLineName, &index);
2625       if (!found) {
2626         found = IsNameWithEndSuffix(aLine.mLineName, &index);
2627         areaEdge = aAreaEnd;
2628       }
2629       if (found) {
2630         const GridNamedArea* area = FindNamedArea(
2631             nsDependentSubstring(aLine.mLineName, 0, index), aStyle);
2632         if (area) {
2633           implicitLine = area->*areaEdge;
2634         }
2635       }
2636       line = aNameMap.FindNamedLine(aLine.mLineName, &aNth, aFromIndex,
2637                                     implicitLine);
2638     }
2639 
2640     if (line == 0) {
2641       MOZ_ASSERT(aNth != 0, "we found all N named lines but 'line' is zero!");
2642       int32_t edgeLine;
2643       if (aLine.mHasSpan) {
2644         // http://dev.w3.org/csswg/css-grid/#grid-placement-span-int
2645         // 'span <custom-ident> N'
2646         edgeLine = aSide == eLineRangeSideStart ? 1 : aExplicitGridEnd;
2647       } else {
2648         // http://dev.w3.org/csswg/css-grid/#grid-placement-int
2649         // '<custom-ident> N'
2650         edgeLine = aNth < 0 ? 1 : aExplicitGridEnd;
2651       }
2652       // "If not enough lines with that name exist, all lines in the implicit
2653       // grid are assumed to have that name..."
2654       line = edgeLine + aNth;
2655     }
2656   }
2657   return clamped(line, nsStyleGridLine::kMinLine, nsStyleGridLine::kMaxLine);
2658 }
2659 
2660 nsGridContainerFrame::Grid::LinePair
ResolveLineRangeHelper(const nsStyleGridLine & aStart,const nsStyleGridLine & aEnd,const LineNameMap & aNameMap,uint32_t GridNamedArea::* aAreaStart,uint32_t GridNamedArea::* aAreaEnd,uint32_t aExplicitGridEnd,const nsStylePosition * aStyle)2661 nsGridContainerFrame::Grid::ResolveLineRangeHelper(
2662     const nsStyleGridLine& aStart, const nsStyleGridLine& aEnd,
2663     const LineNameMap& aNameMap, uint32_t GridNamedArea::*aAreaStart,
2664     uint32_t GridNamedArea::*aAreaEnd, uint32_t aExplicitGridEnd,
2665     const nsStylePosition* aStyle) {
2666   MOZ_ASSERT(int32_t(nsGridContainerFrame::kAutoLine) >
2667              nsStyleGridLine::kMaxLine);
2668 
2669   if (aStart.mHasSpan) {
2670     if (aEnd.mHasSpan || aEnd.IsAuto()) {
2671       // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
2672       if (aStart.mLineName.IsEmpty()) {
2673         // span <integer> / span *
2674         // span <integer> / auto
2675         return LinePair(kAutoLine, aStart.mInteger);
2676       }
2677       // span <custom-ident> / span *
2678       // span <custom-ident> / auto
2679       return LinePair(kAutoLine, 1);  // XXX subgrid explicit size instead of 1?
2680     }
2681 
2682     uint32_t from = aEnd.mInteger < 0 ? aExplicitGridEnd + 1 : 0;
2683     auto end =
2684         ResolveLine(aEnd, aEnd.mInteger, from, aNameMap, aAreaStart, aAreaEnd,
2685                     aExplicitGridEnd, eLineRangeSideEnd, aStyle);
2686     int32_t span = aStart.mInteger == 0 ? 1 : aStart.mInteger;
2687     if (end <= 1) {
2688       // The end is at or before the first explicit line, thus all lines before
2689       // it match <custom-ident> since they're implicit.
2690       int32_t start = std::max(end - span, nsStyleGridLine::kMinLine);
2691       return LinePair(start, end);
2692     }
2693     auto start = ResolveLine(aStart, -span, end, aNameMap, aAreaStart, aAreaEnd,
2694                              aExplicitGridEnd, eLineRangeSideStart, aStyle);
2695     return LinePair(start, end);
2696   }
2697 
2698   int32_t start = kAutoLine;
2699   if (aStart.IsAuto()) {
2700     if (aEnd.IsAuto()) {
2701       // auto / auto
2702       return LinePair(start, 1);  // XXX subgrid explicit size instead of 1?
2703     }
2704     if (aEnd.mHasSpan) {
2705       if (aEnd.mLineName.IsEmpty()) {
2706         // auto / span <integer>
2707         MOZ_ASSERT(aEnd.mInteger != 0);
2708         return LinePair(start, aEnd.mInteger);
2709       }
2710       // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
2711       // auto / span <custom-ident>
2712       return LinePair(start, 1);  // XXX subgrid explicit size instead of 1?
2713     }
2714   } else {
2715     uint32_t from = aStart.mInteger < 0 ? aExplicitGridEnd + 1 : 0;
2716     start =
2717         ResolveLine(aStart, aStart.mInteger, from, aNameMap, aAreaStart,
2718                     aAreaEnd, aExplicitGridEnd, eLineRangeSideStart, aStyle);
2719     if (aEnd.IsAuto()) {
2720       // A "definite line / auto" should resolve the auto to 'span 1'.
2721       // The error handling in ResolveLineRange will make that happen and also
2722       // clamp the end line correctly if we return "start / start".
2723       return LinePair(start, start);
2724     }
2725   }
2726 
2727   uint32_t from;
2728   int32_t nth = aEnd.mInteger == 0 ? 1 : aEnd.mInteger;
2729   if (aEnd.mHasSpan) {
2730     if (MOZ_UNLIKELY(start < 0)) {
2731       if (aEnd.mLineName.IsEmpty()) {
2732         return LinePair(start, start + nth);
2733       }
2734       from = 0;
2735     } else {
2736       if (start >= int32_t(aExplicitGridEnd)) {
2737         // The start is at or after the last explicit line, thus all lines
2738         // after it match <custom-ident> since they're implicit.
2739         return LinePair(start,
2740                         std::min(start + nth, nsStyleGridLine::kMaxLine));
2741       }
2742       from = start;
2743     }
2744   } else {
2745     from = aEnd.mInteger < 0 ? aExplicitGridEnd + 1 : 0;
2746   }
2747   auto end = ResolveLine(aEnd, nth, from, aNameMap, aAreaStart, aAreaEnd,
2748                          aExplicitGridEnd, eLineRangeSideEnd, aStyle);
2749   if (start == int32_t(kAutoLine)) {
2750     // auto / definite line
2751     start = std::max(nsStyleGridLine::kMinLine, end - 1);
2752   }
2753   return LinePair(start, end);
2754 }
2755 
ResolveLineRange(const nsStyleGridLine & aStart,const nsStyleGridLine & aEnd,const LineNameMap & aNameMap,uint32_t GridNamedArea::* aAreaStart,uint32_t GridNamedArea::* aAreaEnd,uint32_t aExplicitGridEnd,const nsStylePosition * aStyle)2756 nsGridContainerFrame::LineRange nsGridContainerFrame::Grid::ResolveLineRange(
2757     const nsStyleGridLine& aStart, const nsStyleGridLine& aEnd,
2758     const LineNameMap& aNameMap, uint32_t GridNamedArea::*aAreaStart,
2759     uint32_t GridNamedArea::*aAreaEnd, uint32_t aExplicitGridEnd,
2760     const nsStylePosition* aStyle) {
2761   LinePair r = ResolveLineRangeHelper(aStart, aEnd, aNameMap, aAreaStart,
2762                                       aAreaEnd, aExplicitGridEnd, aStyle);
2763   MOZ_ASSERT(r.second != int32_t(kAutoLine));
2764 
2765   if (r.first == int32_t(kAutoLine)) {
2766     // r.second is a span, clamp it to kMaxLine - 1 so that the returned
2767     // range has a HypotheticalEnd <= kMaxLine.
2768     // http://dev.w3.org/csswg/css-grid/#overlarge-grids
2769     r.second = std::min(r.second, nsStyleGridLine::kMaxLine - 1);
2770   } else {
2771     // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
2772     if (r.first > r.second) {
2773       Swap(r.first, r.second);
2774     } else if (r.first == r.second) {
2775       if (MOZ_UNLIKELY(r.first == nsStyleGridLine::kMaxLine)) {
2776         r.first = nsStyleGridLine::kMaxLine - 1;
2777       }
2778       r.second = r.first + 1;  // XXX subgrid explicit size instead of 1?
2779     }
2780   }
2781   return LineRange(r.first, r.second);
2782 }
2783 
PlaceDefinite(nsIFrame * aChild,const LineNameMap & aColLineNameMap,const LineNameMap & aRowLineNameMap,const nsStylePosition * aStyle)2784 nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceDefinite(
2785     nsIFrame* aChild, const LineNameMap& aColLineNameMap,
2786     const LineNameMap& aRowLineNameMap, const nsStylePosition* aStyle) {
2787   const nsStylePosition* itemStyle = aChild->StylePosition();
2788   return GridArea(
2789       ResolveLineRange(itemStyle->mGridColumnStart, itemStyle->mGridColumnEnd,
2790                        aColLineNameMap, &GridNamedArea::mColumnStart,
2791                        &GridNamedArea::mColumnEnd, mExplicitGridColEnd, aStyle),
2792       ResolveLineRange(itemStyle->mGridRowStart, itemStyle->mGridRowEnd,
2793                        aRowLineNameMap, &GridNamedArea::mRowStart,
2794                        &GridNamedArea::mRowEnd, mExplicitGridRowEnd, aStyle));
2795 }
2796 
2797 nsGridContainerFrame::LineRange
ResolveAbsPosLineRange(const nsStyleGridLine & aStart,const nsStyleGridLine & aEnd,const LineNameMap & aNameMap,uint32_t GridNamedArea::* aAreaStart,uint32_t GridNamedArea::* aAreaEnd,uint32_t aExplicitGridEnd,int32_t aGridStart,int32_t aGridEnd,const nsStylePosition * aStyle)2798 nsGridContainerFrame::Grid::ResolveAbsPosLineRange(
2799     const nsStyleGridLine& aStart, const nsStyleGridLine& aEnd,
2800     const LineNameMap& aNameMap, uint32_t GridNamedArea::*aAreaStart,
2801     uint32_t GridNamedArea::*aAreaEnd, uint32_t aExplicitGridEnd,
2802     int32_t aGridStart, int32_t aGridEnd, const nsStylePosition* aStyle) {
2803   if (aStart.IsAuto()) {
2804     if (aEnd.IsAuto()) {
2805       return LineRange(kAutoLine, kAutoLine);
2806     }
2807     uint32_t from = aEnd.mInteger < 0 ? aExplicitGridEnd + 1 : 0;
2808     int32_t end =
2809         ResolveLine(aEnd, aEnd.mInteger, from, aNameMap, aAreaStart, aAreaEnd,
2810                     aExplicitGridEnd, eLineRangeSideEnd, aStyle);
2811     if (aEnd.mHasSpan) {
2812       ++end;
2813     }
2814     // A line outside the existing grid is treated as 'auto' for abs.pos (10.1).
2815     end = AutoIfOutside(end, aGridStart, aGridEnd);
2816     return LineRange(kAutoLine, end);
2817   }
2818 
2819   if (aEnd.IsAuto()) {
2820     uint32_t from = aStart.mInteger < 0 ? aExplicitGridEnd + 1 : 0;
2821     int32_t start =
2822         ResolveLine(aStart, aStart.mInteger, from, aNameMap, aAreaStart,
2823                     aAreaEnd, aExplicitGridEnd, eLineRangeSideStart, aStyle);
2824     if (aStart.mHasSpan) {
2825       start = std::max(aGridEnd - start, aGridStart);
2826     }
2827     start = AutoIfOutside(start, aGridStart, aGridEnd);
2828     return LineRange(start, kAutoLine);
2829   }
2830 
2831   LineRange r = ResolveLineRange(aStart, aEnd, aNameMap, aAreaStart, aAreaEnd,
2832                                  aExplicitGridEnd, aStyle);
2833   if (r.IsAuto()) {
2834     MOZ_ASSERT(aStart.mHasSpan && aEnd.mHasSpan,
2835                "span / span is the only case "
2836                "leading to IsAuto here -- we dealt with the other cases above");
2837     // The second span was ignored per 9.2.1.  For abs.pos., 10.1 says that this
2838     // case should result in "auto / auto" unlike normal flow grid items.
2839     return LineRange(kAutoLine, kAutoLine);
2840   }
2841 
2842   return LineRange(AutoIfOutside(r.mUntranslatedStart, aGridStart, aGridEnd),
2843                    AutoIfOutside(r.mUntranslatedEnd, aGridStart, aGridEnd));
2844 }
2845 
PlaceAbsPos(nsIFrame * aChild,const LineNameMap & aColLineNameMap,const LineNameMap & aRowLineNameMap,const nsStylePosition * aStyle)2846 nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceAbsPos(
2847     nsIFrame* aChild, const LineNameMap& aColLineNameMap,
2848     const LineNameMap& aRowLineNameMap, const nsStylePosition* aStyle) {
2849   const nsStylePosition* itemStyle = aChild->StylePosition();
2850   int32_t gridColStart = 1 - mExplicitGridOffsetCol;
2851   int32_t gridRowStart = 1 - mExplicitGridOffsetRow;
2852   return GridArea(
2853       ResolveAbsPosLineRange(itemStyle->mGridColumnStart,
2854                              itemStyle->mGridColumnEnd, aColLineNameMap,
2855                              &GridNamedArea::mColumnStart,
2856                              &GridNamedArea::mColumnEnd, mExplicitGridColEnd,
2857                              gridColStart, mGridColEnd, aStyle),
2858       ResolveAbsPosLineRange(itemStyle->mGridRowStart, itemStyle->mGridRowEnd,
2859                              aRowLineNameMap, &GridNamedArea::mRowStart,
2860                              &GridNamedArea::mRowEnd, mExplicitGridRowEnd,
2861                              gridRowStart, mGridRowEnd, aStyle));
2862 }
2863 
FindAutoCol(uint32_t aStartCol,uint32_t aLockedRow,const GridArea * aArea) const2864 uint32_t nsGridContainerFrame::Grid::FindAutoCol(uint32_t aStartCol,
2865                                                  uint32_t aLockedRow,
2866                                                  const GridArea* aArea) const {
2867   const uint32_t extent = aArea->mCols.Extent();
2868   const uint32_t iStart = aLockedRow;
2869   const uint32_t iEnd = iStart + aArea->mRows.Extent();
2870   uint32_t candidate = aStartCol;
2871   for (uint32_t i = iStart; i < iEnd;) {
2872     if (i >= mCellMap.mCells.Length()) {
2873       break;
2874     }
2875     const nsTArray<CellMap::Cell>& cellsInRow = mCellMap.mCells[i];
2876     const uint32_t len = cellsInRow.Length();
2877     const uint32_t lastCandidate = candidate;
2878     // Find the first gap in the current row that's at least 'extent' wide.
2879     // ('gap' tracks how wide the current column gap is.)
2880     for (uint32_t j = candidate, gap = 0; j < len && gap < extent; ++j) {
2881       if (!cellsInRow[j].mIsOccupied) {
2882         ++gap;
2883         continue;
2884       }
2885       candidate = j + 1;
2886       gap = 0;
2887     }
2888     if (lastCandidate < candidate && i != iStart) {
2889       // Couldn't fit 'extent' tracks at 'lastCandidate' here so we must
2890       // restart from the beginning with the new 'candidate'.
2891       i = iStart;
2892     } else {
2893       ++i;
2894     }
2895   }
2896   return candidate;
2897 }
2898 
PlaceAutoCol(uint32_t aStartCol,GridArea * aArea) const2899 void nsGridContainerFrame::Grid::PlaceAutoCol(uint32_t aStartCol,
2900                                               GridArea* aArea) const {
2901   MOZ_ASSERT(aArea->mRows.IsDefinite() && aArea->mCols.IsAuto());
2902   uint32_t col = FindAutoCol(aStartCol, aArea->mRows.mStart, aArea);
2903   aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol);
2904   MOZ_ASSERT(aArea->IsDefinite());
2905 }
2906 
FindAutoRow(uint32_t aLockedCol,uint32_t aStartRow,const GridArea * aArea) const2907 uint32_t nsGridContainerFrame::Grid::FindAutoRow(uint32_t aLockedCol,
2908                                                  uint32_t aStartRow,
2909                                                  const GridArea* aArea) const {
2910   const uint32_t extent = aArea->mRows.Extent();
2911   const uint32_t jStart = aLockedCol;
2912   const uint32_t jEnd = jStart + aArea->mCols.Extent();
2913   const uint32_t iEnd = mCellMap.mCells.Length();
2914   uint32_t candidate = aStartRow;
2915   // Find the first gap in the rows that's at least 'extent' tall.
2916   // ('gap' tracks how tall the current row gap is.)
2917   for (uint32_t i = candidate, gap = 0; i < iEnd && gap < extent; ++i) {
2918     ++gap;  // tentative, but we may reset it below if a column is occupied
2919     const nsTArray<CellMap::Cell>& cellsInRow = mCellMap.mCells[i];
2920     const uint32_t clampedJEnd = std::min<uint32_t>(jEnd, cellsInRow.Length());
2921     // Check if the current row is unoccupied from jStart to jEnd.
2922     for (uint32_t j = jStart; j < clampedJEnd; ++j) {
2923       if (cellsInRow[j].mIsOccupied) {
2924         // Couldn't fit 'extent' rows at 'candidate' here; we hit something
2925         // at row 'i'.  So, try the row after 'i' as our next candidate.
2926         candidate = i + 1;
2927         gap = 0;
2928         break;
2929       }
2930     }
2931   }
2932   return candidate;
2933 }
2934 
PlaceAutoRow(uint32_t aStartRow,GridArea * aArea) const2935 void nsGridContainerFrame::Grid::PlaceAutoRow(uint32_t aStartRow,
2936                                               GridArea* aArea) const {
2937   MOZ_ASSERT(aArea->mCols.IsDefinite() && aArea->mRows.IsAuto());
2938   uint32_t row = FindAutoRow(aArea->mCols.mStart, aStartRow, aArea);
2939   aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow);
2940   MOZ_ASSERT(aArea->IsDefinite());
2941 }
2942 
PlaceAutoAutoInRowOrder(uint32_t aStartCol,uint32_t aStartRow,GridArea * aArea) const2943 void nsGridContainerFrame::Grid::PlaceAutoAutoInRowOrder(
2944     uint32_t aStartCol, uint32_t aStartRow, GridArea* aArea) const {
2945   MOZ_ASSERT(aArea->mCols.IsAuto() && aArea->mRows.IsAuto());
2946   const uint32_t colExtent = aArea->mCols.Extent();
2947   const uint32_t gridRowEnd = mGridRowEnd;
2948   const uint32_t gridColEnd = mGridColEnd;
2949   uint32_t col = aStartCol;
2950   uint32_t row = aStartRow;
2951   for (; row < gridRowEnd; ++row) {
2952     col = FindAutoCol(col, row, aArea);
2953     if (col + colExtent <= gridColEnd) {
2954       break;
2955     }
2956     col = 0;
2957   }
2958   MOZ_ASSERT(row < gridRowEnd || col == 0,
2959              "expected column 0 for placing in a new row");
2960   aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol);
2961   aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow);
2962   MOZ_ASSERT(aArea->IsDefinite());
2963 }
2964 
PlaceAutoAutoInColOrder(uint32_t aStartCol,uint32_t aStartRow,GridArea * aArea) const2965 void nsGridContainerFrame::Grid::PlaceAutoAutoInColOrder(
2966     uint32_t aStartCol, uint32_t aStartRow, GridArea* aArea) const {
2967   MOZ_ASSERT(aArea->mCols.IsAuto() && aArea->mRows.IsAuto());
2968   const uint32_t rowExtent = aArea->mRows.Extent();
2969   const uint32_t gridRowEnd = mGridRowEnd;
2970   const uint32_t gridColEnd = mGridColEnd;
2971   uint32_t col = aStartCol;
2972   uint32_t row = aStartRow;
2973   for (; col < gridColEnd; ++col) {
2974     row = FindAutoRow(col, row, aArea);
2975     if (row + rowExtent <= gridRowEnd) {
2976       break;
2977     }
2978     row = 0;
2979   }
2980   MOZ_ASSERT(col < gridColEnd || row == 0,
2981              "expected row 0 for placing in a new column");
2982   aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol);
2983   aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow);
2984   MOZ_ASSERT(aArea->IsDefinite());
2985 }
2986 
PlaceGridItems(GridReflowInput & aState,const LogicalSize & aComputedMinSize,const LogicalSize & aComputedSize,const LogicalSize & aComputedMaxSize)2987 void nsGridContainerFrame::Grid::PlaceGridItems(
2988     GridReflowInput& aState, const LogicalSize& aComputedMinSize,
2989     const LogicalSize& aComputedSize, const LogicalSize& aComputedMaxSize) {
2990   mAreas = aState.mFrame->GetImplicitNamedAreas();
2991   const nsStylePosition* const gridStyle = aState.mGridStyle;
2992   MOZ_ASSERT(mCellMap.mCells.IsEmpty(), "unexpected entries in cell map");
2993 
2994   // http://dev.w3.org/csswg/css-grid/#grid-definition
2995   // Initialize the end lines of the Explicit Grid (mExplicitGridCol[Row]End).
2996   // This is determined by the larger of the number of rows/columns defined
2997   // by 'grid-template-areas' and the 'grid-template-rows'/'-columns', plus one.
2998   // Also initialize the Implicit Grid (mGridCol[Row]End) to the same values.
2999   // Note that this is for a grid with a 1,1 origin.  We'll change that
3000   // to a 0,0 based grid after placing definite lines.
3001   auto areas = gridStyle->mGridTemplateAreas.get();
3002   uint32_t numRepeatCols = aState.mColFunctions.InitRepeatTracks(
3003       gridStyle->mGridColumnGap, aComputedMinSize.ISize(aState.mWM),
3004       aComputedSize.ISize(aState.mWM), aComputedMaxSize.ISize(aState.mWM));
3005   mGridColEnd = mExplicitGridColEnd =
3006       aState.mColFunctions.ComputeExplicitGridEnd(areas ? areas->mNColumns + 1
3007                                                         : 1);
3008   LineNameMap colLineNameMap(gridStyle->GridTemplateColumns(), numRepeatCols);
3009 
3010   uint32_t numRepeatRows = aState.mRowFunctions.InitRepeatTracks(
3011       gridStyle->mGridRowGap, aComputedMinSize.BSize(aState.mWM),
3012       aComputedSize.BSize(aState.mWM), aComputedMaxSize.BSize(aState.mWM));
3013   mGridRowEnd = mExplicitGridRowEnd =
3014       aState.mRowFunctions.ComputeExplicitGridEnd(areas ? areas->NRows() + 1
3015                                                         : 1);
3016   LineNameMap rowLineNameMap(gridStyle->GridTemplateRows(), numRepeatRows);
3017 
3018   // http://dev.w3.org/csswg/css-grid/#line-placement
3019   // Resolve definite positions per spec chap 9.2.
3020   int32_t minCol = 1;
3021   int32_t minRow = 1;
3022   aState.mGridItems.ClearAndRetainStorage();
3023   aState.mIter.Reset();
3024   for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
3025     nsIFrame* child = *aState.mIter;
3026     GridItemInfo* info = aState.mGridItems.AppendElement(GridItemInfo(
3027         child,
3028         PlaceDefinite(child, colLineNameMap, rowLineNameMap, gridStyle)));
3029     MOZ_ASSERT(aState.mIter.ItemIndex() == aState.mGridItems.Length() - 1,
3030                "ItemIndex() is broken");
3031     GridArea& area = info->mArea;
3032     if (area.mCols.IsDefinite()) {
3033       minCol = std::min(minCol, area.mCols.mUntranslatedStart);
3034     }
3035     if (area.mRows.IsDefinite()) {
3036       minRow = std::min(minRow, area.mRows.mUntranslatedStart);
3037     }
3038   }
3039 
3040   // Translate the whole grid so that the top-/left-most area is at 0,0.
3041   mExplicitGridOffsetCol = 1 - minCol;  // minCol/Row is always <= 1, see above
3042   mExplicitGridOffsetRow = 1 - minRow;
3043   aState.mColFunctions.mExplicitGridOffset = mExplicitGridOffsetCol;
3044   aState.mRowFunctions.mExplicitGridOffset = mExplicitGridOffsetRow;
3045   const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
3046   const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
3047   mGridColEnd += offsetToColZero;
3048   mGridRowEnd += offsetToRowZero;
3049   aState.mIter.Reset();
3050   for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
3051     GridArea& area = aState.mGridItems[aState.mIter.ItemIndex()].mArea;
3052     if (area.mCols.IsDefinite()) {
3053       area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
3054       area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
3055     }
3056     if (area.mRows.IsDefinite()) {
3057       area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
3058       area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
3059     }
3060     if (area.IsDefinite()) {
3061       mCellMap.Fill(area);
3062       InflateGridFor(area);
3063     }
3064   }
3065 
3066   // http://dev.w3.org/csswg/css-grid/#auto-placement-algo
3067   // Step 1, place 'auto' items that have one definite position -
3068   // definite row (column) for grid-auto-flow:row (column).
3069   auto flowStyle = gridStyle->mGridAutoFlow;
3070   const bool isRowOrder = (flowStyle & NS_STYLE_GRID_AUTO_FLOW_ROW);
3071   const bool isSparse = !(flowStyle & NS_STYLE_GRID_AUTO_FLOW_DENSE);
3072   // We need 1 cursor per row (or column) if placement is sparse.
3073   {
3074     Maybe<nsDataHashtable<nsUint32HashKey, uint32_t>> cursors;
3075     if (isSparse) {
3076       cursors.emplace();
3077     }
3078     auto placeAutoMinorFunc =
3079         isRowOrder ? &Grid::PlaceAutoCol : &Grid::PlaceAutoRow;
3080     aState.mIter.Reset();
3081     for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
3082       GridArea& area = aState.mGridItems[aState.mIter.ItemIndex()].mArea;
3083       LineRange& major = isRowOrder ? area.mRows : area.mCols;
3084       LineRange& minor = isRowOrder ? area.mCols : area.mRows;
3085       if (major.IsDefinite() && minor.IsAuto()) {
3086         // Items with 'auto' in the minor dimension only.
3087         uint32_t cursor = 0;
3088         if (isSparse) {
3089           cursors->Get(major.mStart, &cursor);
3090         }
3091         (this->*placeAutoMinorFunc)(cursor, &area);
3092         mCellMap.Fill(area);
3093         if (isSparse) {
3094           cursors->Put(major.mStart, minor.mEnd);
3095         }
3096       }
3097       InflateGridFor(area);  // Step 2, inflating for auto items too
3098     }
3099   }
3100 
3101   // XXX NOTE possible spec issue.
3102   // XXX It's unclear if the remaining major-dimension auto and
3103   // XXX auto in both dimensions should use the same cursor or not,
3104   // XXX https://www.w3.org/Bugs/Public/show_bug.cgi?id=16044
3105   // XXX seems to indicate it shouldn't.
3106   // XXX http://dev.w3.org/csswg/css-grid/#auto-placement-cursor
3107   // XXX now says it should (but didn't in earlier versions)
3108 
3109   // Step 3, place the remaining grid items
3110   uint32_t cursorMajor = 0;  // for 'dense' these two cursors will stay at 0,0
3111   uint32_t cursorMinor = 0;
3112   auto placeAutoMajorFunc =
3113       isRowOrder ? &Grid::PlaceAutoRow : &Grid::PlaceAutoCol;
3114   aState.mIter.Reset();
3115   for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
3116     GridArea& area = aState.mGridItems[aState.mIter.ItemIndex()].mArea;
3117     MOZ_ASSERT(
3118         *aState.mIter == aState.mGridItems[aState.mIter.ItemIndex()].mFrame,
3119         "iterator out of sync with aState.mGridItems");
3120     LineRange& major = isRowOrder ? area.mRows : area.mCols;
3121     LineRange& minor = isRowOrder ? area.mCols : area.mRows;
3122     if (major.IsAuto()) {
3123       if (minor.IsDefinite()) {
3124         // Items with 'auto' in the major dimension only.
3125         if (isSparse) {
3126           if (minor.mStart < cursorMinor) {
3127             ++cursorMajor;
3128           }
3129           cursorMinor = minor.mStart;
3130         }
3131         (this->*placeAutoMajorFunc)(cursorMajor, &area);
3132         if (isSparse) {
3133           cursorMajor = major.mStart;
3134         }
3135       } else {
3136         // Items with 'auto' in both dimensions.
3137         if (isRowOrder) {
3138           PlaceAutoAutoInRowOrder(cursorMinor, cursorMajor, &area);
3139         } else {
3140           PlaceAutoAutoInColOrder(cursorMajor, cursorMinor, &area);
3141         }
3142         if (isSparse) {
3143           cursorMajor = major.mStart;
3144           cursorMinor = minor.mEnd;
3145 #ifdef DEBUG
3146           uint32_t gridMajorEnd = isRowOrder ? mGridRowEnd : mGridColEnd;
3147           uint32_t gridMinorEnd = isRowOrder ? mGridColEnd : mGridRowEnd;
3148           MOZ_ASSERT(cursorMajor <= gridMajorEnd,
3149                      "we shouldn't need to place items further than 1 track "
3150                      "past the current end of the grid, in major dimension");
3151           MOZ_ASSERT(cursorMinor <= gridMinorEnd,
3152                      "we shouldn't add implicit minor tracks for auto/auto");
3153 #endif
3154         }
3155       }
3156       mCellMap.Fill(area);
3157       InflateGridFor(area);
3158     }
3159   }
3160 
3161   if (aState.mFrame->IsAbsoluteContainer()) {
3162     // 9.4 Absolutely-positioned Grid Items
3163     // http://dev.w3.org/csswg/css-grid/#abspos-items
3164     // We only resolve definite lines here; we'll align auto positions to the
3165     // grid container later during reflow.
3166     nsFrameList children(
3167         aState.mFrame->GetChildList(aState.mFrame->GetAbsoluteListID()));
3168     const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
3169     const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
3170     // Untranslate the grid again temporarily while resolving abs.pos. lines.
3171     AutoRestore<uint32_t> save1(mGridColEnd);
3172     AutoRestore<uint32_t> save2(mGridRowEnd);
3173     mGridColEnd -= offsetToColZero;
3174     mGridRowEnd -= offsetToRowZero;
3175     aState.mAbsPosItems.ClearAndRetainStorage();
3176     size_t i = 0;
3177     for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next(), ++i) {
3178       nsIFrame* child = e.get();
3179       GridItemInfo* info = aState.mAbsPosItems.AppendElement(GridItemInfo(
3180           child,
3181           PlaceAbsPos(child, colLineNameMap, rowLineNameMap, gridStyle)));
3182       GridArea& area = info->mArea;
3183       if (area.mCols.mUntranslatedStart != int32_t(kAutoLine)) {
3184         area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
3185       }
3186       if (area.mCols.mUntranslatedEnd != int32_t(kAutoLine)) {
3187         area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
3188       }
3189       if (area.mRows.mUntranslatedStart != int32_t(kAutoLine)) {
3190         area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
3191       }
3192       if (area.mRows.mUntranslatedEnd != int32_t(kAutoLine)) {
3193         area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
3194       }
3195     }
3196   }
3197 
3198   // Count empty 'auto-fit' tracks in the repeat() range.
3199   // |colAdjust| will have a count for each line in the grid of how many
3200   // tracks were empty between the start of the grid and that line.
3201 
3202   // Since this loop is concerned with just the repeat tracks, we
3203   // iterate from 0..NumRepeatTracks() which is the natural range of
3204   // mRemoveRepeatTracks. This means we have to add
3205   // (mExplicitGridOffset + mRepeatAutoStart) to get a zero-based
3206   // index for arrays like mCellMap and colAdjust. We'll then fill out
3207   // the colAdjust array for all the remaining lines.
3208   Maybe<nsTArray<uint32_t>> colAdjust;
3209   uint32_t numEmptyCols = 0;
3210   if (aState.mColFunctions.mHasRepeatAuto &&
3211       !gridStyle->GridTemplateColumns().mIsAutoFill &&
3212       aState.mColFunctions.NumRepeatTracks() > 0) {
3213     const uint32_t repeatStart = (aState.mColFunctions.mExplicitGridOffset +
3214                                   aState.mColFunctions.mRepeatAutoStart);
3215     const uint32_t numRepeats = aState.mColFunctions.NumRepeatTracks();
3216     const uint32_t numColLines = mGridColEnd + 1;
3217     for (uint32_t i = 0; i < numRepeats; ++i) {
3218       if (numEmptyCols) {
3219         (*colAdjust)[repeatStart + i] = numEmptyCols;
3220       }
3221       if (mCellMap.IsEmptyCol(repeatStart + i)) {
3222         ++numEmptyCols;
3223         if (colAdjust.isNothing()) {
3224           colAdjust.emplace(numColLines);
3225           colAdjust->SetLength(numColLines);
3226           PodZero(colAdjust->Elements(), colAdjust->Length());
3227         }
3228 
3229         aState.mColFunctions.mRemovedRepeatTracks[i] = true;
3230       }
3231     }
3232     // Fill out the colAdjust array for all the columns after the
3233     // repeats.
3234     if (numEmptyCols) {
3235       for (uint32_t col = repeatStart + numRepeats; col < numColLines; ++col) {
3236         (*colAdjust)[col] = numEmptyCols;
3237       }
3238     }
3239   }
3240 
3241   // Do similar work for the row tracks, with the same logic.
3242   Maybe<nsTArray<uint32_t>> rowAdjust;
3243   uint32_t numEmptyRows = 0;
3244   if (aState.mRowFunctions.mHasRepeatAuto &&
3245       !gridStyle->GridTemplateRows().mIsAutoFill &&
3246       aState.mRowFunctions.NumRepeatTracks() > 0) {
3247     const uint32_t repeatStart = (aState.mRowFunctions.mExplicitGridOffset +
3248                                   aState.mRowFunctions.mRepeatAutoStart);
3249     const uint32_t numRepeats = aState.mRowFunctions.NumRepeatTracks();
3250     const uint32_t numRowLines = mGridRowEnd + 1;
3251     for (uint32_t i = 0; i < numRepeats; ++i) {
3252       if (numEmptyRows) {
3253         (*rowAdjust)[repeatStart + i] = numEmptyRows;
3254       }
3255       if (mCellMap.IsEmptyRow(repeatStart + i)) {
3256         ++numEmptyRows;
3257         if (rowAdjust.isNothing()) {
3258           rowAdjust.emplace(numRowLines);
3259           rowAdjust->SetLength(numRowLines);
3260           PodZero(rowAdjust->Elements(), rowAdjust->Length());
3261         }
3262 
3263         aState.mRowFunctions.mRemovedRepeatTracks[i] = true;
3264       }
3265     }
3266     if (numEmptyRows) {
3267       for (uint32_t row = repeatStart + numRepeats; row < numRowLines; ++row) {
3268         (*rowAdjust)[row] = numEmptyRows;
3269       }
3270     }
3271   }
3272   // Remove the empty 'auto-fit' tracks we found above, if any.
3273   if (numEmptyCols || numEmptyRows) {
3274     // Adjust the line numbers in the grid areas.
3275     for (auto& item : aState.mGridItems) {
3276       GridArea& area = item.mArea;
3277       if (numEmptyCols) {
3278         area.mCols.AdjustForRemovedTracks(*colAdjust);
3279       }
3280       if (numEmptyRows) {
3281         area.mRows.AdjustForRemovedTracks(*rowAdjust);
3282       }
3283     }
3284     for (auto& item : aState.mAbsPosItems) {
3285       GridArea& area = item.mArea;
3286       if (numEmptyCols) {
3287         area.mCols.AdjustAbsPosForRemovedTracks(*colAdjust);
3288       }
3289       if (numEmptyRows) {
3290         area.mRows.AdjustAbsPosForRemovedTracks(*rowAdjust);
3291       }
3292     }
3293     // Adjust the grid size.
3294     mGridColEnd -= numEmptyCols;
3295     mExplicitGridColEnd -= numEmptyCols;
3296     mGridRowEnd -= numEmptyRows;
3297     mExplicitGridRowEnd -= numEmptyRows;
3298     // Adjust the track mapping to unmap the removed tracks.
3299     auto colRepeatCount = aState.mColFunctions.NumRepeatTracks();
3300     aState.mColFunctions.SetNumRepeatTracks(colRepeatCount - numEmptyCols);
3301     auto rowRepeatCount = aState.mRowFunctions.NumRepeatTracks();
3302     aState.mRowFunctions.SetNumRepeatTracks(rowRepeatCount - numEmptyRows);
3303   }
3304 
3305   // Update the line boundaries of the implicit grid areas, if needed.
3306   if (mAreas &&
3307       aState.mFrame->HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES)) {
3308     for (auto iter = mAreas->Iter(); !iter.Done(); iter.Next()) {
3309       auto& areaInfo = iter.Data();
3310 
3311       // Resolve the lines for the area. We use the name of the area as the
3312       // name of the lines, knowing that the line placement algorithm will
3313       // add the -start and -end suffixes as appropriate for layout.
3314       nsStyleGridLine lineStartAndEnd;
3315       lineStartAndEnd.mLineName = areaInfo.mName;
3316 
3317       LineRange columnLines = ResolveLineRange(
3318           lineStartAndEnd, lineStartAndEnd, colLineNameMap,
3319           &GridNamedArea::mColumnStart, &GridNamedArea::mColumnEnd,
3320           mExplicitGridColEnd, gridStyle);
3321 
3322       LineRange rowLines =
3323           ResolveLineRange(lineStartAndEnd, lineStartAndEnd, rowLineNameMap,
3324                            &GridNamedArea::mRowStart, &GridNamedArea::mRowEnd,
3325                            mExplicitGridRowEnd, gridStyle);
3326 
3327       // Put the resolved line indices back into the area structure.
3328       areaInfo.mColumnStart = columnLines.mStart + mExplicitGridOffsetCol;
3329       areaInfo.mColumnEnd = columnLines.mEnd + mExplicitGridOffsetCol;
3330       areaInfo.mRowStart = rowLines.mStart + mExplicitGridOffsetRow;
3331       areaInfo.mRowEnd = rowLines.mEnd + mExplicitGridOffsetRow;
3332     }
3333   }
3334 }
3335 
Initialize(const TrackSizingFunctions & aFunctions,const nsStyleCoord & aGridGap,uint32_t aNumTracks,nscoord aContentBoxSize)3336 void nsGridContainerFrame::Tracks::Initialize(
3337     const TrackSizingFunctions& aFunctions, const nsStyleCoord& aGridGap,
3338     uint32_t aNumTracks, nscoord aContentBoxSize) {
3339   MOZ_ASSERT(aNumTracks >=
3340              aFunctions.mExplicitGridOffset + aFunctions.NumExplicitTracks());
3341   mSizes.SetLength(aNumTracks);
3342   PodZero(mSizes.Elements(), mSizes.Length());
3343   for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
3344     mStateUnion |=
3345         mSizes[i].Initialize(aContentBoxSize, aFunctions.MinSizingFor(i),
3346                              aFunctions.MaxSizingFor(i));
3347   }
3348   mGridGap = ::ResolveToDefiniteSize(aGridGap, aContentBoxSize);
3349   mContentBoxSize = aContentBoxSize;
3350 }
3351 
3352 /**
3353  * Reflow aChild in the given aAvailableSize.
3354  */
MeasuringReflow(nsIFrame * aChild,const ReflowInput * aReflowInput,gfxContext * aRC,const LogicalSize & aAvailableSize,const LogicalSize & aCBSize,nscoord aIMinSizeClamp=NS_MAXSIZE,nscoord aBMinSizeClamp=NS_MAXSIZE)3355 static nscoord MeasuringReflow(nsIFrame* aChild,
3356                                const ReflowInput* aReflowInput, gfxContext* aRC,
3357                                const LogicalSize& aAvailableSize,
3358                                const LogicalSize& aCBSize,
3359                                nscoord aIMinSizeClamp = NS_MAXSIZE,
3360                                nscoord aBMinSizeClamp = NS_MAXSIZE) {
3361   nsContainerFrame* parent = aChild->GetParent();
3362   nsPresContext* pc = aChild->PresContext();
3363   Maybe<ReflowInput> dummyParentState;
3364   const ReflowInput* rs = aReflowInput;
3365   if (!aReflowInput) {
3366     MOZ_ASSERT(!parent->HasAnyStateBits(NS_FRAME_IN_REFLOW));
3367     dummyParentState.emplace(
3368         pc, parent, aRC,
3369         LogicalSize(parent->GetWritingMode(), 0, NS_UNCONSTRAINEDSIZE),
3370         ReflowInput::DUMMY_PARENT_REFLOW_STATE);
3371     rs = dummyParentState.ptr();
3372   }
3373 #ifdef DEBUG
3374   // This will suppress various CRAZY_SIZE warnings for this reflow.
3375   parent->SetProperty(nsContainerFrame::DebugReflowingWithInfiniteISize(),
3376                       true);
3377 #endif
3378   auto wm = aChild->GetWritingMode();
3379   uint32_t riFlags = ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE;
3380   if (aAvailableSize.ISize(wm) == INFINITE_ISIZE_COORD) {
3381     riFlags |= ReflowInput::COMPUTE_SIZE_SHRINK_WRAP;
3382   }
3383   if (aIMinSizeClamp != NS_MAXSIZE) {
3384     riFlags |= ReflowInput::I_CLAMP_MARGIN_BOX_MIN_SIZE;
3385   }
3386   if (aBMinSizeClamp != NS_MAXSIZE) {
3387     riFlags |= ReflowInput::B_CLAMP_MARGIN_BOX_MIN_SIZE;
3388     aChild->SetProperty(nsIFrame::BClampMarginBoxMinSizeProperty(),
3389                         aBMinSizeClamp);
3390   } else {
3391     aChild->DeleteProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
3392   }
3393   ReflowInput childRI(pc, *rs, aChild, aAvailableSize, &aCBSize, riFlags);
3394 
3395   // Because we pass ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE, and the
3396   // previous reflow of the child might not have, set the child's
3397   // block-resize flag to true.
3398   // FIXME (perf): It would be faster to do this only if the previous
3399   // reflow of the child was not a measuring reflow, and only if the
3400   // child does some of the things that are affected by
3401   // ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE.
3402   childRI.SetBResize(true);
3403 
3404   ReflowOutput childSize(childRI);
3405   nsReflowStatus childStatus;
3406   const uint32_t flags = NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW;
3407   parent->ReflowChild(aChild, pc, childSize, childRI, wm, LogicalPoint(wm),
3408                       nsSize(), flags, childStatus);
3409   parent->FinishReflowChild(aChild, pc, childSize, &childRI, wm,
3410                             LogicalPoint(wm), nsSize(), flags);
3411 #ifdef DEBUG
3412   parent->DeleteProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
3413 #endif
3414   return childSize.BSize(wm);
3415 }
3416 
3417 /**
3418  * Return the [min|max]-content contribution of aChild to its parent (i.e.
3419  * the child's margin-box) in aAxis.
3420  */
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)3421 static nscoord ContentContribution(
3422     const GridItemInfo& aGridItem, const GridReflowInput& aState,
3423     gfxContext* aRC, WritingMode aCBWM, LogicalAxis aAxis,
3424     const Maybe<LogicalSize>& aPercentageBasis, IntrinsicISizeType aConstraint,
3425     nscoord aMinSizeClamp = NS_MAXSIZE, uint32_t aFlags = 0) {
3426   nsIFrame* child = aGridItem.mFrame;
3427   PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
3428   nscoord size = nsLayoutUtils::IntrinsicForAxis(
3429       axis, aRC, child, aConstraint, aPercentageBasis,
3430       aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED |
3431           nsLayoutUtils::ADD_PERCENTS,
3432       aMinSizeClamp);
3433   if (size == NS_INTRINSIC_WIDTH_UNKNOWN) {
3434     // We need to reflow the child to find its BSize contribution.
3435     // XXX this will give mostly correct results for now (until bug 1174569).
3436     nscoord availISize = INFINITE_ISIZE_COORD;
3437     nscoord availBSize = NS_UNCONSTRAINEDSIZE;
3438     auto childWM = child->GetWritingMode();
3439     const bool isOrthogonal = childWM.IsOrthogonalTo(aCBWM);
3440     // The next two variables are MinSizeClamp values in the child's axes.
3441     nscoord iMinSizeClamp = NS_MAXSIZE;
3442     nscoord bMinSizeClamp = NS_MAXSIZE;
3443     LogicalSize cbSize(childWM, 0, 0);
3444     if (aState.mCols.mCanResolveLineRangeSize) {
3445       nscoord sz = aState.mCols.ResolveSize(aGridItem.mArea.mCols);
3446       if (isOrthogonal) {
3447         availBSize = sz;
3448         cbSize.BSize(childWM) = sz;
3449         if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
3450           bMinSizeClamp = sz;
3451         }
3452       } else {
3453         availISize = sz;
3454         cbSize.ISize(childWM) = sz;
3455         if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
3456           iMinSizeClamp = sz;
3457         }
3458       }
3459     }
3460     if (isOrthogonal == (aAxis == eLogicalAxisInline)) {
3461       bMinSizeClamp = aMinSizeClamp;
3462     } else {
3463       iMinSizeClamp = aMinSizeClamp;
3464     }
3465     LogicalSize availableSize(childWM, availISize, availBSize);
3466     size = ::MeasuringReflow(child, aState.mReflowInput, aRC, availableSize,
3467                              cbSize, iMinSizeClamp, bMinSizeClamp);
3468     nsIFrame::IntrinsicISizeOffsetData offsets = child->IntrinsicBSizeOffsets();
3469     size += offsets.hMargin;
3470     auto percent = offsets.hPctMargin;
3471     if (availBSize == NS_UNCONSTRAINEDSIZE) {
3472       // We always want to add in percent padding too, unless we already did so
3473       // using a resolved column size above.
3474       percent += offsets.hPctPadding;
3475     }
3476     size = nsLayoutUtils::AddPercents(size, percent);
3477     nscoord overflow = size - aMinSizeClamp;
3478     if (MOZ_UNLIKELY(overflow > 0)) {
3479       nscoord contentSize = child->ContentBSize(childWM);
3480       nscoord newContentSize = std::max(nscoord(0), contentSize - overflow);
3481       // XXXmats deal with percentages better, see bug 1300369 comment 27.
3482       size -= contentSize - newContentSize;
3483     }
3484   }
3485   MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
3486              "baseline offset should be non-negative at this point");
3487   MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
3488                  aGridItem.mBaselineOffset[aAxis] == nscoord(0),
3489              "baseline offset should be zero when not baseline-aligned");
3490   size += aGridItem.mBaselineOffset[aAxis];
3491   return std::max(size, 0);
3492 }
3493 
3494 struct CachedIntrinsicSizes {
3495   Maybe<nscoord> mMinSize;
3496   Maybe<nscoord> mMinContentContribution;
3497   Maybe<nscoord> mMaxContentContribution;
3498 
3499   // The item's percentage basis for intrinsic sizing purposes.
3500   Maybe<LogicalSize> mPercentageBasis;
3501 
3502   // "if the grid item spans only grid tracks that have a fixed max track
3503   // sizing function, its automatic minimum size in that dimension is
3504   // further clamped to less than or equal to the size necessary to fit its
3505   // margin box within the resulting grid area (flooring at zero)"
3506   // https://drafts.csswg.org/css-grid/#min-size-auto
3507   // This is the clamp value to use for that:
3508   nscoord mMinSizeClamp = NS_MAXSIZE;
3509 };
3510 
MinContentContribution(const GridItemInfo & aGridItem,const GridReflowInput & aState,gfxContext * aRC,WritingMode aCBWM,LogicalAxis aAxis,CachedIntrinsicSizes * aCache)3511 static nscoord MinContentContribution(const GridItemInfo& aGridItem,
3512                                       const GridReflowInput& aState,
3513                                       gfxContext* aRC, WritingMode aCBWM,
3514                                       LogicalAxis aAxis,
3515                                       CachedIntrinsicSizes* aCache) {
3516   if (aCache->mMinContentContribution.isSome()) {
3517     return aCache->mMinContentContribution.value();
3518   }
3519   if (aCache->mPercentageBasis.isNothing()) {
3520     aCache->mPercentageBasis.emplace(
3521         aState.PercentageBasisFor(aAxis, aGridItem));
3522   }
3523   nscoord s = ContentContribution(
3524       aGridItem, aState, aRC, aCBWM, aAxis, aCache->mPercentageBasis,
3525       nsLayoutUtils::MIN_ISIZE, aCache->mMinSizeClamp);
3526   aCache->mMinContentContribution.emplace(s);
3527   return s;
3528 }
3529 
MaxContentContribution(const GridItemInfo & aGridItem,const GridReflowInput & aState,gfxContext * aRC,WritingMode aCBWM,LogicalAxis aAxis,CachedIntrinsicSizes * aCache)3530 static nscoord MaxContentContribution(const GridItemInfo& aGridItem,
3531                                       const GridReflowInput& aState,
3532                                       gfxContext* aRC, WritingMode aCBWM,
3533                                       LogicalAxis aAxis,
3534                                       CachedIntrinsicSizes* aCache) {
3535   if (aCache->mMaxContentContribution.isSome()) {
3536     return aCache->mMaxContentContribution.value();
3537   }
3538   if (aCache->mPercentageBasis.isNothing()) {
3539     aCache->mPercentageBasis.emplace(
3540         aState.PercentageBasisFor(aAxis, aGridItem));
3541   }
3542   nscoord s = ContentContribution(
3543       aGridItem, aState, aRC, aCBWM, aAxis, aCache->mPercentageBasis,
3544       nsLayoutUtils::PREF_ISIZE, aCache->mMinSizeClamp);
3545   aCache->mMaxContentContribution.emplace(s);
3546   return s;
3547 }
3548 
3549 // Computes the min-size contribution for a grid item, as defined at
3550 // https://drafts.csswg.org/css-grid/#min-size-contributions
MinSize(const GridItemInfo & aGridItem,const GridReflowInput & aState,gfxContext * aRC,WritingMode aCBWM,LogicalAxis aAxis,CachedIntrinsicSizes * aCache)3551 static nscoord MinSize(const GridItemInfo& aGridItem,
3552                        const GridReflowInput& aState, gfxContext* aRC,
3553                        WritingMode aCBWM, LogicalAxis aAxis,
3554                        CachedIntrinsicSizes* aCache) {
3555   if (aCache->mMinSize.isSome()) {
3556     return aCache->mMinSize.value();
3557   }
3558   nsIFrame* child = aGridItem.mFrame;
3559   PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
3560   const nsStylePosition* stylePos = child->StylePosition();
3561   const nsStyleCoord& sizeStyle =
3562       axis == eAxisHorizontal ? stylePos->mWidth : stylePos->mHeight;
3563   if (sizeStyle.GetUnit() != eStyleUnit_Auto) {
3564     nscoord s =
3565         MinContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, aCache);
3566     aCache->mMinSize.emplace(s);
3567     return s;
3568   }
3569 
3570   // https://drafts.csswg.org/css-grid/#min-size-auto
3571   // This calculates the min-content contribution from either a definite
3572   // min-width (or min-height depending on aAxis), or the "specified /
3573   // transferred size" for min-width:auto if overflow == visible (as min-width:0
3574   // otherwise), or NS_UNCONSTRAINEDSIZE for other min-width intrinsic values
3575   // (which results in always taking the "content size" part below).
3576   MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
3577              "baseline offset should be non-negative at this point");
3578   MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
3579                  aGridItem.mBaselineOffset[aAxis] == nscoord(0),
3580              "baseline offset should be zero when not baseline-aligned");
3581   nscoord sz = aGridItem.mBaselineOffset[aAxis] +
3582                nsLayoutUtils::MinSizeContributionForAxis(
3583                    axis, aRC, child, nsLayoutUtils::MIN_ISIZE);
3584   const nsStyleCoord& style =
3585       axis == eAxisHorizontal ? stylePos->mMinWidth : stylePos->mMinHeight;
3586   auto unit = style.GetUnit();
3587   if (unit == eStyleUnit_Enumerated ||
3588       (unit == eStyleUnit_Auto &&
3589        child->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)) {
3590     // Now calculate the "content size" part and return whichever is smaller.
3591     MOZ_ASSERT(unit != eStyleUnit_Enumerated || sz == NS_UNCONSTRAINEDSIZE);
3592     if (aCache->mPercentageBasis.isNothing()) {
3593       aCache->mPercentageBasis.emplace(
3594           aState.PercentageBasisFor(aAxis, aGridItem));
3595     }
3596     sz = std::min(
3597         sz, ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
3598                                 aCache->mPercentageBasis,
3599                                 nsLayoutUtils::MIN_ISIZE, aCache->mMinSizeClamp,
3600                                 nsLayoutUtils::MIN_INTRINSIC_ISIZE));
3601   }
3602   aCache->mMinSize.emplace(sz);
3603   return sz;
3604 }
3605 
CalculateSizes(GridReflowInput & aState,nsTArray<GridItemInfo> & aGridItems,const TrackSizingFunctions & aFunctions,nscoord aContentBoxSize,LineRange GridArea::* aRange,SizingConstraint aConstraint)3606 void nsGridContainerFrame::Tracks::CalculateSizes(
3607     GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
3608     const TrackSizingFunctions& aFunctions, nscoord aContentBoxSize,
3609     LineRange GridArea::*aRange, SizingConstraint aConstraint) {
3610   nscoord percentageBasis = aContentBoxSize;
3611   if (percentageBasis == NS_UNCONSTRAINEDSIZE) {
3612     percentageBasis = 0;
3613   }
3614   InitializeItemBaselines(aState, aGridItems);
3615   ResolveIntrinsicSize(aState, aGridItems, aFunctions, aRange, percentageBasis,
3616                        aConstraint);
3617   if (aConstraint != SizingConstraint::eMinContent) {
3618     nscoord freeSpace = aContentBoxSize;
3619     if (freeSpace != NS_UNCONSTRAINEDSIZE) {
3620       freeSpace -= SumOfGridGaps();
3621     }
3622     DistributeFreeSpace(freeSpace);
3623     StretchFlexibleTracks(aState, aGridItems, aFunctions, freeSpace);
3624   }
3625 }
3626 
StateBitsForRange(const LineRange & aRange) const3627 TrackSize::StateBits nsGridContainerFrame::Tracks::StateBitsForRange(
3628     const LineRange& aRange) const {
3629   MOZ_ASSERT(!aRange.IsAuto(), "must have a definite range");
3630   TrackSize::StateBits state = TrackSize::StateBits(0);
3631   const uint32_t start = aRange.mStart;
3632   const uint32_t end = aRange.mEnd;
3633   for (uint32_t i = start; i < end; ++i) {
3634     state |= mSizes[i].mState;
3635   }
3636   return state;
3637 }
3638 
ResolveIntrinsicSizeStep1(GridReflowInput & aState,const TrackSizingFunctions & aFunctions,nscoord aPercentageBasis,SizingConstraint aConstraint,const LineRange & aRange,const GridItemInfo & aGridItem)3639 bool nsGridContainerFrame::Tracks::ResolveIntrinsicSizeStep1(
3640     GridReflowInput& aState, const TrackSizingFunctions& aFunctions,
3641     nscoord aPercentageBasis, SizingConstraint aConstraint,
3642     const LineRange& aRange, const GridItemInfo& aGridItem) {
3643   CachedIntrinsicSizes cache;
3644   TrackSize& sz = mSizes[aRange.mStart];
3645   WritingMode wm = aState.mWM;
3646 
3647   // min sizing
3648   gfxContext* rc = &aState.mRenderingContext;
3649   if (sz.mState & TrackSize::eAutoMinSizing) {
3650     nscoord s;
3651     // Check if we need to apply "Automatic Minimum Size" and cache it.
3652     if (aGridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) {
3653       aGridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize;
3654       // Clamp it if it's spanning a definite track max-sizing function.
3655       if (TrackSize::IsDefiniteMaxSizing(sz.mState)) {
3656         auto maxCoord = aFunctions.MaxSizingFor(aRange.mStart);
3657         cache.mMinSizeClamp =
3658             maxCoord.ComputeCoordPercentCalc(aPercentageBasis);
3659         aGridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
3660       }
3661       if (aConstraint != SizingConstraint::eMaxContent) {
3662         s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
3663       } else {
3664         s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
3665       }
3666     } else {
3667       s = MinSize(aGridItem, aState, rc, wm, mAxis, &cache);
3668     }
3669     sz.mBase = std::max(sz.mBase, s);
3670   } else if (sz.mState & TrackSize::eMinContentMinSizing) {
3671     auto s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
3672     sz.mBase = std::max(sz.mBase, s);
3673   } else if (sz.mState & TrackSize::eMaxContentMinSizing) {
3674     auto s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
3675     sz.mBase = std::max(sz.mBase, s);
3676   }
3677   // max sizing
3678   if (sz.mState & TrackSize::eMinContentMaxSizing) {
3679     auto s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
3680     if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
3681       sz.mLimit = s;
3682     } else {
3683       sz.mLimit = std::max(sz.mLimit, s);
3684     }
3685   } else if (sz.mState &
3686              (TrackSize::eAutoMaxSizing | TrackSize::eMaxContentMaxSizing)) {
3687     auto s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
3688     if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
3689       sz.mLimit = s;
3690     } else {
3691       sz.mLimit = std::max(sz.mLimit, s);
3692     }
3693     if (MOZ_UNLIKELY(sz.mState & TrackSize::eFitContent)) {
3694       // Clamp mLimit to the fit-content() size, for §12.5.1.
3695       auto maxCoord = aFunctions.MaxSizingFor(aRange.mStart);
3696       nscoord fitContentClamp =
3697           maxCoord.ComputeCoordPercentCalc(aPercentageBasis);
3698       sz.mLimit = std::min(sz.mLimit, fitContentClamp);
3699     }
3700   }
3701   if (sz.mLimit < sz.mBase) {
3702     sz.mLimit = sz.mBase;
3703   }
3704   return sz.mState & TrackSize::eFlexMaxSizing;
3705 }
3706 
CalculateItemBaselines(nsTArray<ItemBaselineData> & aBaselineItems,BaselineSharingGroup aBaselineGroup)3707 void nsGridContainerFrame::Tracks::CalculateItemBaselines(
3708     nsTArray<ItemBaselineData>& aBaselineItems,
3709     BaselineSharingGroup aBaselineGroup) {
3710   if (aBaselineItems.IsEmpty()) {
3711     return;
3712   }
3713 
3714   // Sort the collected items on their baseline track.
3715   std::sort(aBaselineItems.begin(), aBaselineItems.end(),
3716             ItemBaselineData::IsBaselineTrackLessThan);
3717 
3718   MOZ_ASSERT(mSizes.Length() > 0, "having an item implies at least one track");
3719   const uint32_t lastTrack = mSizes.Length() - 1;
3720   nscoord maxBaseline = 0;
3721   nscoord maxDescent = 0;
3722   uint32_t currentTrack = kAutoLine;  // guaranteed to not match any item
3723   uint32_t trackStartIndex = 0;
3724   for (uint32_t i = 0, len = aBaselineItems.Length(); true; ++i) {
3725     // Find the maximum baseline and descent in the current track.
3726     if (i != len) {
3727       const ItemBaselineData& item = aBaselineItems[i];
3728       if (currentTrack == item.mBaselineTrack) {
3729         maxBaseline = std::max(maxBaseline, item.mBaseline);
3730         maxDescent = std::max(maxDescent, item.mSize - item.mBaseline);
3731         continue;
3732       }
3733     }
3734     // Iterate the current track again and update the baseline offsets making
3735     // all items baseline-aligned within this group in this track.
3736     for (uint32_t j = trackStartIndex; j < i; ++j) {
3737       const ItemBaselineData& item = aBaselineItems[j];
3738       item.mGridItem->mBaselineOffset[mAxis] = maxBaseline - item.mBaseline;
3739       MOZ_ASSERT(item.mGridItem->mBaselineOffset[mAxis] >= 0);
3740     }
3741     if (i != 0) {
3742       // Store the size of this baseline-aligned subtree.
3743       mSizes[currentTrack].mBaselineSubtreeSize[aBaselineGroup] =
3744           maxBaseline + maxDescent;
3745       // Record the first(last) baseline for the first(last) track.
3746       if (currentTrack == 0 && aBaselineGroup == BaselineSharingGroup::eFirst) {
3747         mBaseline[aBaselineGroup] = maxBaseline;
3748       }
3749       if (currentTrack == lastTrack &&
3750           aBaselineGroup == BaselineSharingGroup::eLast) {
3751         mBaseline[aBaselineGroup] = maxBaseline;
3752       }
3753     }
3754     if (i == len) {
3755       break;
3756     }
3757     // Initialize data for the next track with baseline-aligned items.
3758     const ItemBaselineData& item = aBaselineItems[i];
3759     currentTrack = item.mBaselineTrack;
3760     trackStartIndex = i;
3761     maxBaseline = item.mBaseline;
3762     maxDescent = item.mSize - item.mBaseline;
3763   }
3764 }
3765 
InitializeItemBaselines(GridReflowInput & aState,nsTArray<GridItemInfo> & aGridItems)3766 void nsGridContainerFrame::Tracks::InitializeItemBaselines(
3767     GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems) {
3768   nsTArray<ItemBaselineData> firstBaselineItems;
3769   nsTArray<ItemBaselineData> lastBaselineItems;
3770   WritingMode wm = aState.mWM;
3771   nsStyleContext* containerSC = aState.mFrame->StyleContext();
3772   CSSOrderAwareFrameIterator& iter = aState.mIter;
3773   iter.Reset();
3774   for (; !iter.AtEnd(); iter.Next()) {
3775     nsIFrame* child = *iter;
3776     GridItemInfo& gridItem = aGridItems[iter.ItemIndex()];
3777     uint32_t baselineTrack = kAutoLine;
3778     auto state = ItemState(0);
3779     auto childWM = child->GetWritingMode();
3780     const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
3781     const bool isInlineAxis = mAxis == eLogicalAxisInline;  // i.e. columns
3782     // XXX update the line below to include orthogonal grid/table boxes
3783     // XXX since they have baselines in both dimensions. And flexbox with
3784     // XXX reversed main/cross axis?
3785     const bool itemHasBaselineParallelToTrack = isInlineAxis == isOrthogonal;
3786     if (itemHasBaselineParallelToTrack) {
3787       // [align|justify]-self:[last ]baseline.
3788       auto selfAlignment =
3789           isOrthogonal ? child->StylePosition()->UsedJustifySelf(containerSC)
3790                        : child->StylePosition()->UsedAlignSelf(containerSC);
3791       selfAlignment &= ~NS_STYLE_ALIGN_FLAG_BITS;
3792       if (selfAlignment == NS_STYLE_ALIGN_BASELINE) {
3793         state |= ItemState::eFirstBaseline | ItemState::eSelfBaseline;
3794         const GridArea& area = gridItem.mArea;
3795         baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
3796       } else if (selfAlignment == NS_STYLE_ALIGN_LAST_BASELINE) {
3797         state |= ItemState::eLastBaseline | ItemState::eSelfBaseline;
3798         const GridArea& area = gridItem.mArea;
3799         baselineTrack = (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
3800       }
3801 
3802       // [align|justify]-content:[last ]baseline.
3803       // https://drafts.csswg.org/css-align-3/#baseline-align-content
3804       // "[...] and its computed 'align-self' or 'justify-self' (whichever
3805       // affects its block axis) is 'stretch' or 'self-start' ('self-end').
3806       // For this purpose, the 'start', 'end', 'flex-start', and 'flex-end'
3807       // values of 'align-self' are treated as either 'self-start' or
3808       // 'self-end', whichever they end up equivalent to.
3809       auto alignContent = child->StylePosition()->mAlignContent;
3810       alignContent &= ~NS_STYLE_ALIGN_FLAG_BITS;
3811       if (alignContent == NS_STYLE_ALIGN_BASELINE ||
3812           alignContent == NS_STYLE_ALIGN_LAST_BASELINE) {
3813         const auto selfAlignEdge = alignContent == NS_STYLE_ALIGN_BASELINE
3814                                        ? NS_STYLE_ALIGN_SELF_START
3815                                        : NS_STYLE_ALIGN_SELF_END;
3816         bool validCombo = selfAlignment == NS_STYLE_ALIGN_NORMAL ||
3817                           selfAlignment == NS_STYLE_ALIGN_STRETCH ||
3818                           selfAlignment == selfAlignEdge;
3819         if (!validCombo) {
3820           // We're doing alignment in the axis that's orthogonal to mAxis here.
3821           LogicalAxis alignAxis = GetOrthogonalAxis(mAxis);
3822           // |sameSide| is true if the container's start side in this axis is
3823           // the same as the child's start side, in the child's parallel axis.
3824           bool sameSide = wm.ParallelAxisStartsOnSameSide(alignAxis, childWM);
3825           switch (selfAlignment) {
3826             case NS_STYLE_ALIGN_LEFT:
3827               selfAlignment = !isInlineAxis || wm.IsBidiLTR()
3828                                   ? NS_STYLE_ALIGN_START
3829                                   : NS_STYLE_ALIGN_END;
3830               break;
3831             case NS_STYLE_ALIGN_RIGHT:
3832               selfAlignment = isInlineAxis && wm.IsBidiLTR()
3833                                   ? NS_STYLE_ALIGN_END
3834                                   : NS_STYLE_ALIGN_START;
3835               break;
3836           }
3837           switch (selfAlignment) {
3838             case NS_STYLE_ALIGN_START:
3839             case NS_STYLE_ALIGN_FLEX_START:
3840               validCombo =
3841                   sameSide == (alignContent == NS_STYLE_ALIGN_BASELINE);
3842               break;
3843             case NS_STYLE_ALIGN_END:
3844             case NS_STYLE_ALIGN_FLEX_END:
3845               validCombo =
3846                   sameSide == (alignContent == NS_STYLE_ALIGN_LAST_BASELINE);
3847               break;
3848           }
3849         }
3850         if (validCombo) {
3851           const GridArea& area = gridItem.mArea;
3852           if (alignContent == NS_STYLE_ALIGN_BASELINE) {
3853             state |= ItemState::eFirstBaseline | ItemState::eContentBaseline;
3854             baselineTrack =
3855                 isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
3856           } else if (alignContent == NS_STYLE_ALIGN_LAST_BASELINE) {
3857             state |= ItemState::eLastBaseline | ItemState::eContentBaseline;
3858             baselineTrack =
3859                 (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
3860           }
3861         }
3862       }
3863     }
3864 
3865     if (state & ItemState::eIsBaselineAligned) {
3866       // XXX available size issue
3867       LogicalSize avail(childWM, INFINITE_ISIZE_COORD, NS_UNCONSTRAINEDSIZE);
3868       auto* rc = &aState.mRenderingContext;
3869       // XXX figure out if we can avoid/merge this reflow with the main reflow.
3870       // XXX (after bug 1174569 is sorted out)
3871       //
3872       // XXX How should we handle percentage padding here? (bug 1330866)
3873       // XXX (see ::ContentContribution and how it deals with percentages)
3874       // XXX What if the true baseline after line-breaking differs from this
3875       // XXX hypothetical baseline based on an infinite inline size?
3876       // XXX Maybe we should just call ::ContentContribution here instead?
3877       // XXX For now we just pass a zero-sized CB:
3878       LogicalSize cbSize(childWM, 0, 0);
3879       ::MeasuringReflow(child, aState.mReflowInput, rc, avail, cbSize);
3880       nscoord baseline;
3881       nsGridContainerFrame* grid = do_QueryFrame(child);
3882       if (state & ItemState::eFirstBaseline) {
3883         if (grid) {
3884           if (isOrthogonal == isInlineAxis) {
3885             grid->GetBBaseline(BaselineSharingGroup::eFirst, &baseline);
3886           } else {
3887             grid->GetIBaseline(BaselineSharingGroup::eFirst, &baseline);
3888           }
3889         }
3890         if (grid || nsLayoutUtils::GetFirstLineBaseline(wm, child, &baseline)) {
3891           NS_ASSERTION(baseline != NS_INTRINSIC_WIDTH_UNKNOWN,
3892                        "about to use an unknown baseline");
3893           auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
3894           auto m = child->GetLogicalUsedMargin(wm);
3895           baseline += isInlineAxis ? m.IStart(wm) : m.BStart(wm);
3896           auto alignSize =
3897               frameSize + (isInlineAxis ? m.IStartEnd(wm) : m.BStartEnd(wm));
3898           firstBaselineItems.AppendElement(ItemBaselineData(
3899               {baselineTrack, baseline, alignSize, &gridItem}));
3900         } else {
3901           state &= ~ItemState::eAllBaselineBits;
3902         }
3903       } else {
3904         if (grid) {
3905           if (isOrthogonal == isInlineAxis) {
3906             grid->GetBBaseline(BaselineSharingGroup::eLast, &baseline);
3907           } else {
3908             grid->GetIBaseline(BaselineSharingGroup::eLast, &baseline);
3909           }
3910         }
3911         if (grid || nsLayoutUtils::GetLastLineBaseline(wm, child, &baseline)) {
3912           NS_ASSERTION(baseline != NS_INTRINSIC_WIDTH_UNKNOWN,
3913                        "about to use an unknown baseline");
3914           auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
3915           auto m = child->GetLogicalUsedMargin(wm);
3916           if (!grid) {
3917             // Convert to distance from border-box end.
3918             baseline = frameSize - baseline;
3919           }
3920           auto descent = baseline + (isInlineAxis ? m.IEnd(wm) : m.BEnd(wm));
3921           auto alignSize =
3922               frameSize + (isInlineAxis ? m.IStartEnd(wm) : m.BStartEnd(wm));
3923           lastBaselineItems.AppendElement(
3924               ItemBaselineData({baselineTrack, descent, alignSize, &gridItem}));
3925         } else {
3926           state &= ~ItemState::eAllBaselineBits;
3927         }
3928       }
3929     }
3930     MOZ_ASSERT(
3931         (state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) !=
3932             (ItemState::eFirstBaseline | ItemState::eLastBaseline),
3933         "first/last baseline bits are mutually exclusive");
3934     MOZ_ASSERT(
3935         (state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)) !=
3936             (ItemState::eSelfBaseline | ItemState::eContentBaseline),
3937         "*-self and *-content baseline bits are mutually exclusive");
3938     MOZ_ASSERT(
3939         !(state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) ==
3940             !(state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)),
3941         "first/last bit requires self/content bit and vice versa");
3942     gridItem.mState[mAxis] = state;
3943     gridItem.mBaselineOffset[mAxis] = nscoord(0);
3944   }
3945 
3946   if (firstBaselineItems.IsEmpty() && lastBaselineItems.IsEmpty()) {
3947     return;
3948   }
3949 
3950   // TODO: CSS Align spec issue - how to align a baseline subtree in a track?
3951   // https://lists.w3.org/Archives/Public/www-style/2016May/0141.html
3952   mBaselineSubtreeAlign[BaselineSharingGroup::eFirst] = NS_STYLE_ALIGN_START;
3953   mBaselineSubtreeAlign[BaselineSharingGroup::eLast] = NS_STYLE_ALIGN_END;
3954 
3955   CalculateItemBaselines(firstBaselineItems, BaselineSharingGroup::eFirst);
3956   CalculateItemBaselines(lastBaselineItems, BaselineSharingGroup::eLast);
3957 }
3958 
AlignBaselineSubtree(const GridItemInfo & aGridItem) const3959 void nsGridContainerFrame::Tracks::AlignBaselineSubtree(
3960     const GridItemInfo& aGridItem) const {
3961   auto state = aGridItem.mState[mAxis];
3962   if (!(state & ItemState::eIsBaselineAligned)) {
3963     return;
3964   }
3965   const GridArea& area = aGridItem.mArea;
3966   int32_t baselineTrack;
3967   const bool isFirstBaseline = state & ItemState::eFirstBaseline;
3968   if (isFirstBaseline) {
3969     baselineTrack =
3970         mAxis == eLogicalAxisBlock ? area.mRows.mStart : area.mCols.mStart;
3971   } else {
3972     baselineTrack =
3973         (mAxis == eLogicalAxisBlock ? area.mRows.mEnd : area.mCols.mEnd) - 1;
3974   }
3975   const TrackSize& sz = mSizes[baselineTrack];
3976   auto baselineGroup = isFirstBaseline ? BaselineSharingGroup::eFirst
3977                                        : BaselineSharingGroup::eLast;
3978   nscoord delta = sz.mBase - sz.mBaselineSubtreeSize[baselineGroup];
3979   const auto subtreeAlign = mBaselineSubtreeAlign[baselineGroup];
3980   switch (subtreeAlign) {
3981     case NS_STYLE_ALIGN_START:
3982       if (state & ItemState::eLastBaseline) {
3983         aGridItem.mBaselineOffset[mAxis] += delta;
3984       }
3985       break;
3986     case NS_STYLE_ALIGN_END:
3987       if (isFirstBaseline) {
3988         aGridItem.mBaselineOffset[mAxis] += delta;
3989       }
3990       break;
3991     case NS_STYLE_ALIGN_CENTER:
3992       aGridItem.mBaselineOffset[mAxis] += delta / 2;
3993       break;
3994     default:
3995       MOZ_ASSERT_UNREACHABLE("unexpected baseline subtree alignment");
3996   }
3997 }
3998 
3999 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)4000 bool nsGridContainerFrame::Tracks::GrowSizeForSpanningItems(
4001     nsTArray<Step2ItemData>::iterator aIter,
4002     const nsTArray<Step2ItemData>::iterator aIterEnd,
4003     nsTArray<uint32_t>& aTracks, nsTArray<TrackSize>& aPlan,
4004     nsTArray<TrackSize>& aItemPlan, TrackSize::StateBits aSelector,
4005     const FitContentClamper& aFitContentClamper,
4006     bool aNeedInfinitelyGrowableFlag) {
4007   constexpr bool isMaxSizingPhase =
4008       phase == TrackSizingPhase::eIntrinsicMaximums ||
4009       phase == TrackSizingPhase::eMaxContentMaximums;
4010   bool needToUpdateSizes = false;
4011   InitializePlan<phase>(aPlan);
4012   for (; aIter != aIterEnd; ++aIter) {
4013     const Step2ItemData& item = *aIter;
4014     if (!(item.mState & aSelector)) {
4015       continue;
4016     }
4017     if (isMaxSizingPhase) {
4018       for (auto j = item.mLineRange.mStart, end = item.mLineRange.mEnd; j < end;
4019            ++j) {
4020         aPlan[j].mState |= TrackSize::eModified;
4021       }
4022     }
4023     nscoord space = item.SizeContributionForPhase<phase>();
4024     if (space <= 0) {
4025       continue;
4026     }
4027     aTracks.ClearAndRetainStorage();
4028     space = CollectGrowable<phase>(space, item.mLineRange, aSelector, aTracks);
4029     if (space > 0) {
4030       DistributeToTrackSizes<phase>(space, aPlan, aItemPlan, aTracks, aSelector,
4031                                     aFitContentClamper);
4032       needToUpdateSizes = true;
4033     }
4034   }
4035   if (isMaxSizingPhase) {
4036     needToUpdateSizes = true;
4037   }
4038   if (needToUpdateSizes) {
4039     CopyPlanToSize<phase>(aPlan, aNeedInfinitelyGrowableFlag);
4040   }
4041   return needToUpdateSizes;
4042 }
4043 
ResolveIntrinsicSize(GridReflowInput & aState,nsTArray<GridItemInfo> & aGridItems,const TrackSizingFunctions & aFunctions,LineRange GridArea::* aRange,nscoord aPercentageBasis,SizingConstraint aConstraint)4044 void nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
4045     GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
4046     const TrackSizingFunctions& aFunctions, LineRange GridArea::*aRange,
4047     nscoord aPercentageBasis, SizingConstraint aConstraint) {
4048   // Resolve Intrinsic Track Sizes
4049   // http://dev.w3.org/csswg/css-grid/#algo-content
4050   // We're also setting eIsFlexing on the item state here to speed up
4051   // FindUsedFlexFraction later.
4052   AutoTArray<TrackSize::StateBits, 16> stateBitsPerSpan;
4053   nsTArray<Step2ItemData> step2Items;
4054   CSSOrderAwareFrameIterator& iter = aState.mIter;
4055   gfxContext* rc = &aState.mRenderingContext;
4056   WritingMode wm = aState.mWM;
4057   uint32_t maxSpan = 0;  // max span of the step2Items items
4058   // Setup track selector for step 2.2:
4059   const auto contentBasedMinSelector =
4060       aConstraint == SizingConstraint::eMinContent
4061           ? TrackSize::eIntrinsicMinSizing
4062           : TrackSize::eMinOrMaxContentMinSizing;
4063   // Setup track selector for step 2.3:
4064   const auto maxContentMinSelector =
4065       aConstraint == SizingConstraint::eMaxContent
4066           ? (TrackSize::eMaxContentMinSizing | TrackSize::eAutoMinSizing)
4067           : TrackSize::eMaxContentMinSizing;
4068   iter.Reset();
4069   for (; !iter.AtEnd(); iter.Next()) {
4070     auto& gridItem = aGridItems[iter.ItemIndex()];
4071     MOZ_ASSERT(!(gridItem.mState[mAxis] &
4072                  (ItemState::eApplyAutoMinSize | ItemState::eIsFlexing |
4073                   ItemState::eClampMarginBoxMinSize)),
4074                "Why are any of these bits set already?");
4075     const GridArea& area = gridItem.mArea;
4076     const LineRange& lineRange = area.*aRange;
4077     uint32_t span = lineRange.Extent();
4078     if (span == 1) {
4079       // Step 1. Size tracks to fit non-spanning items.
4080       if (ResolveIntrinsicSizeStep1(aState, aFunctions, aPercentageBasis,
4081                                     aConstraint, lineRange, gridItem)) {
4082         gridItem.mState[mAxis] |= ItemState::eIsFlexing;
4083       }
4084     } else {
4085       TrackSize::StateBits state = StateBitsForRange(lineRange);
4086 
4087       // Check if we need to apply "Automatic Minimum Size" and cache it.
4088       if ((state & TrackSize::eAutoMinSizing) &&
4089           gridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) {
4090         gridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize;
4091       }
4092 
4093       if ((state &
4094            (TrackSize::eIntrinsicMinSizing | TrackSize::eIntrinsicMaxSizing)) &&
4095           !(state & TrackSize::eFlexMaxSizing)) {
4096         // Collect data for Step 2.
4097         maxSpan = std::max(maxSpan, span);
4098         if (span >= stateBitsPerSpan.Length()) {
4099           uint32_t len = 2 * span;
4100           stateBitsPerSpan.SetCapacity(len);
4101           for (uint32_t i = stateBitsPerSpan.Length(); i < len; ++i) {
4102             stateBitsPerSpan.AppendElement(TrackSize::StateBits(0));
4103           }
4104         }
4105         stateBitsPerSpan[span] |= state;
4106         CachedIntrinsicSizes cache;
4107         // Calculate data for "Automatic Minimum Size" clamping, if needed.
4108         bool needed = ((state & TrackSize::eIntrinsicMinSizing) ||
4109                        aConstraint == SizingConstraint::eNoConstraint) &&
4110                       (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize);
4111         if (needed && TrackSize::IsDefiniteMaxSizing(state)) {
4112           nscoord minSizeClamp = 0;
4113           for (auto i = lineRange.mStart, end = lineRange.mEnd; i < end; ++i) {
4114             auto maxCoord = aFunctions.MaxSizingFor(i);
4115             minSizeClamp += maxCoord.ComputeCoordPercentCalc(aPercentageBasis);
4116           }
4117           minSizeClamp += mGridGap * (span - 1);
4118           cache.mMinSizeClamp = minSizeClamp;
4119           gridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
4120         }
4121         // Collect the various grid item size contributions we need.
4122         nscoord minSize = 0;
4123         if (state & (TrackSize::eIntrinsicMinSizing |    // for 2.1
4124                      TrackSize::eIntrinsicMaxSizing)) {  // for 2.5
4125           minSize = MinSize(gridItem, aState, rc, wm, mAxis, &cache);
4126         }
4127         nscoord minContent = 0;
4128         if (state & contentBasedMinSelector) {  // for 2.2
4129           minContent =
4130               MinContentContribution(gridItem, aState, rc, wm, mAxis, &cache);
4131         }
4132         nscoord maxContent = 0;
4133         if (state & (maxContentMinSelector |                    // for 2.3
4134                      TrackSize::eAutoOrMaxContentMaxSizing)) {  // for 2.6
4135           maxContent =
4136               MaxContentContribution(gridItem, aState, rc, wm, mAxis, &cache);
4137         }
4138         step2Items.AppendElement(Step2ItemData(
4139             {span, state, lineRange, minSize, minContent, maxContent, *iter}));
4140       } else {
4141         if (state & TrackSize::eFlexMaxSizing) {
4142           gridItem.mState[mAxis] |= ItemState::eIsFlexing;
4143         } else if (aConstraint == SizingConstraint::eNoConstraint &&
4144                    TrackSize::IsDefiniteMaxSizing(state) &&
4145                    (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize)) {
4146           gridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
4147         }
4148       }
4149     }
4150     MOZ_ASSERT(!(gridItem.mState[mAxis] & ItemState::eClampMarginBoxMinSize) ||
4151                    (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize),
4152                "clamping only applies to Automatic Minimum Size");
4153   }
4154 
4155   // Step 2.
4156   if (maxSpan) {
4157     auto fitContentClamper = [&aFunctions, aPercentageBasis](uint32_t aTrack,
4158                                                              nscoord aMinSize,
4159                                                              nscoord* aSize) {
4160       nscoord fitContentLimit = ::ResolveToDefiniteSize(
4161           aFunctions.MaxSizingFor(aTrack), aPercentageBasis);
4162       if (*aSize > fitContentLimit) {
4163         *aSize = std::max(aMinSize, fitContentLimit);
4164         return true;
4165       }
4166       return false;
4167     };
4168 
4169     // Sort the collected items on span length, shortest first.
4170     std::stable_sort(step2Items.begin(), step2Items.end(),
4171                      Step2ItemData::IsSpanLessThan);
4172 
4173     nsTArray<uint32_t> tracks(maxSpan);
4174     nsTArray<TrackSize> plan(mSizes.Length());
4175     plan.SetLength(mSizes.Length());
4176     nsTArray<TrackSize> itemPlan(mSizes.Length());
4177     itemPlan.SetLength(mSizes.Length());
4178     // Start / end iterator for items of the same span length:
4179     auto spanGroupStart = step2Items.begin();
4180     auto spanGroupEnd = spanGroupStart;
4181     const auto end = step2Items.end();
4182     for (; spanGroupStart != end; spanGroupStart = spanGroupEnd) {
4183       while (spanGroupEnd != end &&
4184              !Step2ItemData::IsSpanLessThan(*spanGroupStart, *spanGroupEnd)) {
4185         ++spanGroupEnd;
4186       }
4187 
4188       const uint32_t span = spanGroupStart->mSpan;
4189       bool updatedBase = false;  // Did we update any mBase in step 2.1 - 2.3?
4190       TrackSize::StateBits selector(TrackSize::eIntrinsicMinSizing);
4191       if (stateBitsPerSpan[span] & selector) {
4192         // Step 2.1 MinSize to intrinsic min-sizing.
4193         updatedBase =
4194             GrowSizeForSpanningItems<TrackSizingPhase::eIntrinsicMinimums>(
4195                 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
4196       }
4197 
4198       selector = contentBasedMinSelector;
4199       if (stateBitsPerSpan[span] & selector) {
4200         // Step 2.2 MinContentContribution to min-/max-content (and 'auto' when
4201         // sizing under a min-content constraint) min-sizing.
4202         updatedBase |=
4203             GrowSizeForSpanningItems<TrackSizingPhase::eContentBasedMinimums>(
4204                 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
4205       }
4206 
4207       selector = maxContentMinSelector;
4208       if (stateBitsPerSpan[span] & selector) {
4209         // Step 2.3 MaxContentContribution to max-content (and 'auto' when
4210         // sizing under a max-content constraint) min-sizing.
4211         updatedBase |=
4212             GrowSizeForSpanningItems<TrackSizingPhase::eMaxContentMinimums>(
4213                 spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
4214       }
4215 
4216       if (updatedBase) {
4217         // Step 2.4
4218         for (TrackSize& sz : mSizes) {
4219           if (sz.mBase > sz.mLimit) {
4220             sz.mLimit = sz.mBase;
4221           }
4222         }
4223       }
4224 
4225       selector = TrackSize::eIntrinsicMaxSizing;
4226       if (stateBitsPerSpan[span] & selector) {
4227         const bool willRunStep2_6 =
4228             stateBitsPerSpan[span] & TrackSize::eAutoOrMaxContentMaxSizing;
4229         // Step 2.5 MinSize to intrinsic max-sizing.
4230         GrowSizeForSpanningItems<TrackSizingPhase::eIntrinsicMaximums>(
4231             spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector,
4232             fitContentClamper, willRunStep2_6);
4233 
4234         if (willRunStep2_6) {
4235           // Step 2.6 MaxContentContribution to max-content max-sizing.
4236           selector = TrackSize::eAutoOrMaxContentMaxSizing;
4237           GrowSizeForSpanningItems<TrackSizingPhase::eMaxContentMaximums>(
4238               spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector,
4239               fitContentClamper);
4240         }
4241       }
4242     }
4243   }
4244 
4245   // Step 3.
4246   for (TrackSize& sz : mSizes) {
4247     if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
4248       sz.mLimit = sz.mBase;
4249     }
4250   }
4251 }
4252 
FindFrUnitSize(const LineRange & aRange,const nsTArray<uint32_t> & aFlexTracks,const TrackSizingFunctions & aFunctions,nscoord aSpaceToFill) const4253 float nsGridContainerFrame::Tracks::FindFrUnitSize(
4254     const LineRange& aRange, const nsTArray<uint32_t>& aFlexTracks,
4255     const TrackSizingFunctions& aFunctions, nscoord aSpaceToFill) const {
4256   MOZ_ASSERT(aSpaceToFill > 0 && !aFlexTracks.IsEmpty());
4257   float flexFactorSum = 0.0f;
4258   nscoord leftOverSpace = aSpaceToFill;
4259   for (uint32_t i = aRange.mStart, end = aRange.mEnd; i < end; ++i) {
4260     const TrackSize& sz = mSizes[i];
4261     if (sz.mState & TrackSize::eFlexMaxSizing) {
4262       flexFactorSum += aFunctions.MaxSizingFor(i).GetFlexFractionValue();
4263     } else {
4264       leftOverSpace -= sz.mBase;
4265       if (leftOverSpace <= 0) {
4266         return 0.0f;
4267       }
4268     }
4269   }
4270   bool restart;
4271   float hypotheticalFrSize;
4272   nsTArray<uint32_t> flexTracks(aFlexTracks);
4273   uint32_t numFlexTracks = flexTracks.Length();
4274   do {
4275     restart = false;
4276     hypotheticalFrSize = leftOverSpace / std::max(flexFactorSum, 1.0f);
4277     for (uint32_t i = 0, len = flexTracks.Length(); i < len; ++i) {
4278       uint32_t track = flexTracks[i];
4279       if (track == kAutoLine) {
4280         continue;  // Track marked as inflexible in a prev. iter of this loop.
4281       }
4282       float flexFactor = aFunctions.MaxSizingFor(track).GetFlexFractionValue();
4283       const nscoord base = mSizes[track].mBase;
4284       if (flexFactor * hypotheticalFrSize < base) {
4285         // 12.7.1.4: Treat this track as inflexible.
4286         flexTracks[i] = kAutoLine;
4287         flexFactorSum -= flexFactor;
4288         leftOverSpace -= base;
4289         --numFlexTracks;
4290         if (numFlexTracks == 0 || leftOverSpace <= 0) {
4291           return 0.0f;
4292         }
4293         restart = true;
4294         // break; XXX (bug 1176621 comment 16) measure which is more common
4295       }
4296     }
4297   } while (restart);
4298   return hypotheticalFrSize;
4299 }
4300 
FindUsedFlexFraction(GridReflowInput & aState,nsTArray<GridItemInfo> & aGridItems,const nsTArray<uint32_t> & aFlexTracks,const TrackSizingFunctions & aFunctions,nscoord aAvailableSize) const4301 float nsGridContainerFrame::Tracks::FindUsedFlexFraction(
4302     GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
4303     const nsTArray<uint32_t>& aFlexTracks,
4304     const TrackSizingFunctions& aFunctions, nscoord aAvailableSize) const {
4305   if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
4306     // Use all of the grid tracks and a 'space to fill' of the available space.
4307     const TranslatedLineRange range(0, mSizes.Length());
4308     return FindFrUnitSize(range, aFlexTracks, aFunctions, aAvailableSize);
4309   }
4310 
4311   // The used flex fraction is the maximum of:
4312   // ... each flexible track's base size divided by its flex factor (which is
4313   // floored at 1).
4314   float fr = 0.0f;
4315   for (uint32_t track : aFlexTracks) {
4316     float flexFactor = aFunctions.MaxSizingFor(track).GetFlexFractionValue();
4317     float possiblyDividedBaseSize = (flexFactor > 1.0f)
4318                                         ? mSizes[track].mBase / flexFactor
4319                                         : mSizes[track].mBase;
4320     fr = std::max(fr, possiblyDividedBaseSize);
4321   }
4322   WritingMode wm = aState.mWM;
4323   gfxContext* rc = &aState.mRenderingContext;
4324   CSSOrderAwareFrameIterator& iter = aState.mIter;
4325   iter.Reset();
4326   // ... the result of 'finding the size of an fr' for each item that spans
4327   // a flex track with its max-content contribution as 'space to fill'
4328   for (; !iter.AtEnd(); iter.Next()) {
4329     const GridItemInfo& item = aGridItems[iter.ItemIndex()];
4330     if (item.mState[mAxis] & ItemState::eIsFlexing) {
4331       // XXX optimize: bug 1194446
4332       auto pb = Some(aState.PercentageBasisFor(mAxis, item));
4333       nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis, pb,
4334                                                 nsLayoutUtils::PREF_ISIZE);
4335       const LineRange& range =
4336           mAxis == eLogicalAxisInline ? item.mArea.mCols : item.mArea.mRows;
4337       MOZ_ASSERT(range.Extent() >= 1);
4338       const auto spannedGaps = range.Extent() - 1;
4339       if (spannedGaps > 0) {
4340         spaceToFill -= mGridGap * spannedGaps;
4341       }
4342       if (spaceToFill <= 0) {
4343         continue;
4344       }
4345       // ... and all its spanned tracks as input.
4346       nsTArray<uint32_t> itemFlexTracks;
4347       for (uint32_t i = range.mStart, end = range.mEnd; i < end; ++i) {
4348         if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
4349           itemFlexTracks.AppendElement(i);
4350         }
4351       }
4352       float itemFr =
4353           FindFrUnitSize(range, itemFlexTracks, aFunctions, spaceToFill);
4354       fr = std::max(fr, itemFr);
4355     }
4356   }
4357   return fr;
4358 }
4359 
StretchFlexibleTracks(GridReflowInput & aState,nsTArray<GridItemInfo> & aGridItems,const TrackSizingFunctions & aFunctions,nscoord aAvailableSize)4360 void nsGridContainerFrame::Tracks::StretchFlexibleTracks(
4361     GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems,
4362     const TrackSizingFunctions& aFunctions, nscoord aAvailableSize) {
4363   if (aAvailableSize <= 0) {
4364     return;
4365   }
4366   nsTArray<uint32_t> flexTracks(mSizes.Length());
4367   for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
4368     if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
4369       flexTracks.AppendElement(i);
4370     }
4371   }
4372   if (flexTracks.IsEmpty()) {
4373     return;
4374   }
4375   nscoord minSize = 0;
4376   nscoord maxSize = NS_UNCONSTRAINEDSIZE;
4377   if (aState.mReflowInput) {
4378     auto* ri = aState.mReflowInput;
4379     minSize = mAxis == eLogicalAxisBlock ? ri->ComputedMinBSize()
4380                                          : ri->ComputedMinISize();
4381     maxSize = mAxis == eLogicalAxisBlock ? ri->ComputedMaxBSize()
4382                                          : ri->ComputedMaxISize();
4383   }
4384   Maybe<nsTArray<TrackSize>> origSizes;
4385   bool applyMinMax = (minSize != 0 || maxSize != NS_UNCONSTRAINEDSIZE) &&
4386                      aAvailableSize == NS_UNCONSTRAINEDSIZE;
4387   // We iterate twice at most.  The 2nd time if the grid size changed after
4388   // applying a min/max-size (can only occur if aAvailableSize is indefinite).
4389   while (true) {
4390     float fr = FindUsedFlexFraction(aState, aGridItems, flexTracks, aFunctions,
4391                                     aAvailableSize);
4392     if (fr != 0.0f) {
4393       for (uint32_t i : flexTracks) {
4394         float flexFactor = aFunctions.MaxSizingFor(i).GetFlexFractionValue();
4395         nscoord flexLength = NSToCoordRound(flexFactor * fr);
4396         nscoord& base = mSizes[i].mBase;
4397         if (flexLength > base) {
4398           if (applyMinMax && origSizes.isNothing()) {
4399             origSizes.emplace(mSizes);
4400           }
4401           base = flexLength;
4402         }
4403       }
4404     }
4405     if (applyMinMax) {
4406       applyMinMax = false;
4407       // https://drafts.csswg.org/css-grid/#algo-flex-tracks
4408       // "If using this flex fraction would cause the grid to be smaller than
4409       // the grid container’s min-width/height (or larger than the grid
4410       // container’s max-width/height), then redo this step, treating the free
4411       // space as definite [...]"
4412       nscoord newSize = 0;
4413       for (auto& sz : mSizes) {
4414         newSize += sz.mBase;
4415       }
4416       const auto sumOfGridGaps = SumOfGridGaps();
4417       newSize += sumOfGridGaps;
4418       if (newSize > maxSize) {
4419         aAvailableSize = maxSize;
4420       } else if (newSize < minSize) {
4421         aAvailableSize = minSize;
4422       }
4423       if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
4424         aAvailableSize = std::max(0, aAvailableSize - sumOfGridGaps);
4425         // Restart with the original track sizes and definite aAvailableSize.
4426         if (origSizes.isSome()) {
4427           mSizes = Move(*origSizes);
4428           origSizes.reset();
4429         }  // else, no mSizes[].mBase were changed above so it's still correct
4430         if (aAvailableSize == 0) {
4431           break;  // zero available size wouldn't change any sizes though...
4432         }
4433         continue;
4434       }
4435     }
4436     break;
4437   }
4438 }
4439 
AlignJustifyContent(const nsStylePosition * aStyle,WritingMode aWM,const LogicalSize & aContainerSize)4440 void nsGridContainerFrame::Tracks::AlignJustifyContent(
4441     const nsStylePosition* aStyle, WritingMode aWM,
4442     const LogicalSize& aContainerSize) {
4443   if (mSizes.IsEmpty()) {
4444     return;
4445   }
4446 
4447   const bool isAlign = mAxis == eLogicalAxisBlock;
4448   auto valueAndFallback =
4449       isAlign ? aStyle->mAlignContent : aStyle->mJustifyContent;
4450   bool overflowSafe;
4451   auto alignment =
4452       ::GetAlignJustifyValue(valueAndFallback, aWM, isAlign, &overflowSafe);
4453   if (alignment == NS_STYLE_ALIGN_NORMAL) {
4454     MOZ_ASSERT(valueAndFallback == NS_STYLE_ALIGN_NORMAL,
4455                "*-content:normal cannot be specified with explicit fallback");
4456     alignment = NS_STYLE_ALIGN_STRETCH;
4457     valueAndFallback = alignment;  // we may need a fallback for 'stretch' below
4458   }
4459 
4460   // Compute the free space and count auto-sized tracks.
4461   size_t numAutoTracks = 0;
4462   nscoord space;
4463   if (alignment != NS_STYLE_ALIGN_START) {
4464     nscoord trackSizeSum = 0;
4465     for (const TrackSize& sz : mSizes) {
4466       trackSizeSum += sz.mBase;
4467       if (sz.mState & TrackSize::eAutoMaxSizing) {
4468         ++numAutoTracks;
4469       }
4470     }
4471     nscoord cbSize =
4472         isAlign ? aContainerSize.BSize(aWM) : aContainerSize.ISize(aWM);
4473     space = cbSize - trackSizeSum - SumOfGridGaps();
4474     // Use the fallback value instead when applicable.
4475     if (space < 0 ||
4476         (alignment == NS_STYLE_ALIGN_SPACE_BETWEEN && mSizes.Length() == 1)) {
4477       auto fallback = ::GetAlignJustifyFallbackIfAny(valueAndFallback, aWM,
4478                                                      isAlign, &overflowSafe);
4479       if (fallback) {
4480         alignment = fallback;
4481       }
4482     }
4483     if (space == 0 || (space < 0 && overflowSafe)) {
4484       // XXX check that this makes sense also for [last ]baseline (bug 1151204).
4485       alignment = NS_STYLE_ALIGN_START;
4486     }
4487   }
4488 
4489   // Optimize the cases where we just need to set each track's position.
4490   nscoord pos = 0;
4491   bool distribute = true;
4492   switch (alignment) {
4493     case NS_STYLE_ALIGN_BASELINE:
4494     case NS_STYLE_ALIGN_LAST_BASELINE:
4495       NS_WARNING("NYI: 'first/last baseline' (bug 1151204)");  // XXX
4496       MOZ_FALLTHROUGH;
4497     case NS_STYLE_ALIGN_START:
4498       distribute = false;
4499       break;
4500     case NS_STYLE_ALIGN_END:
4501       pos = space;
4502       distribute = false;
4503       break;
4504     case NS_STYLE_ALIGN_CENTER:
4505       pos = space / 2;
4506       distribute = false;
4507       break;
4508     case NS_STYLE_ALIGN_STRETCH:
4509       distribute = numAutoTracks != 0;
4510       break;
4511   }
4512   if (!distribute) {
4513     for (TrackSize& sz : mSizes) {
4514       sz.mPosition = pos;
4515       pos += sz.mBase + mGridGap;
4516     }
4517     return;
4518   }
4519 
4520   // Distribute free space to/between tracks and set their position.
4521   MOZ_ASSERT(space > 0, "should've handled that on the fallback path above");
4522   nscoord between, roundingError;
4523   switch (alignment) {
4524     case NS_STYLE_ALIGN_STRETCH: {
4525       MOZ_ASSERT(numAutoTracks > 0, "we handled numAutoTracks == 0 above");
4526       nscoord spacePerTrack;
4527       roundingError = NSCoordDivRem(space, numAutoTracks, &spacePerTrack);
4528       for (TrackSize& sz : mSizes) {
4529         sz.mPosition = pos;
4530         if (!(sz.mState & TrackSize::eAutoMaxSizing)) {
4531           pos += sz.mBase + mGridGap;
4532           continue;
4533         }
4534         nscoord stretch = spacePerTrack;
4535         if (roundingError) {
4536           roundingError -= 1;
4537           stretch += 1;
4538         }
4539         nscoord newBase = sz.mBase + stretch;
4540         sz.mBase = newBase;
4541         pos += newBase + mGridGap;
4542       }
4543       MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
4544       return;
4545     }
4546     case NS_STYLE_ALIGN_SPACE_BETWEEN:
4547       MOZ_ASSERT(mSizes.Length() > 1, "should've used a fallback above");
4548       roundingError = NSCoordDivRem(space, mSizes.Length() - 1, &between);
4549       break;
4550     case NS_STYLE_ALIGN_SPACE_AROUND:
4551       roundingError = NSCoordDivRem(space, mSizes.Length(), &between);
4552       pos = between / 2;
4553       break;
4554     case NS_STYLE_ALIGN_SPACE_EVENLY:
4555       roundingError = NSCoordDivRem(space, mSizes.Length() + 1, &between);
4556       pos = between;
4557       break;
4558     default:
4559       MOZ_ASSERT_UNREACHABLE("unknown align-/justify-content value");
4560       between = 0;        // just to avoid a compiler warning
4561       roundingError = 0;  // just to avoid a compiler warning
4562   }
4563   between += mGridGap;
4564   for (TrackSize& sz : mSizes) {
4565     sz.mPosition = pos;
4566     nscoord spacing = between;
4567     if (roundingError) {
4568       roundingError -= 1;
4569       spacing += 1;
4570     }
4571     pos += sz.mBase + spacing;
4572   }
4573   MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
4574 }
4575 
BackComputedIntrinsicSize(const TrackSizingFunctions & aFunctions,const nsStyleCoord & aGridGap) const4576 nscoord nsGridContainerFrame::Tracks::BackComputedIntrinsicSize(
4577     const TrackSizingFunctions& aFunctions,
4578     const nsStyleCoord& aGridGap) const {
4579   // Sum up the current sizes (where percentage tracks were treated as 'auto')
4580   // in 'size'.
4581   nscoord size = 0;
4582   for (size_t i = 0, len = mSizes.Length(); i < len; ++i) {
4583     size += mSizes[i].mBase;
4584   }
4585 
4586   // Add grid-gap contributions to 'size' and calculate a 'percent' sum.
4587   float percent = 0.0f;
4588   size_t numTracks = mSizes.Length();
4589   if (numTracks > 1) {
4590     const size_t gridGapCount = numTracks - 1;
4591     nscoord gridGapLength;
4592     float gridGapPercent;
4593     if (::GetPercentSizeParts(aGridGap, &gridGapLength, &gridGapPercent)) {
4594       percent = gridGapCount * gridGapPercent;
4595     } else {
4596       gridGapLength = aGridGap.ToLength();
4597     }
4598     size += gridGapCount * gridGapLength;
4599   }
4600 
4601   return std::max(0, nsLayoutUtils::AddPercents(size, percent));
4602 }
4603 
ToPositionAndLength(const nsTArray<TrackSize> & aTrackSizes,nscoord * aPos,nscoord * aLength) const4604 void nsGridContainerFrame::LineRange::ToPositionAndLength(
4605     const nsTArray<TrackSize>& aTrackSizes, nscoord* aPos,
4606     nscoord* aLength) const {
4607   MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
4608              "expected a definite LineRange");
4609   MOZ_ASSERT(mStart < mEnd);
4610   nscoord startPos = aTrackSizes[mStart].mPosition;
4611   const TrackSize& sz = aTrackSizes[mEnd - 1];
4612   *aPos = startPos;
4613   *aLength = (sz.mPosition + sz.mBase) - startPos;
4614 }
4615 
ToLength(const nsTArray<TrackSize> & aTrackSizes) const4616 nscoord nsGridContainerFrame::LineRange::ToLength(
4617     const nsTArray<TrackSize>& aTrackSizes) const {
4618   MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
4619              "expected a definite LineRange");
4620   MOZ_ASSERT(mStart < mEnd);
4621   nscoord startPos = aTrackSizes[mStart].mPosition;
4622   const TrackSize& sz = aTrackSizes[mEnd - 1];
4623   return (sz.mPosition + sz.mBase) - startPos;
4624 }
4625 
ToPositionAndLengthForAbsPos(const Tracks & aTracks,nscoord aGridOrigin,nscoord * aPos,nscoord * aLength) const4626 void nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos(
4627     const Tracks& aTracks, nscoord aGridOrigin, nscoord* aPos,
4628     nscoord* aLength) const {
4629   // kAutoLine for abspos children contributes the corresponding edge
4630   // of the grid container's padding-box.
4631   if (mEnd == kAutoLine) {
4632     if (mStart == kAutoLine) {
4633       // done
4634     } else {
4635       const nscoord endPos = *aPos + *aLength;
4636       auto side = mStart == aTracks.mSizes.Length()
4637                       ? GridLineSide::eBeforeGridGap
4638                       : GridLineSide::eAfterGridGap;
4639       nscoord startPos = aTracks.GridLineEdge(mStart, side);
4640       *aPos = aGridOrigin + startPos;
4641       *aLength = std::max(endPos - *aPos, 0);
4642     }
4643   } else {
4644     if (mStart == kAutoLine) {
4645       auto side = mEnd == 0 ? GridLineSide::eAfterGridGap
4646                             : GridLineSide::eBeforeGridGap;
4647       nscoord endPos = aTracks.GridLineEdge(mEnd, side);
4648       *aLength = std::max(aGridOrigin + endPos, 0);
4649     } else if (MOZ_LIKELY(mStart != mEnd)) {
4650       nscoord pos;
4651       ToPositionAndLength(aTracks.mSizes, &pos, aLength);
4652       *aPos = aGridOrigin + pos;
4653     } else {
4654       // The grid area only covers removed 'auto-fit' tracks.
4655       nscoord pos = aTracks.GridLineEdge(mStart, GridLineSide::eBeforeGridGap);
4656       *aPos = aGridOrigin + pos;
4657       *aLength = nscoord(0);
4658     }
4659   }
4660 }
4661 
PercentageBasisFor(LogicalAxis aAxis,const GridItemInfo & aGridItem) const4662 LogicalSize nsGridContainerFrame::GridReflowInput::PercentageBasisFor(
4663     LogicalAxis aAxis, const GridItemInfo& aGridItem) const {
4664   auto wm = aGridItem.mFrame->GetWritingMode();
4665   if (aAxis == eLogicalAxisInline) {
4666     return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
4667   }
4668   // Note: for now, we only resolve transferred percentages to row sizing.
4669   // We may need to adjust these assertions once we implement bug 1300366.
4670   MOZ_ASSERT(mCols.mCanResolveLineRangeSize);
4671   MOZ_ASSERT(!mRows.mCanResolveLineRangeSize);
4672   nscoord colSize = aGridItem.mArea.mCols.ToLength(mCols.mSizes);
4673   nscoord rowSize = NS_UNCONSTRAINEDSIZE;
4674   return !wm.IsOrthogonalTo(mWM) ? LogicalSize(wm, colSize, rowSize)
4675                                  : LogicalSize(wm, rowSize, colSize);
4676 }
4677 
ContainingBlockFor(const GridArea & aArea) const4678 LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockFor(
4679     const GridArea& aArea) const {
4680   nscoord i, b, iSize, bSize;
4681   MOZ_ASSERT(aArea.mCols.Extent() > 0, "grid items cover at least one track");
4682   MOZ_ASSERT(aArea.mRows.Extent() > 0, "grid items cover at least one track");
4683   aArea.mCols.ToPositionAndLength(mCols.mSizes, &i, &iSize);
4684   aArea.mRows.ToPositionAndLength(mRows.mSizes, &b, &bSize);
4685   return LogicalRect(mWM, i, b, iSize, bSize);
4686 }
4687 
ContainingBlockForAbsPos(const GridArea & aArea,const LogicalPoint & aGridOrigin,const LogicalRect & aGridCB) const4688 LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockForAbsPos(
4689     const GridArea& aArea, const LogicalPoint& aGridOrigin,
4690     const LogicalRect& aGridCB) const {
4691   nscoord i = aGridCB.IStart(mWM);
4692   nscoord b = aGridCB.BStart(mWM);
4693   nscoord iSize = aGridCB.ISize(mWM);
4694   nscoord bSize = aGridCB.BSize(mWM);
4695   aArea.mCols.ToPositionAndLengthForAbsPos(mCols, aGridOrigin.I(mWM), &i,
4696                                            &iSize);
4697   aArea.mRows.ToPositionAndLengthForAbsPos(mRows, aGridOrigin.B(mWM), &b,
4698                                            &bSize);
4699   return LogicalRect(mWM, i, b, iSize, bSize);
4700 }
4701 
4702 /**
4703  * Return a Fragmentainer object if we have a fragmentainer frame in our
4704  * ancestor chain of containing block (CB) reflow states.  We'll only
4705  * continue traversing the ancestor chain as long as the CBs have
4706  * the same writing-mode and have overflow:visible.
4707  */
4708 Maybe<nsGridContainerFrame::Fragmentainer>
GetNearestFragmentainer(const GridReflowInput & aState) const4709 nsGridContainerFrame::GetNearestFragmentainer(
4710     const GridReflowInput& aState) const {
4711   Maybe<nsGridContainerFrame::Fragmentainer> data;
4712   const ReflowInput* gridRI = aState.mReflowInput;
4713   if (gridRI->AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
4714     return data;
4715   }
4716   WritingMode wm = aState.mWM;
4717   const ReflowInput* cbRI = gridRI->mCBReflowInput;
4718   for (; cbRI; cbRI = cbRI->mCBReflowInput) {
4719     nsIScrollableFrame* sf = do_QueryFrame(cbRI->mFrame);
4720     if (sf) {
4721       break;
4722     }
4723     if (wm.IsOrthogonalTo(cbRI->GetWritingMode())) {
4724       break;
4725     }
4726     LayoutFrameType frameType = cbRI->mFrame->Type();
4727     if ((frameType == LayoutFrameType::Canvas &&
4728          PresContext()->IsPaginated()) ||
4729         frameType == LayoutFrameType::ColumnSet) {
4730       data.emplace();
4731       data->mIsTopOfPage = gridRI->mFlags.mIsTopOfPage;
4732       data->mToFragmentainerEnd = aState.mFragBStart +
4733                                   gridRI->AvailableBSize() -
4734                                   aState.mBorderPadding.BStart(wm);
4735       const auto numRows = aState.mRows.mSizes.Length();
4736       data->mCanBreakAtStart =
4737           numRows > 0 && aState.mRows.mSizes[0].mPosition > 0;
4738       nscoord bSize = gridRI->ComputedBSize();
4739       data->mIsAutoBSize = bSize == NS_AUTOHEIGHT;
4740       if (data->mIsAutoBSize) {
4741         bSize = gridRI->ComputedMinBSize();
4742       } else {
4743         bSize = NS_CSS_MINMAX(bSize, gridRI->ComputedMinBSize(),
4744                               gridRI->ComputedMaxBSize());
4745       }
4746       nscoord gridEnd =
4747           aState.mRows.GridLineEdge(numRows, GridLineSide::eBeforeGridGap);
4748       data->mCanBreakAtEnd = bSize > gridEnd && bSize > aState.mFragBStart;
4749       break;
4750     }
4751   }
4752   return data;
4753 }
4754 
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)4755 void nsGridContainerFrame::ReflowInFlowChild(
4756     nsIFrame* aChild, const GridItemInfo* aGridItemInfo, nsSize aContainerSize,
4757     const Maybe<nscoord>& aStretchBSize, const Fragmentainer* aFragmentainer,
4758     const GridReflowInput& aState, const LogicalRect& aContentArea,
4759     ReflowOutput& aDesiredSize, nsReflowStatus& aStatus) {
4760   nsPresContext* pc = PresContext();
4761   nsStyleContext* containerSC = StyleContext();
4762   WritingMode wm = aState.mReflowInput->GetWritingMode();
4763   LogicalMargin pad(aState.mReflowInput->ComputedLogicalPadding());
4764   const LogicalPoint padStart(wm, pad.IStart(wm), pad.BStart(wm));
4765   const bool isGridItem = !!aGridItemInfo;
4766   MOZ_ASSERT(isGridItem == !aChild->IsPlaceholderFrame());
4767   LogicalRect cb(wm);
4768   WritingMode childWM = aChild->GetWritingMode();
4769   bool isConstrainedBSize = false;
4770   nscoord toFragmentainerEnd;
4771   // The part of the child's grid area that's in previous container fragments.
4772   nscoord consumedGridAreaBSize = 0;
4773   const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
4774   if (MOZ_LIKELY(isGridItem)) {
4775     MOZ_ASSERT(aGridItemInfo->mFrame == aChild);
4776     const GridArea& area = aGridItemInfo->mArea;
4777     MOZ_ASSERT(area.IsDefinite());
4778     cb = aState.ContainingBlockFor(area);
4779     isConstrainedBSize = aFragmentainer && !wm.IsOrthogonalTo(childWM);
4780     if (isConstrainedBSize) {
4781       // |gridAreaBOffset| is the offset of the child's grid area in this
4782       // container fragment (if negative, that distance is the child CB size
4783       // consumed in previous container fragments).  Note that cb.BStart
4784       // (initially) and aState.mFragBStart are in "global" grid coordinates
4785       // (like all track positions).
4786       nscoord gridAreaBOffset = cb.BStart(wm) - aState.mFragBStart;
4787       consumedGridAreaBSize = std::max(0, -gridAreaBOffset);
4788       cb.BStart(wm) = std::max(0, gridAreaBOffset);
4789       toFragmentainerEnd = aFragmentainer->mToFragmentainerEnd -
4790                            aState.mFragBStart - cb.BStart(wm);
4791       toFragmentainerEnd = std::max(toFragmentainerEnd, 0);
4792     }
4793     cb += aContentArea.Origin(wm);
4794     aState.mRows.AlignBaselineSubtree(*aGridItemInfo);
4795     aState.mCols.AlignBaselineSubtree(*aGridItemInfo);
4796     // Setup [align|justify]-content:[last ]baseline related frame properties.
4797     // These are added to the padding in SizeComputationInput::InitOffsets.
4798     // (a negative value signals the value is for 'last baseline' and should be
4799     //  added to the (logical) end padding)
4800     typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop;
4801     auto SetProp = [aGridItemInfo, aChild](LogicalAxis aGridAxis, Prop aProp) {
4802       auto state = aGridItemInfo->mState[aGridAxis];
4803       auto baselineAdjust = (state & ItemState::eContentBaseline)
4804                                 ? aGridItemInfo->mBaselineOffset[aGridAxis]
4805                                 : nscoord(0);
4806       if (baselineAdjust < nscoord(0)) {
4807         // This happens when the subtree overflows its track.
4808         // XXX spec issue? it's unclear how to handle this.
4809         baselineAdjust = nscoord(0);
4810       } else if (baselineAdjust > nscoord(0) &&
4811                  (state & ItemState::eLastBaseline)) {
4812         baselineAdjust = -baselineAdjust;
4813       }
4814       if (baselineAdjust != nscoord(0)) {
4815         aChild->SetProperty(aProp, baselineAdjust);
4816       } else {
4817         aChild->DeleteProperty(aProp);
4818       }
4819     };
4820     SetProp(eLogicalAxisBlock,
4821             isOrthogonal ? IBaselinePadProperty() : BBaselinePadProperty());
4822     SetProp(eLogicalAxisInline,
4823             isOrthogonal ? BBaselinePadProperty() : IBaselinePadProperty());
4824   } else {
4825     // By convention, for frames that perform CSS Box Alignment, we position
4826     // placeholder children at the start corner of their alignment container,
4827     // and in this case that's usually the grid's padding box.
4828     // ("Usually" - the exception is when the grid *also* forms the
4829     // abs.pos. containing block. In that case, the alignment container isn't
4830     // the padding box -- it's some grid area instead.  But that case doesn't
4831     // require any special handling here, because we handle it later using a
4832     // special flag (STATIC_POS_IS_CB_ORIGIN) which will make us ignore the
4833     // placeholder's position entirely.)
4834     cb = aContentArea - padStart;
4835     aChild->AddStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN);
4836   }
4837 
4838   LogicalSize reflowSize(cb.Size(wm));
4839   if (isConstrainedBSize) {
4840     reflowSize.BSize(wm) = toFragmentainerEnd;
4841   }
4842   LogicalSize childCBSize = reflowSize.ConvertTo(childWM, wm);
4843 
4844   // Setup the ClampMarginBoxMinSize reflow flags and property, if needed.
4845   uint32_t flags = 0;
4846   if (aGridItemInfo) {
4847     // Clamp during reflow if we're stretching in that axis.
4848     auto* pos = aChild->StylePosition();
4849     auto j = pos->UsedJustifySelf(StyleContext());
4850     auto a = pos->UsedAlignSelf(StyleContext());
4851     bool stretch[2];
4852     stretch[eLogicalAxisInline] =
4853         j == NS_STYLE_JUSTIFY_NORMAL || j == NS_STYLE_JUSTIFY_STRETCH;
4854     stretch[eLogicalAxisBlock] =
4855         a == NS_STYLE_ALIGN_NORMAL || a == NS_STYLE_ALIGN_STRETCH;
4856     auto childIAxis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
4857     if (stretch[childIAxis] &&
4858         aGridItemInfo->mState[childIAxis] & ItemState::eClampMarginBoxMinSize) {
4859       flags |= ReflowInput::I_CLAMP_MARGIN_BOX_MIN_SIZE;
4860     }
4861 
4862     auto childBAxis = GetOrthogonalAxis(childIAxis);
4863     if (stretch[childBAxis] &&
4864         aGridItemInfo->mState[childBAxis] & ItemState::eClampMarginBoxMinSize) {
4865       flags |= ReflowInput::B_CLAMP_MARGIN_BOX_MIN_SIZE;
4866       aChild->SetProperty(BClampMarginBoxMinSizeProperty(),
4867                           childCBSize.BSize(childWM));
4868     } else {
4869       aChild->DeleteProperty(BClampMarginBoxMinSizeProperty());
4870     }
4871 
4872     if ((aGridItemInfo->mState[childIAxis] & ItemState::eApplyAutoMinSize)) {
4873       flags |= ReflowInput::I_APPLY_AUTO_MIN_SIZE;
4874     }
4875   }
4876 
4877   if (!isConstrainedBSize) {
4878     childCBSize.BSize(childWM) = NS_UNCONSTRAINEDSIZE;
4879   }
4880   LogicalSize percentBasis(cb.Size(wm).ConvertTo(childWM, wm));
4881   ReflowInput childRI(pc, *aState.mReflowInput, aChild, childCBSize,
4882                       &percentBasis, flags);
4883   childRI.mFlags.mIsTopOfPage =
4884       aFragmentainer ? aFragmentainer->mIsTopOfPage : false;
4885 
4886   // Because we pass ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE, and the
4887   // previous reflow of the child might not have, set the child's
4888   // block-resize flag to true.
4889   // FIXME (perf): It would be faster to do this only if the previous
4890   // reflow of the child was a measuring reflow, and only if the child
4891   // does some of the things that are affected by
4892   // ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE.
4893   childRI.SetBResize(true);
4894 
4895   // A table-wrapper needs to propagate the CB size we give it to its
4896   // inner table frame later.  @see nsTableWrapperFrame::InitChildReflowInput.
4897   if (aChild->IsTableWrapperFrame()) {
4898     LogicalSize* cb =
4899         aChild->GetProperty(nsTableWrapperFrame::GridItemCBSizeProperty());
4900     if (!cb) {
4901       cb = new LogicalSize(childWM);
4902       aChild->SetProperty(nsTableWrapperFrame::GridItemCBSizeProperty(), cb);
4903     }
4904     *cb = percentBasis;
4905   }
4906 
4907   // If the child is stretching in its block axis, and we might be fragmenting
4908   // it in that axis, then setup a frame property to tell
4909   // nsBlockFrame::ComputeFinalSize the size.
4910   if (isConstrainedBSize && !wm.IsOrthogonalTo(childWM)) {
4911     bool stretch = false;
4912     if (!childRI.mStyleMargin->HasBlockAxisAuto(childWM) &&
4913         childRI.mStylePosition->BSize(childWM).GetUnit() == eStyleUnit_Auto) {
4914       auto blockAxisAlignment =
4915           childRI.mStylePosition->UsedAlignSelf(StyleContext());
4916       if (blockAxisAlignment == NS_STYLE_ALIGN_NORMAL ||
4917           blockAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
4918         stretch = true;
4919       }
4920     }
4921     if (stretch) {
4922       aChild->SetProperty(FragStretchBSizeProperty(), *aStretchBSize);
4923     } else {
4924       aChild->DeleteProperty(FragStretchBSizeProperty());
4925     }
4926   }
4927 
4928   // We need the width of the child before we can correctly convert
4929   // the writing-mode of its origin, so we reflow at (0, 0) using a dummy
4930   // aContainerSize, and then pass the correct position to FinishReflowChild.
4931   ReflowOutput childSize(childRI);
4932   const nsSize dummyContainerSize;
4933   ReflowChild(aChild, pc, childSize, childRI, childWM, LogicalPoint(childWM),
4934               dummyContainerSize, 0, aStatus);
4935   LogicalPoint childPos = cb.Origin(wm).ConvertTo(
4936       childWM, wm, aContainerSize - childSize.PhysicalSize());
4937   // Apply align/justify-self and reflow again if that affects the size.
4938   if (MOZ_LIKELY(isGridItem)) {
4939     LogicalSize size = childSize.Size(childWM);  // from the ReflowChild()
4940     if (aStatus.IsComplete()) {
4941       auto align = childRI.mStylePosition->UsedAlignSelf(containerSC);
4942       auto state = aGridItemInfo->mState[eLogicalAxisBlock];
4943       if (state & ItemState::eContentBaseline) {
4944         align = (state & ItemState::eFirstBaseline) ? NS_STYLE_ALIGN_SELF_START
4945                                                     : NS_STYLE_ALIGN_SELF_END;
4946       }
4947       nscoord cbsz = cb.BSize(wm) - consumedGridAreaBSize;
4948       AlignSelf(*aGridItemInfo, align, cbsz, wm, childRI, size, &childPos);
4949     }
4950     auto justify = childRI.mStylePosition->UsedJustifySelf(containerSC);
4951     auto state = aGridItemInfo->mState[eLogicalAxisInline];
4952     if (state & ItemState::eContentBaseline) {
4953       justify = (state & ItemState::eFirstBaseline)
4954                     ? NS_STYLE_JUSTIFY_SELF_START
4955                     : NS_STYLE_JUSTIFY_SELF_END;
4956     }
4957     nscoord cbsz = cb.ISize(wm);
4958     JustifySelf(*aGridItemInfo, justify, cbsz, wm, childRI, size, &childPos);
4959   }  // else, nsAbsoluteContainingBlock.cpp will handle align/justify-self.
4960 
4961   childRI.ApplyRelativePositioning(&childPos, aContainerSize);
4962   FinishReflowChild(aChild, pc, childSize, &childRI, childWM, childPos,
4963                     aContainerSize, 0);
4964   ConsiderChildOverflow(aDesiredSize.mOverflowAreas, aChild);
4965 }
4966 
ReflowInFragmentainer(GridReflowInput & aState,const LogicalRect & aContentArea,ReflowOutput & aDesiredSize,nsReflowStatus & aStatus,Fragmentainer & aFragmentainer,const nsSize & aContainerSize)4967 nscoord nsGridContainerFrame::ReflowInFragmentainer(
4968     GridReflowInput& aState, const LogicalRect& aContentArea,
4969     ReflowOutput& aDesiredSize, nsReflowStatus& aStatus,
4970     Fragmentainer& aFragmentainer, const nsSize& aContainerSize) {
4971   MOZ_ASSERT(aStatus.IsEmpty());
4972   MOZ_ASSERT(aState.mReflowInput);
4973 
4974   // Collect our grid items and sort them in row order.  Collect placeholders
4975   // and put them in a separate array.
4976   nsTArray<const GridItemInfo*> sortedItems(aState.mGridItems.Length());
4977   nsTArray<nsIFrame*> placeholders(aState.mAbsPosItems.Length());
4978   aState.mIter.Reset(CSSOrderAwareFrameIterator::eIncludeAll);
4979   for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
4980     nsIFrame* child = *aState.mIter;
4981     if (!child->IsPlaceholderFrame()) {
4982       const GridItemInfo* info = &aState.mGridItems[aState.mIter.ItemIndex()];
4983       sortedItems.AppendElement(info);
4984     } else {
4985       placeholders.AppendElement(child);
4986     }
4987   }
4988   // NOTE: no need to use stable_sort here, there are no dependencies on
4989   // having content order between items on the same row in the code below.
4990   std::sort(sortedItems.begin(), sortedItems.end(),
4991             GridItemInfo::IsStartRowLessThan);
4992 
4993   // Reflow our placeholder children; they must all be complete.
4994   for (auto child : placeholders) {
4995     nsReflowStatus childStatus;
4996     ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(),
4997                       &aFragmentainer, aState, aContentArea, aDesiredSize,
4998                       childStatus);
4999     MOZ_ASSERT(childStatus.IsComplete(),
5000                "nsPlaceholderFrame should never need to be fragmented");
5001   }
5002 
5003   // The available size for children - we'll set this to the edge of the last
5004   // row in most cases below, but for now use the full size.
5005   nscoord childAvailableSize = aFragmentainer.mToFragmentainerEnd;
5006   const uint32_t startRow = aState.mStartRow;
5007   const uint32_t numRows = aState.mRows.mSizes.Length();
5008   bool isBDBClone = aState.mReflowInput->mStyleBorder->mBoxDecorationBreak ==
5009                     StyleBoxDecorationBreak::Clone;
5010   nscoord bpBEnd = aState.mBorderPadding.BEnd(aState.mWM);
5011 
5012   // Set |endRow| to the first row that doesn't fit.
5013   uint32_t endRow = numRows;
5014   for (uint32_t row = startRow; row < numRows; ++row) {
5015     auto& sz = aState.mRows.mSizes[row];
5016     const nscoord bEnd = sz.mPosition + sz.mBase;
5017     nscoord remainingAvailableSize = childAvailableSize - bEnd;
5018     if (remainingAvailableSize < 0 ||
5019         (isBDBClone && remainingAvailableSize < bpBEnd)) {
5020       endRow = row;
5021       break;
5022     }
5023   }
5024 
5025   // Check for forced breaks on the items.
5026   const bool isTopOfPage = aFragmentainer.mIsTopOfPage;
5027   bool isForcedBreak = false;
5028   const bool avoidBreakInside = ShouldAvoidBreakInside(*aState.mReflowInput);
5029   for (const GridItemInfo* info : sortedItems) {
5030     uint32_t itemStartRow = info->mArea.mRows.mStart;
5031     if (itemStartRow == endRow) {
5032       break;
5033     }
5034     auto disp = info->mFrame->StyleDisplay();
5035     if (disp->mBreakBefore) {
5036       // Propagate break-before on the first row to the container unless we're
5037       // already at top-of-page.
5038       if ((itemStartRow == 0 && !isTopOfPage) || avoidBreakInside) {
5039         aStatus.SetInlineLineBreakBeforeAndReset();
5040         return aState.mFragBStart;
5041       }
5042       if ((itemStartRow > startRow ||
5043            (itemStartRow == startRow && !isTopOfPage)) &&
5044           itemStartRow < endRow) {
5045         endRow = itemStartRow;
5046         isForcedBreak = true;
5047         // reset any BREAK_AFTER we found on an earlier item
5048         aStatus.Reset();
5049         break;  // we're done since the items are sorted in row order
5050       }
5051     }
5052     uint32_t itemEndRow = info->mArea.mRows.mEnd;
5053     if (disp->mBreakAfter) {
5054       if (itemEndRow != numRows) {
5055         if (itemEndRow > startRow && itemEndRow < endRow) {
5056           endRow = itemEndRow;
5057           isForcedBreak = true;
5058           // No "break;" here since later items with break-after may have
5059           // a shorter span.
5060         }
5061       } else {
5062         // Propagate break-after on the last row to the container, we may still
5063         // find a break-before on this row though (and reset aStatus).
5064         aStatus.SetInlineLineBreakAfter();  // tentative
5065       }
5066     }
5067   }
5068 
5069   // Consume at least one row in each fragment until we have consumed them all.
5070   // Except for the first row if there's a break opportunity before it.
5071   if (startRow == endRow && startRow != numRows &&
5072       (startRow != 0 || !aFragmentainer.mCanBreakAtStart)) {
5073     ++endRow;
5074   }
5075 
5076   // Honor break-inside:avoid if we can't fit all rows.
5077   if (avoidBreakInside && endRow < numRows) {
5078     aStatus.SetInlineLineBreakBeforeAndReset();
5079     return aState.mFragBStart;
5080   }
5081 
5082   // Calculate the block-size including this fragment.
5083   nscoord bEndRow =
5084       aState.mRows.GridLineEdge(endRow, GridLineSide::eBeforeGridGap);
5085   nscoord bSize;
5086   if (aFragmentainer.mIsAutoBSize) {
5087     // We only apply min-bsize once all rows are complete (when bsize is auto).
5088     if (endRow < numRows) {
5089       bSize = bEndRow;
5090       auto clampedBSize = ClampToCSSMaxBSize(bSize, aState.mReflowInput);
5091       if (MOZ_UNLIKELY(clampedBSize != bSize)) {
5092         // We apply max-bsize in all fragments though.
5093         bSize = clampedBSize;
5094       } else if (!isBDBClone) {
5095         // The max-bsize won't make this fragment COMPLETE, so the block-end
5096         // border will be in a later fragment.
5097         bpBEnd = 0;
5098       }
5099     } else {
5100       bSize = NS_CSS_MINMAX(bEndRow, aState.mReflowInput->ComputedMinBSize(),
5101                             aState.mReflowInput->ComputedMaxBSize());
5102     }
5103   } else {
5104     bSize = NS_CSS_MINMAX(aState.mReflowInput->ComputedBSize(),
5105                           aState.mReflowInput->ComputedMinBSize(),
5106                           aState.mReflowInput->ComputedMaxBSize());
5107   }
5108 
5109   // Check for overflow and set aStatus INCOMPLETE if so.
5110   bool overflow = bSize + bpBEnd > childAvailableSize;
5111   if (overflow) {
5112     if (avoidBreakInside) {
5113       aStatus.SetInlineLineBreakBeforeAndReset();
5114       return aState.mFragBStart;
5115     }
5116     bool breakAfterLastRow = endRow == numRows && aFragmentainer.mCanBreakAtEnd;
5117     if (breakAfterLastRow) {
5118       MOZ_ASSERT(bEndRow < bSize, "bogus aFragmentainer.mCanBreakAtEnd");
5119       nscoord availableSize = childAvailableSize;
5120       if (isBDBClone) {
5121         availableSize -= bpBEnd;
5122       }
5123       // Pretend we have at least 1px available size, otherwise we'll never make
5124       // progress in consuming our bSize.
5125       availableSize =
5126           std::max(availableSize, aState.mFragBStart + AppUnitsPerCSSPixel());
5127       // Fill the fragmentainer, but not more than our desired block-size and
5128       // at least to the size of the last row (even if that overflows).
5129       nscoord newBSize = std::min(bSize, availableSize);
5130       newBSize = std::max(newBSize, bEndRow);
5131       // If it's just the border+padding that is overflowing and we have
5132       // box-decoration-break:clone then we are technically COMPLETE.  There's
5133       // no point in creating another zero-bsize fragment in this case.
5134       if (newBSize < bSize || !isBDBClone) {
5135         aStatus.SetIncomplete();
5136       }
5137       bSize = newBSize;
5138     } else if (bSize <= bEndRow && startRow + 1 < endRow) {
5139       if (endRow == numRows) {
5140         // We have more than one row in this fragment, so we can break before
5141         // the last row instead.
5142         --endRow;
5143         bEndRow =
5144             aState.mRows.GridLineEdge(endRow, GridLineSide::eBeforeGridGap);
5145         bSize = bEndRow;
5146         if (aFragmentainer.mIsAutoBSize) {
5147           bSize = ClampToCSSMaxBSize(bSize, aState.mReflowInput);
5148         }
5149       }
5150       aStatus.SetIncomplete();
5151     } else if (endRow < numRows) {
5152       bSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
5153     }  // else - no break opportunities.
5154   } else {
5155     // Even though our block-size fits we need to honor forced breaks, or if
5156     // a row doesn't fit in an auto-sized container (unless it's constrained
5157     // by a max-bsize which make us overflow-incomplete).
5158     if (endRow < numRows &&
5159         (isForcedBreak || (aFragmentainer.mIsAutoBSize && bEndRow == bSize))) {
5160       bSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
5161     }
5162   }
5163 
5164   // If we can't fit all rows then we're at least overflow-incomplete.
5165   if (endRow < numRows) {
5166     childAvailableSize = bEndRow;
5167     if (aStatus.IsComplete()) {
5168       aStatus.SetOverflowIncomplete();
5169       aStatus.SetNextInFlowNeedsReflow();
5170     }
5171   } else {
5172     // Children always have the full size of the rows in this fragment.
5173     childAvailableSize = std::max(childAvailableSize, bEndRow);
5174   }
5175 
5176   return ReflowRowsInFragmentainer(aState, aContentArea, aDesiredSize, aStatus,
5177                                    aFragmentainer, aContainerSize, sortedItems,
5178                                    startRow, endRow, bSize, childAvailableSize);
5179 }
5180 
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)5181 nscoord nsGridContainerFrame::ReflowRowsInFragmentainer(
5182     GridReflowInput& aState, const LogicalRect& aContentArea,
5183     ReflowOutput& aDesiredSize, nsReflowStatus& aStatus,
5184     Fragmentainer& aFragmentainer, const nsSize& aContainerSize,
5185     const nsTArray<const GridItemInfo*>& aSortedItems, uint32_t aStartRow,
5186     uint32_t aEndRow, nscoord aBSize, nscoord aAvailableSize) {
5187   FrameHashtable pushedItems;
5188   FrameHashtable incompleteItems;
5189   FrameHashtable overflowIncompleteItems;
5190   bool isBDBClone = aState.mReflowInput->mStyleBorder->mBoxDecorationBreak ==
5191                     StyleBoxDecorationBreak::Clone;
5192   bool didGrowRow = false;
5193   // As we walk across rows, we track whether the current row is at the top
5194   // of its grid-fragment, to help decide whether we can break before it. When
5195   // this function starts, our row is at the top of the current fragment if:
5196   //  - we're starting with a nonzero row (i.e. we're a continuation)
5197   // OR:
5198   //  - we're starting with the first row, & we're not allowed to break before
5199   //    it (which makes it effectively at the top of its grid-fragment).
5200   bool isRowTopOfPage = aStartRow != 0 || !aFragmentainer.mCanBreakAtStart;
5201   const bool isStartRowTopOfPage = isRowTopOfPage;
5202   // Save our full available size for later.
5203   const nscoord gridAvailableSize = aFragmentainer.mToFragmentainerEnd;
5204   // Propagate the constrained size to our children.
5205   aFragmentainer.mToFragmentainerEnd = aAvailableSize;
5206   // Reflow the items in row order up to |aEndRow| and push items after that.
5207   uint32_t row = 0;
5208   // |i| is intentionally signed, so we can set it to -1 to restart the loop.
5209   for (int32_t i = 0, len = aSortedItems.Length(); i < len; ++i) {
5210     const GridItemInfo* const info = aSortedItems[i];
5211     nsIFrame* child = info->mFrame;
5212     row = info->mArea.mRows.mStart;
5213     MOZ_ASSERT(child->GetPrevInFlow() ? row < aStartRow : row >= aStartRow,
5214                "unexpected child start row");
5215     if (row >= aEndRow) {
5216       pushedItems.PutEntry(child);
5217       continue;
5218     }
5219 
5220     bool rowCanGrow = false;
5221     nscoord maxRowSize = 0;
5222     if (row >= aStartRow) {
5223       if (row > aStartRow) {
5224         isRowTopOfPage = false;
5225       }
5226       // Can we grow this row?  Only consider span=1 items per spec...
5227       rowCanGrow = !didGrowRow && info->mArea.mRows.Extent() == 1;
5228       if (rowCanGrow) {
5229         auto& sz = aState.mRows.mSizes[row];
5230         // and only min-/max-content rows or flex rows in an auto-sized
5231         // container
5232         rowCanGrow = (sz.mState & TrackSize::eMinOrMaxContentMinSizing) ||
5233                      ((sz.mState & TrackSize::eFlexMaxSizing) &&
5234                       aFragmentainer.mIsAutoBSize);
5235         if (rowCanGrow) {
5236           if (isBDBClone) {
5237             maxRowSize =
5238                 gridAvailableSize - aState.mBorderPadding.BEnd(aState.mWM);
5239           } else {
5240             maxRowSize = gridAvailableSize;
5241           }
5242           maxRowSize -= sz.mPosition;
5243           // ...and only if there is space for it to grow.
5244           rowCanGrow = maxRowSize > sz.mBase;
5245         }
5246       }
5247     }
5248 
5249     // aFragmentainer.mIsTopOfPage is propagated to the child reflow state.
5250     // When it's false the child can request BREAK_BEFORE.  We intentionally
5251     // set it to false when the row is growable (as determined in CSS Grid
5252     // Fragmentation) and there is a non-zero space between it and the
5253     // fragmentainer end (that can be used to grow it).  If the child reports
5254     // a forced break in this case, we grow this row to fill the fragment and
5255     // restart the loop.  We also restart the loop with |aEndRow = row|
5256     // (but without growing any row) for a BREAK_BEFORE child if it spans
5257     // beyond the last row in this fragment.  This is to avoid fragmenting it.
5258     // We only restart the loop once.
5259     aFragmentainer.mIsTopOfPage = isRowTopOfPage && !rowCanGrow;
5260     nsReflowStatus childStatus;
5261     // Pass along how much to stretch this fragment, in case it's needed.
5262     nscoord bSize =
5263         aState.mRows.GridLineEdge(std::min(aEndRow, info->mArea.mRows.mEnd),
5264                                   GridLineSide::eBeforeGridGap) -
5265         aState.mRows.GridLineEdge(std::max(aStartRow, row),
5266                                   GridLineSide::eAfterGridGap);
5267     ReflowInFlowChild(child, info, aContainerSize, Some(bSize), &aFragmentainer,
5268                       aState, aContentArea, aDesiredSize, childStatus);
5269     MOZ_ASSERT(childStatus.IsInlineBreakBefore() ||
5270                    !childStatus.IsFullyComplete() || !child->GetNextInFlow(),
5271                "fully-complete reflow should destroy any NIFs");
5272 
5273     if (childStatus.IsInlineBreakBefore()) {
5274       MOZ_ASSERT(!child->GetPrevInFlow(),
5275                  "continuations should never report BREAK_BEFORE status");
5276       MOZ_ASSERT(!aFragmentainer.mIsTopOfPage,
5277                  "got IsInlineBreakBefore() at top of page");
5278       if (!didGrowRow) {
5279         if (rowCanGrow) {
5280           // Grow this row and restart with the next row as |aEndRow|.
5281           aState.mRows.ResizeRow(row, maxRowSize);
5282           if (aState.mSharedGridData) {
5283             aState.mSharedGridData->mRows.ResizeRow(row, maxRowSize);
5284           }
5285           didGrowRow = true;
5286           aEndRow = row + 1;  // growing this row makes the next one not fit
5287           i = -1;             // i == 0 after the next loop increment
5288           isRowTopOfPage = isStartRowTopOfPage;
5289           overflowIncompleteItems.Clear();
5290           incompleteItems.Clear();
5291           nscoord bEndRow =
5292               aState.mRows.GridLineEdge(aEndRow, GridLineSide::eBeforeGridGap);
5293           aFragmentainer.mToFragmentainerEnd = bEndRow;
5294           if (aFragmentainer.mIsAutoBSize) {
5295             aBSize = ClampToCSSMaxBSize(bEndRow, aState.mReflowInput, &aStatus);
5296           } else if (aStatus.IsIncomplete()) {
5297             aBSize = NS_CSS_MINMAX(aState.mReflowInput->ComputedBSize(),
5298                                    aState.mReflowInput->ComputedMinBSize(),
5299                                    aState.mReflowInput->ComputedMaxBSize());
5300             aBSize = std::min(bEndRow, aBSize);
5301           }
5302           continue;
5303         }
5304 
5305         if (!isRowTopOfPage) {
5306           // We can break before this row - restart with it as the new end row.
5307           aEndRow = row;
5308           aBSize =
5309               aState.mRows.GridLineEdge(aEndRow, GridLineSide::eBeforeGridGap);
5310           i = -1;  // i == 0 after the next loop increment
5311           isRowTopOfPage = isStartRowTopOfPage;
5312           overflowIncompleteItems.Clear();
5313           incompleteItems.Clear();
5314           aStatus.SetIncomplete();
5315           continue;
5316         }
5317         NS_ERROR("got BREAK_BEFORE at top-of-page");
5318         childStatus.Reset();
5319       } else {
5320         NS_ERROR("got BREAK_BEFORE again after growing the row?");
5321         childStatus.SetIncomplete();
5322       }
5323     } else if (childStatus.IsInlineBreakAfter()) {
5324       MOZ_ASSERT_UNREACHABLE("unexpected child reflow status");
5325     }
5326 
5327     if (childStatus.IsIncomplete()) {
5328       incompleteItems.PutEntry(child);
5329     } else if (!childStatus.IsFullyComplete()) {
5330       overflowIncompleteItems.PutEntry(child);
5331     }
5332   }
5333 
5334   // Record a break before |aEndRow|.
5335   aState.mNextFragmentStartRow = aEndRow;
5336   if (aEndRow < aState.mRows.mSizes.Length()) {
5337     aState.mRows.BreakBeforeRow(aEndRow);
5338     if (aState.mSharedGridData) {
5339       aState.mSharedGridData->mRows.BreakBeforeRow(aEndRow);
5340     }
5341   }
5342 
5343   if (!pushedItems.IsEmpty() || !incompleteItems.IsEmpty() ||
5344       !overflowIncompleteItems.IsEmpty()) {
5345     if (aStatus.IsComplete()) {
5346       aStatus.SetOverflowIncomplete();
5347       aStatus.SetNextInFlowNeedsReflow();
5348     }
5349     // Iterate the children in normal document order and append them (or a NIF)
5350     // to one of the following frame lists according to their status.
5351     nsFrameList pushedList;
5352     nsFrameList incompleteList;
5353     nsFrameList overflowIncompleteList;
5354     auto* pc = PresContext();
5355     auto* fc = pc->PresShell()->FrameConstructor();
5356     for (nsIFrame* child = GetChildList(kPrincipalList).FirstChild(); child;) {
5357       MOZ_ASSERT((pushedItems.Contains(child) ? 1 : 0) +
5358                          (incompleteItems.Contains(child) ? 1 : 0) +
5359                          (overflowIncompleteItems.Contains(child) ? 1 : 0) <=
5360                      1,
5361                  "child should only be in one of these sets");
5362       // Save the next-sibling so we can continue the loop if |child| is moved.
5363       nsIFrame* next = child->GetNextSibling();
5364       if (pushedItems.Contains(child)) {
5365         MOZ_ASSERT(child->GetParent() == this);
5366         StealFrame(child);
5367         pushedList.AppendFrame(nullptr, child);
5368       } else if (incompleteItems.Contains(child)) {
5369         nsIFrame* childNIF = child->GetNextInFlow();
5370         if (!childNIF) {
5371           childNIF = fc->CreateContinuingFrame(pc, child, this);
5372           incompleteList.AppendFrame(nullptr, childNIF);
5373         } else {
5374           auto parent =
5375               static_cast<nsGridContainerFrame*>(childNIF->GetParent());
5376           MOZ_ASSERT(parent != this || !mFrames.ContainsFrame(childNIF),
5377                      "child's NIF shouldn't be in the same principal list");
5378           // If child's existing NIF is an overflow container, convert it to an
5379           // actual NIF, since now |child| has non-overflow stuff to give it.
5380           // Or, if it's further away then our next-in-flow, then pull it up.
5381           if ((childNIF->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ||
5382               (parent != this && parent != GetNextInFlow())) {
5383             parent->StealFrame(childNIF);
5384             childNIF->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
5385             if (parent == this) {
5386               incompleteList.AppendFrame(nullptr, childNIF);
5387             } else {
5388               // If childNIF already lives on the next grid fragment, then we
5389               // don't need to reparent it, since we know it's destined to end
5390               // up there anyway.  Just move it to its parent's overflow list.
5391               if (parent == GetNextInFlow()) {
5392                 nsFrameList toMove(childNIF, childNIF);
5393                 parent->MergeSortedOverflow(toMove);
5394               } else {
5395                 ReparentFrame(childNIF, parent, this);
5396                 incompleteList.AppendFrame(nullptr, childNIF);
5397               }
5398             }
5399           }
5400         }
5401       } else if (overflowIncompleteItems.Contains(child)) {
5402         nsIFrame* childNIF = child->GetNextInFlow();
5403         if (!childNIF) {
5404           childNIF = fc->CreateContinuingFrame(pc, child, this);
5405           childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
5406           overflowIncompleteList.AppendFrame(nullptr, childNIF);
5407         } else {
5408           DebugOnly<nsGridContainerFrame*> lastParent = this;
5409           auto nif = static_cast<nsGridContainerFrame*>(GetNextInFlow());
5410           // If child has any non-overflow-container NIFs, convert them to
5411           // overflow containers, since that's all |child| needs now.
5412           while (childNIF &&
5413                  !childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
5414             auto parent =
5415                 static_cast<nsGridContainerFrame*>(childNIF->GetParent());
5416             parent->StealFrame(childNIF);
5417             childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
5418             if (parent == this) {
5419               overflowIncompleteList.AppendFrame(nullptr, childNIF);
5420             } else {
5421               if (!nif || parent == nif) {
5422                 nsFrameList toMove(childNIF, childNIF);
5423                 parent->MergeSortedExcessOverflowContainers(toMove);
5424               } else {
5425                 ReparentFrame(childNIF, parent, nif);
5426                 nsFrameList toMove(childNIF, childNIF);
5427                 nif->MergeSortedExcessOverflowContainers(toMove);
5428               }
5429               // We only need to reparent the first childNIF (or not at all if
5430               // its parent is our NIF).
5431               nif = nullptr;
5432             }
5433             lastParent = parent;
5434             childNIF = childNIF->GetNextInFlow();
5435           }
5436         }
5437       }
5438       child = next;
5439     }
5440 
5441     // Merge the results into our respective overflow child lists.
5442     if (!pushedList.IsEmpty()) {
5443       MergeSortedOverflow(pushedList);
5444       AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
5445       // NOTE since we messed with our child list here, we intentionally
5446       // make aState.mIter invalid to avoid any use of it after this point.
5447       aState.mIter.Invalidate();
5448     }
5449     if (!incompleteList.IsEmpty()) {
5450       MergeSortedOverflow(incompleteList);
5451       // NOTE since we messed with our child list here, we intentionally
5452       // make aState.mIter invalid to avoid any use of it after this point.
5453       aState.mIter.Invalidate();
5454     }
5455     if (!overflowIncompleteList.IsEmpty()) {
5456       MergeSortedExcessOverflowContainers(overflowIncompleteList);
5457     }
5458   }
5459   return aBSize;
5460 }
5461 
ReflowChildren(GridReflowInput & aState,const LogicalRect & aContentArea,ReflowOutput & aDesiredSize,nsReflowStatus & aStatus)5462 nscoord nsGridContainerFrame::ReflowChildren(GridReflowInput& aState,
5463                                              const LogicalRect& aContentArea,
5464                                              ReflowOutput& aDesiredSize,
5465                                              nsReflowStatus& aStatus) {
5466   MOZ_ASSERT(aState.mReflowInput);
5467   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
5468 
5469   nsOverflowAreas ocBounds;
5470   nsReflowStatus ocStatus;
5471   if (GetPrevInFlow()) {
5472     ReflowOverflowContainerChildren(PresContext(), *aState.mReflowInput,
5473                                     ocBounds, 0, ocStatus,
5474                                     MergeSortedFrameListsFor);
5475   }
5476 
5477   WritingMode wm = aState.mReflowInput->GetWritingMode();
5478   const nsSize containerSize =
5479       (aContentArea.Size(wm) + aState.mBorderPadding.Size(wm))
5480           .GetPhysicalSize(wm);
5481 
5482   nscoord bSize = aContentArea.BSize(wm);
5483   Maybe<Fragmentainer> fragmentainer = GetNearestFragmentainer(aState);
5484   if (MOZ_UNLIKELY(fragmentainer.isSome())) {
5485     aState.mInFragmentainer = true;
5486     bSize = ReflowInFragmentainer(aState, aContentArea, aDesiredSize, aStatus,
5487                                   *fragmentainer, containerSize);
5488   } else {
5489     aState.mIter.Reset(CSSOrderAwareFrameIterator::eIncludeAll);
5490     for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
5491       nsIFrame* child = *aState.mIter;
5492       const GridItemInfo* info = nullptr;
5493       if (!child->IsPlaceholderFrame()) {
5494         info = &aState.mGridItems[aState.mIter.ItemIndex()];
5495       }
5496       ReflowInFlowChild(*aState.mIter, info, containerSize, Nothing(), nullptr,
5497                         aState, aContentArea, aDesiredSize, aStatus);
5498       MOZ_ASSERT(aStatus.IsComplete(),
5499                  "child should be complete "
5500                  "in unconstrained reflow");
5501     }
5502   }
5503 
5504   // Merge overflow container bounds and status.
5505   aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
5506   aStatus.MergeCompletionStatusFrom(ocStatus);
5507 
5508   if (IsAbsoluteContainer()) {
5509     nsFrameList children(GetChildList(GetAbsoluteListID()));
5510     if (!children.IsEmpty()) {
5511       // 'gridOrigin' is the origin of the grid (the start of the first track),
5512       // with respect to the grid container's padding-box (CB).
5513       LogicalMargin pad(aState.mReflowInput->ComputedLogicalPadding());
5514       const LogicalPoint gridOrigin(wm, pad.IStart(wm), pad.BStart(wm));
5515       const LogicalRect gridCB(wm, 0, 0,
5516                                aContentArea.ISize(wm) + pad.IStartEnd(wm),
5517                                bSize + pad.BStartEnd(wm));
5518       const nsSize gridCBPhysicalSize = gridCB.Size(wm).GetPhysicalSize(wm);
5519       size_t i = 0;
5520       for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next(), ++i) {
5521         nsIFrame* child = e.get();
5522         MOZ_ASSERT(i < aState.mAbsPosItems.Length());
5523         MOZ_ASSERT(aState.mAbsPosItems[i].mFrame == child);
5524         GridArea& area = aState.mAbsPosItems[i].mArea;
5525         LogicalRect itemCB =
5526             aState.ContainingBlockForAbsPos(area, gridOrigin, gridCB);
5527         // nsAbsoluteContainingBlock::Reflow uses physical coordinates.
5528         nsRect* cb = child->GetProperty(GridItemContainingBlockRect());
5529         if (!cb) {
5530           cb = new nsRect;
5531           child->SetProperty(GridItemContainingBlockRect(), cb);
5532         }
5533         *cb = itemCB.GetPhysicalRect(wm, gridCBPhysicalSize);
5534       }
5535       // We pass a dummy rect as CB because each child has its own CB rect.
5536       // The eIsGridContainerCB flag tells nsAbsoluteContainingBlock::Reflow to
5537       // use those instead.
5538       nsRect dummyRect;
5539       AbsPosReflowFlags flags =
5540           AbsPosReflowFlags::eCBWidthAndHeightChanged;  // XXX could be
5541                                                         // optimized
5542       flags |= AbsPosReflowFlags::eConstrainHeight;
5543       flags |= AbsPosReflowFlags::eIsGridContainerCB;
5544       GetAbsoluteContainingBlock()->Reflow(
5545           this, PresContext(), *aState.mReflowInput, aStatus, dummyRect, flags,
5546           &aDesiredSize.mOverflowAreas);
5547     }
5548   }
5549   return bSize;
5550 }
5551 
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)5552 void nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
5553                                   ReflowOutput& aDesiredSize,
5554                                   const ReflowInput& aReflowInput,
5555                                   nsReflowStatus& aStatus) {
5556   MarkInReflow();
5557   DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
5558   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
5559   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
5560 
5561   if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) {
5562     return;
5563   }
5564 
5565   // First we gather child frames we should include in our reflow,
5566   // i.e. overflowed children from our prev-in-flow, and pushed first-in-flow
5567   // children (that might now fit). It's important to note that these children
5568   // can be in arbitrary order vis-a-vis the current children in our lists.
5569   // E.g. grid items in the document order: A, B, C may be placed in the rows
5570   // 3, 2, 1.  Assume each row goes in a separate grid container fragment,
5571   // and we reflow the second fragment.  Now if C (in fragment 1) overflows,
5572   // we can't just prepend it to our mFrames like we usually do because that
5573   // would violate the document order invariant that other code depends on.
5574   // Similarly if we pull up child A (from fragment 3) we can't just append
5575   // that for the same reason.  Instead, we must sort these children into
5576   // our child lists.  (The sorting is trivial given that both lists are
5577   // already fully sorted individually - it's just a merge.)
5578   //
5579   // The invariants that we maintain are that each grid container child list
5580   // is sorted in the normal document order at all times, but that children
5581   // in different grid container continuations may be in arbitrary order.
5582 
5583   auto prevInFlow = static_cast<nsGridContainerFrame*>(GetPrevInFlow());
5584   // Merge overflow frames from our prev-in-flow into our principal child list.
5585   if (prevInFlow) {
5586     AutoFrameListPtr overflow(aPresContext, prevInFlow->StealOverflowFrames());
5587     if (overflow) {
5588       ReparentFrames(*overflow, prevInFlow, this);
5589       ::MergeSortedFrameLists(mFrames, *overflow, GetContent());
5590 
5591       // Move trailing next-in-flows into our overflow list.
5592       nsFrameList continuations;
5593       for (nsIFrame* f = mFrames.FirstChild(); f;) {
5594         nsIFrame* next = f->GetNextSibling();
5595         nsIFrame* pif = f->GetPrevInFlow();
5596         if (pif && pif->GetParent() == this) {
5597           mFrames.RemoveFrame(f);
5598           continuations.AppendFrame(nullptr, f);
5599         }
5600         f = next;
5601       }
5602       MergeSortedOverflow(continuations);
5603 
5604       // Move trailing OC next-in-flows into our excess overflow containers
5605       // list.
5606       nsFrameList* overflowContainers =
5607           GetPropTableFrames(OverflowContainersProperty());
5608       if (overflowContainers) {
5609         nsFrameList moveToEOC;
5610         for (nsIFrame* f = overflowContainers->FirstChild(); f;) {
5611           nsIFrame* next = f->GetNextSibling();
5612           nsIFrame* pif = f->GetPrevInFlow();
5613           if (pif && pif->GetParent() == this) {
5614             overflowContainers->RemoveFrame(f);
5615             moveToEOC.AppendFrame(nullptr, f);
5616           }
5617           f = next;
5618         }
5619         if (overflowContainers->IsEmpty()) {
5620           DeleteProperty(OverflowContainersProperty());
5621         }
5622         MergeSortedExcessOverflowContainers(moveToEOC);
5623       }
5624     }
5625   }
5626 
5627   // Merge our own overflow frames into our principal child list,
5628   // except those that are a next-in-flow for one of our items.
5629   DebugOnly<bool> foundOwnPushedChild = false;
5630   {
5631     nsFrameList* ourOverflow = GetOverflowFrames();
5632     if (ourOverflow) {
5633       nsFrameList items;
5634       for (nsIFrame* f = ourOverflow->FirstChild(); f;) {
5635         nsIFrame* next = f->GetNextSibling();
5636         nsIFrame* pif = f->GetPrevInFlow();
5637         if (!pif || pif->GetParent() != this) {
5638           MOZ_ASSERT(f->GetParent() == this);
5639           ourOverflow->RemoveFrame(f);
5640           items.AppendFrame(nullptr, f);
5641           if (!pif) {
5642             foundOwnPushedChild = true;
5643           }
5644         }
5645         f = next;
5646       }
5647       ::MergeSortedFrameLists(mFrames, items, GetContent());
5648       if (ourOverflow->IsEmpty()) {
5649         DestroyOverflowList();
5650       }
5651     }
5652   }
5653 
5654   // Pull up any first-in-flow children we might have pushed.
5655   if (HasAnyStateBits(NS_STATE_GRID_DID_PUSH_ITEMS)) {
5656     RemoveStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
5657     nsFrameList items;
5658     auto nif = static_cast<nsGridContainerFrame*>(GetNextInFlow());
5659     auto firstNIF = nif;
5660     DebugOnly<bool> nifNeedPushedItem = false;
5661     while (nif) {
5662       nsFrameList nifItems;
5663       for (nsIFrame* nifChild = nif->GetChildList(kPrincipalList).FirstChild();
5664            nifChild;) {
5665         nsIFrame* next = nifChild->GetNextSibling();
5666         if (!nifChild->GetPrevInFlow()) {
5667           nif->StealFrame(nifChild);
5668           ReparentFrame(nifChild, nif, this);
5669           nifItems.AppendFrame(nullptr, nifChild);
5670           nifNeedPushedItem = false;
5671         }
5672         nifChild = next;
5673       }
5674       ::MergeSortedFrameLists(items, nifItems, GetContent());
5675 
5676       if (!nif->HasAnyStateBits(NS_STATE_GRID_DID_PUSH_ITEMS)) {
5677         MOZ_ASSERT(!nifNeedPushedItem || mDidPushItemsBitMayLie,
5678                    "NS_STATE_GRID_DID_PUSH_ITEMS lied");
5679         break;
5680       }
5681       nifNeedPushedItem = true;
5682 
5683       for (nsIFrame* nifChild = nif->GetChildList(kOverflowList).FirstChild();
5684            nifChild;) {
5685         nsIFrame* next = nifChild->GetNextSibling();
5686         if (!nifChild->GetPrevInFlow()) {
5687           nif->StealFrame(nifChild);
5688           ReparentFrame(nifChild, nif, this);
5689           nifItems.AppendFrame(nullptr, nifChild);
5690           nifNeedPushedItem = false;
5691         }
5692         nifChild = next;
5693       }
5694       ::MergeSortedFrameLists(items, nifItems, GetContent());
5695 
5696       nif->RemoveStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
5697       nif = static_cast<nsGridContainerFrame*>(nif->GetNextInFlow());
5698       MOZ_ASSERT(nif || !nifNeedPushedItem || mDidPushItemsBitMayLie,
5699                  "NS_STATE_GRID_DID_PUSH_ITEMS lied");
5700     }
5701 
5702     if (!items.IsEmpty()) {
5703       // Pull up the first next-in-flow of the pulled up items too, unless its
5704       // parent is our nif (to avoid leaving a hole there).
5705       nsFrameList childNIFs;
5706       nsFrameList childOCNIFs;
5707       for (auto child : items) {
5708         auto childNIF = child->GetNextInFlow();
5709         if (childNIF && childNIF->GetParent() != firstNIF) {
5710           auto parent = childNIF->GetParent();
5711           parent->StealFrame(childNIF);
5712           ReparentFrame(childNIF, parent, firstNIF);
5713           if ((childNIF->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
5714             childOCNIFs.AppendFrame(nullptr, childNIF);
5715           } else {
5716             childNIFs.AppendFrame(nullptr, childNIF);
5717           }
5718         }
5719       }
5720       // Merge items' NIFs into our NIF's respective overflow child lists.
5721       firstNIF->MergeSortedOverflow(childNIFs);
5722       firstNIF->MergeSortedExcessOverflowContainers(childOCNIFs);
5723     }
5724 
5725     MOZ_ASSERT(
5726         foundOwnPushedChild || !items.IsEmpty() || mDidPushItemsBitMayLie,
5727         "NS_STATE_GRID_DID_PUSH_ITEMS lied");
5728     ::MergeSortedFrameLists(mFrames, items, GetContent());
5729   }
5730 
5731   RenumberList();
5732 
5733 #ifdef DEBUG
5734   mDidPushItemsBitMayLie = false;
5735   SanityCheckGridItemsBeforeReflow();
5736 #endif  // DEBUG
5737 
5738   mBaseline[0][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
5739   mBaseline[0][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
5740   mBaseline[1][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
5741   mBaseline[1][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
5742 
5743   const nsStylePosition* stylePos = aReflowInput.mStylePosition;
5744   if (!prevInFlow) {
5745     InitImplicitNamedAreas(stylePos);
5746   }
5747   GridReflowInput gridReflowInput(this, aReflowInput);
5748   if (gridReflowInput.mIter.ItemsAreAlreadyInOrder()) {
5749     AddStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
5750   } else {
5751     RemoveStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
5752   }
5753   if (gridReflowInput.mIter.AtEnd()) {
5754     // We have no grid items, our parent should synthesize a baseline if needed.
5755     AddStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
5756   } else {
5757     RemoveStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
5758   }
5759   const nscoord computedBSize = aReflowInput.ComputedBSize();
5760   const nscoord computedISize = aReflowInput.ComputedISize();
5761   const WritingMode& wm = gridReflowInput.mWM;
5762   LogicalSize computedSize(wm, computedISize, computedBSize);
5763 
5764   nscoord consumedBSize = 0;
5765   nscoord bSize;
5766   if (!prevInFlow) {
5767     Grid grid;
5768     grid.PlaceGridItems(gridReflowInput, aReflowInput.ComputedMinSize(),
5769                         computedSize, aReflowInput.ComputedMaxSize());
5770 
5771     gridReflowInput.CalculateTrackSizes(grid, computedSize,
5772                                         SizingConstraint::eNoConstraint);
5773     bSize = computedSize.BSize(wm);
5774   } else {
5775     consumedBSize = ConsumedBSize(wm);
5776     gridReflowInput.InitializeForContinuation(this, consumedBSize);
5777     const uint32_t numRows = gridReflowInput.mRows.mSizes.Length();
5778     bSize = gridReflowInput.mRows.GridLineEdge(numRows,
5779                                                GridLineSide::eAfterGridGap);
5780   }
5781   if (computedBSize == NS_AUTOHEIGHT) {
5782     bSize = NS_CSS_MINMAX(bSize, aReflowInput.ComputedMinBSize(),
5783                           aReflowInput.ComputedMaxBSize());
5784   } else {
5785     bSize = computedBSize;
5786   }
5787   bSize = std::max(bSize - consumedBSize, 0);
5788   auto& bp = gridReflowInput.mBorderPadding;
5789   LogicalRect contentArea(wm, bp.IStart(wm), bp.BStart(wm), computedISize,
5790                           bSize);
5791 
5792   if (!prevInFlow) {
5793     // Apply 'align/justify-content' to the grid.
5794     // CalculateTrackSizes did the columns.
5795     gridReflowInput.mRows.AlignJustifyContent(stylePos, wm,
5796                                               contentArea.Size(wm));
5797   }
5798 
5799   bSize = ReflowChildren(gridReflowInput, contentArea, aDesiredSize, aStatus);
5800   bSize = std::max(bSize - consumedBSize, 0);
5801 
5802   // Skip our block-end border if we're INCOMPLETE.
5803   if (!aStatus.IsComplete() && !gridReflowInput.mSkipSides.BEnd() &&
5804       StyleBorder()->mBoxDecorationBreak != StyleBoxDecorationBreak::Clone) {
5805     bp.BEnd(wm) = nscoord(0);
5806   }
5807 
5808   LogicalSize desiredSize(wm, computedISize + bp.IStartEnd(wm),
5809                           bSize + bp.BStartEnd(wm));
5810   aDesiredSize.SetSize(wm, desiredSize);
5811   nsRect frameRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height());
5812   aDesiredSize.mOverflowAreas.UnionAllWith(frameRect);
5813 
5814   // Convert INCOMPLETE -> OVERFLOW_INCOMPLETE and zero bsize if we're an OC.
5815   if (HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
5816     if (!aStatus.IsComplete()) {
5817       aStatus.SetOverflowIncomplete();
5818       aStatus.SetNextInFlowNeedsReflow();
5819     }
5820     bSize = 0;
5821     desiredSize.BSize(wm) = bSize + bp.BStartEnd(wm);
5822     aDesiredSize.SetSize(wm, desiredSize);
5823   }
5824 
5825   if (!gridReflowInput.mInFragmentainer) {
5826     MOZ_ASSERT(gridReflowInput.mIter.IsValid());
5827     auto sz = frameRect.Size();
5828     CalculateBaselines(BaselineSet::eBoth, &gridReflowInput.mIter,
5829                        &gridReflowInput.mGridItems, gridReflowInput.mCols, 0,
5830                        gridReflowInput.mCols.mSizes.Length(), wm, sz,
5831                        bp.IStart(wm), bp.IEnd(wm), desiredSize.ISize(wm));
5832     CalculateBaselines(BaselineSet::eBoth, &gridReflowInput.mIter,
5833                        &gridReflowInput.mGridItems, gridReflowInput.mRows, 0,
5834                        gridReflowInput.mRows.mSizes.Length(), wm, sz,
5835                        bp.BStart(wm), bp.BEnd(wm), desiredSize.BSize(wm));
5836   } else {
5837     // Only compute 'first baseline' if this fragment contains the first track.
5838     // XXXmats maybe remove this condition? bug 1306499
5839     BaselineSet baselines = BaselineSet::eNone;
5840     if (gridReflowInput.mStartRow == 0 &&
5841         gridReflowInput.mStartRow != gridReflowInput.mNextFragmentStartRow) {
5842       baselines = BaselineSet::eFirst;
5843     }
5844     // Only compute 'last baseline' if this fragment contains the last track.
5845     // XXXmats maybe remove this condition? bug 1306499
5846     uint32_t len = gridReflowInput.mRows.mSizes.Length();
5847     if (gridReflowInput.mStartRow != len &&
5848         gridReflowInput.mNextFragmentStartRow == len) {
5849       baselines = BaselineSet(baselines | BaselineSet::eLast);
5850     }
5851     Maybe<CSSOrderAwareFrameIterator> iter;
5852     Maybe<nsTArray<GridItemInfo>> gridItems;
5853     if (baselines != BaselineSet::eNone) {
5854       // We need to create a new iterator and GridItemInfo array because we
5855       // might have pushed some children at this point.
5856       // Even if the gridReflowInput iterator is invalid we can reuse its
5857       // state about order to optimize initialization of the new iterator.
5858       // An ordered child list can't become unordered by pushing frames.
5859       // An unordered list can become ordered in a number of cases, but we
5860       // ignore that here and guess that the child list is still unordered.
5861       // XXX this is O(n^2) in the number of items in this fragment: bug 1306705
5862       using Filter = CSSOrderAwareFrameIterator::ChildFilter;
5863       using Order = CSSOrderAwareFrameIterator::OrderState;
5864       bool ordered = gridReflowInput.mIter.ItemsAreAlreadyInOrder();
5865       auto orderState = ordered ? Order::eKnownOrdered : Order::eKnownUnordered;
5866       iter.emplace(this, kPrincipalList, Filter::eSkipPlaceholders, orderState);
5867       gridItems.emplace();
5868       for (; !iter->AtEnd(); iter->Next()) {
5869         auto child = **iter;
5870         for (const auto& info : gridReflowInput.mGridItems) {
5871           if (info.mFrame == child) {
5872             gridItems->AppendElement(info);
5873           }
5874         }
5875       }
5876     }
5877     auto sz = frameRect.Size();
5878     CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
5879                        gridReflowInput.mCols, 0,
5880                        gridReflowInput.mCols.mSizes.Length(), wm, sz,
5881                        bp.IStart(wm), bp.IEnd(wm), desiredSize.ISize(wm));
5882     CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
5883                        gridReflowInput.mRows, gridReflowInput.mStartRow,
5884                        gridReflowInput.mNextFragmentStartRow, wm, sz,
5885                        bp.BStart(wm), bp.BEnd(wm), desiredSize.BSize(wm));
5886   }
5887 
5888   if (HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES)) {
5889     // This state bit will never be cleared, since reflow can be called
5890     // multiple times in fragmented grids, and it's challenging to scope
5891     // the bit to only that sequence of calls. This is relatively harmless
5892     // since this bit is only set by accessing a ChromeOnly property, and
5893     // therefore can't unduly slow down normal web browsing.
5894 
5895     // Now that we know column and row sizes and positions, set
5896     // the ComputedGridTrackInfo and related properties
5897 
5898     uint32_t colTrackCount = gridReflowInput.mCols.mSizes.Length();
5899     nsTArray<nscoord> colTrackPositions(colTrackCount);
5900     nsTArray<nscoord> colTrackSizes(colTrackCount);
5901     nsTArray<uint32_t> colTrackStates(colTrackCount);
5902     nsTArray<bool> colRemovedRepeatTracks(
5903         gridReflowInput.mColFunctions.mRemovedRepeatTracks);
5904     uint32_t col = 0;
5905     for (const TrackSize& sz : gridReflowInput.mCols.mSizes) {
5906       colTrackPositions.AppendElement(sz.mPosition);
5907       colTrackSizes.AppendElement(sz.mBase);
5908       bool isRepeat =
5909           ((col >= gridReflowInput.mColFunctions.mRepeatAutoStart) &&
5910            (col < gridReflowInput.mColFunctions.mRepeatAutoEnd));
5911       colTrackStates.AppendElement(
5912           isRepeat ? (uint32_t)mozilla::dom::GridTrackState::Repeat
5913                    : (uint32_t)mozilla::dom::GridTrackState::Static);
5914 
5915       col++;
5916     }
5917     ComputedGridTrackInfo* colInfo = new ComputedGridTrackInfo(
5918         gridReflowInput.mColFunctions.mExplicitGridOffset,
5919         gridReflowInput.mColFunctions.NumExplicitTracks(), 0, col,
5920         Move(colTrackPositions), Move(colTrackSizes), Move(colTrackStates),
5921         Move(colRemovedRepeatTracks),
5922         gridReflowInput.mColFunctions.mRepeatAutoStart);
5923     SetProperty(GridColTrackInfo(), colInfo);
5924 
5925     uint32_t rowTrackCount = gridReflowInput.mRows.mSizes.Length();
5926     nsTArray<nscoord> rowTrackPositions(rowTrackCount);
5927     nsTArray<nscoord> rowTrackSizes(rowTrackCount);
5928     nsTArray<uint32_t> rowTrackStates(rowTrackCount);
5929     nsTArray<bool> rowRemovedRepeatTracks(
5930         gridReflowInput.mRowFunctions.mRemovedRepeatTracks);
5931     uint32_t row = 0;
5932     for (const TrackSize& sz : gridReflowInput.mRows.mSizes) {
5933       rowTrackPositions.AppendElement(sz.mPosition);
5934       rowTrackSizes.AppendElement(sz.mBase);
5935       bool isRepeat =
5936           ((row >= gridReflowInput.mRowFunctions.mRepeatAutoStart) &&
5937            (row < gridReflowInput.mRowFunctions.mRepeatAutoEnd));
5938       rowTrackStates.AppendElement(
5939           isRepeat ? (uint32_t)mozilla::dom::GridTrackState::Repeat
5940                    : (uint32_t)mozilla::dom::GridTrackState::Static);
5941 
5942       row++;
5943     }
5944     // Row info has to accomodate fragmentation of the grid, which may happen in
5945     // later calls to Reflow. For now, presume that no more fragmentation will
5946     // occur.
5947     ComputedGridTrackInfo* rowInfo = new ComputedGridTrackInfo(
5948         gridReflowInput.mRowFunctions.mExplicitGridOffset,
5949         gridReflowInput.mRowFunctions.NumExplicitTracks(),
5950         gridReflowInput.mStartRow, row, Move(rowTrackPositions),
5951         Move(rowTrackSizes), Move(rowTrackStates), Move(rowRemovedRepeatTracks),
5952         gridReflowInput.mRowFunctions.mRepeatAutoStart);
5953     SetProperty(GridRowTrackInfo(), rowInfo);
5954 
5955     if (prevInFlow) {
5956       // This frame is fragmenting rows from a previous frame, so patch up
5957       // the prior GridRowTrackInfo with a new end row.
5958 
5959       // FIXME: This can be streamlined and/or removed when bug 1151204 lands.
5960 
5961       ComputedGridTrackInfo* priorRowInfo =
5962           prevInFlow->GetProperty(GridRowTrackInfo());
5963 
5964       // Adjust track positions based on the first track in this fragment.
5965       if (priorRowInfo->mPositions.Length() >
5966           priorRowInfo->mStartFragmentTrack) {
5967         nscoord delta =
5968             priorRowInfo->mPositions[priorRowInfo->mStartFragmentTrack];
5969         for (nscoord& pos : priorRowInfo->mPositions) {
5970           pos -= delta;
5971         }
5972       }
5973 
5974       ComputedGridTrackInfo* revisedPriorRowInfo = new ComputedGridTrackInfo(
5975           priorRowInfo->mNumLeadingImplicitTracks,
5976           priorRowInfo->mNumExplicitTracks, priorRowInfo->mStartFragmentTrack,
5977           gridReflowInput.mStartRow, Move(priorRowInfo->mPositions),
5978           Move(priorRowInfo->mSizes), Move(priorRowInfo->mStates),
5979           Move(priorRowInfo->mRemovedRepeatTracks),
5980           priorRowInfo->mRepeatFirstTrack);
5981       prevInFlow->SetProperty(GridRowTrackInfo(), revisedPriorRowInfo);
5982     }
5983 
5984     // Generate the line info properties. We need to provide the number of
5985     // repeat tracks produced in the reflow. Only explicit names are assigned
5986     // to lines here; the mozilla::dom::GridLines class will later extract
5987     // implicit names from grid areas and assign them to the appropriate lines.
5988 
5989     // Generate column lines first.
5990     uint32_t capacity = gridReflowInput.mCols.mSizes.Length();
5991     const nsStyleGridTemplate& gridColTemplate =
5992         gridReflowInput.mGridStyle->GridTemplateColumns();
5993     nsTArray<nsTArray<nsString>> columnLineNames(capacity);
5994     for (col = 0; col <= gridReflowInput.mCols.mSizes.Length(); col++) {
5995       // Offset col by the explicit grid offset, to get the original names.
5996       nsTArray<nsString> explicitNames =
5997           gridReflowInput.mCols.GetExplicitLineNamesAtIndex(
5998               gridColTemplate, gridReflowInput.mColFunctions,
5999               col - gridReflowInput.mColFunctions.mExplicitGridOffset);
6000 
6001       columnLineNames.AppendElement(explicitNames);
6002     }
6003     // Get the explicit names that follow a repeat auto declaration.
6004     nsTArray<nsString> colNamesFollowingRepeat;
6005     if (gridColTemplate.HasRepeatAuto()) {
6006       // The line name list after the repeatAutoIndex holds the line names
6007       // for the first explicit line after the repeat auto declaration.
6008       uint32_t repeatAutoEnd = gridColTemplate.mRepeatAutoIndex + 1;
6009       MOZ_ASSERT(repeatAutoEnd < gridColTemplate.mLineNameLists.Length());
6010       colNamesFollowingRepeat.AppendElements(
6011           gridColTemplate.mLineNameLists[repeatAutoEnd]);
6012     }
6013 
6014     ComputedGridLineInfo* columnLineInfo = new ComputedGridLineInfo(
6015         Move(columnLineNames), gridColTemplate.mRepeatAutoLineNameListBefore,
6016         gridColTemplate.mRepeatAutoLineNameListAfter,
6017         Move(colNamesFollowingRepeat));
6018     SetProperty(GridColumnLineInfo(), columnLineInfo);
6019 
6020     // Generate row lines next.
6021     capacity = gridReflowInput.mRows.mSizes.Length();
6022     const nsStyleGridTemplate& gridRowTemplate =
6023         gridReflowInput.mGridStyle->GridTemplateRows();
6024     nsTArray<nsTArray<nsString>> rowLineNames(capacity);
6025     for (row = 0; row <= gridReflowInput.mRows.mSizes.Length(); row++) {
6026       // Offset row by the explicit grid offset, to get the original names.
6027       nsTArray<nsString> explicitNames =
6028           gridReflowInput.mRows.GetExplicitLineNamesAtIndex(
6029               gridRowTemplate, gridReflowInput.mRowFunctions,
6030               row - gridReflowInput.mRowFunctions.mExplicitGridOffset);
6031 
6032       rowLineNames.AppendElement(explicitNames);
6033     }
6034     // Get the explicit names that follow a repeat auto declaration.
6035     nsTArray<nsString> rowNamesFollowingRepeat;
6036     if (gridRowTemplate.HasRepeatAuto()) {
6037       // The line name list after the repeatAutoIndex holds the line names
6038       // for the first explicit line after the repeat auto declaration.
6039       uint32_t repeatAutoEnd = gridRowTemplate.mRepeatAutoIndex + 1;
6040       MOZ_ASSERT(repeatAutoEnd < gridRowTemplate.mLineNameLists.Length());
6041       rowNamesFollowingRepeat.AppendElements(
6042           gridRowTemplate.mLineNameLists[repeatAutoEnd]);
6043     }
6044 
6045     ComputedGridLineInfo* rowLineInfo = new ComputedGridLineInfo(
6046         Move(rowLineNames), gridRowTemplate.mRepeatAutoLineNameListBefore,
6047         gridRowTemplate.mRepeatAutoLineNameListAfter,
6048         Move(rowNamesFollowingRepeat));
6049     SetProperty(GridRowLineInfo(), rowLineInfo);
6050 
6051     // Generate area info for explicit areas. Implicit areas are handled
6052     // elsewhere.
6053     if (gridReflowInput.mGridStyle->mGridTemplateAreas) {
6054       nsTArray<css::GridNamedArea>* areas = new nsTArray<css::GridNamedArea>(
6055           gridReflowInput.mGridStyle->mGridTemplateAreas->mNamedAreas);
6056       SetProperty(ExplicitNamedAreasProperty(), areas);
6057     } else {
6058       DeleteProperty(ExplicitNamedAreasProperty());
6059     }
6060   }
6061 
6062   if (!prevInFlow) {
6063     SharedGridData* sharedGridData = GetProperty(SharedGridData::Prop());
6064     if (!aStatus.IsFullyComplete()) {
6065       if (!sharedGridData) {
6066         sharedGridData = new SharedGridData;
6067         SetProperty(SharedGridData::Prop(), sharedGridData);
6068       }
6069       sharedGridData->mCols.mSizes.Clear();
6070       sharedGridData->mCols.mSizes.SwapElements(gridReflowInput.mCols.mSizes);
6071       sharedGridData->mCols.mContentBoxSize =
6072           gridReflowInput.mCols.mContentBoxSize;
6073       sharedGridData->mCols.mBaselineSubtreeAlign[0] =
6074           gridReflowInput.mCols.mBaselineSubtreeAlign[0];
6075       sharedGridData->mCols.mBaselineSubtreeAlign[1] =
6076           gridReflowInput.mCols.mBaselineSubtreeAlign[1];
6077       sharedGridData->mRows.mSizes.Clear();
6078       sharedGridData->mRows.mSizes.SwapElements(gridReflowInput.mRows.mSizes);
6079       // Save the original row grid sizes and gaps so we can restore them later
6080       // in GridReflowInput::Initialize for the continuations.
6081       auto& origRowData = sharedGridData->mOriginalRowData;
6082       origRowData.ClearAndRetainStorage();
6083       origRowData.SetCapacity(sharedGridData->mRows.mSizes.Length());
6084       nscoord prevTrackEnd = 0;
6085       for (auto& sz : sharedGridData->mRows.mSizes) {
6086         SharedGridData::RowData data = {sz.mBase, sz.mPosition - prevTrackEnd};
6087         origRowData.AppendElement(data);
6088         prevTrackEnd = sz.mPosition + sz.mBase;
6089       }
6090       sharedGridData->mRows.mContentBoxSize =
6091           gridReflowInput.mRows.mContentBoxSize;
6092       sharedGridData->mRows.mBaselineSubtreeAlign[0] =
6093           gridReflowInput.mRows.mBaselineSubtreeAlign[0];
6094       sharedGridData->mRows.mBaselineSubtreeAlign[1] =
6095           gridReflowInput.mRows.mBaselineSubtreeAlign[1];
6096       sharedGridData->mGridItems.Clear();
6097       sharedGridData->mGridItems.SwapElements(gridReflowInput.mGridItems);
6098       sharedGridData->mAbsPosItems.Clear();
6099       sharedGridData->mAbsPosItems.SwapElements(gridReflowInput.mAbsPosItems);
6100 
6101       sharedGridData->mGenerateComputedGridInfo =
6102           HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
6103     } else if (sharedGridData && !GetNextInFlow()) {
6104       DeleteProperty(SharedGridData::Prop());
6105     }
6106   }
6107 
6108   FinishAndStoreOverflow(&aDesiredSize);
6109   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
6110 }
6111 
IntrinsicISize(gfxContext * aRenderingContext,IntrinsicISizeType aType)6112 nscoord nsGridContainerFrame::IntrinsicISize(gfxContext* aRenderingContext,
6113                                              IntrinsicISizeType aType) {
6114   RenumberList();
6115 
6116   // Calculate the sum of column sizes under intrinsic sizing.
6117   // http://dev.w3.org/csswg/css-grid/#intrinsic-sizes
6118   GridReflowInput state(this, *aRenderingContext);
6119   InitImplicitNamedAreas(state.mGridStyle);  // XXX optimize
6120 
6121   auto GetDefiniteSizes = [](const nsStyleCoord& aMinCoord,
6122                              const nsStyleCoord& aSizeCoord,
6123                              const nsStyleCoord& aMaxCoord, nscoord* aMin,
6124                              nscoord* aSize, nscoord* aMax) {
6125     if (aMinCoord.ConvertsToLength()) {
6126       *aMin = aMinCoord.ToLength();
6127     }
6128     if (aMaxCoord.ConvertsToLength()) {
6129       *aMax = std::max(*aMin, aMaxCoord.ToLength());
6130     }
6131     if (aSizeCoord.ConvertsToLength()) {
6132       *aSize = Clamp(aSizeCoord.ToLength(), *aMin, *aMax);
6133     }
6134   };
6135   // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
6136   // https://drafts.csswg.org/css-grid/#auto-repeat
6137   // They're only used for auto-repeat so we skip computing them otherwise.
6138   LogicalSize min(state.mWM, 0, 0);
6139   LogicalSize sz(state.mWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
6140   LogicalSize max(state.mWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
6141   if (state.mColFunctions.mHasRepeatAuto) {
6142     GetDefiniteSizes(state.mGridStyle->MinISize(state.mWM),
6143                      state.mGridStyle->ISize(state.mWM),
6144                      state.mGridStyle->MaxISize(state.mWM),
6145                      &min.ISize(state.mWM), &sz.ISize(state.mWM),
6146                      &max.ISize(state.mWM));
6147   }
6148   if (state.mRowFunctions.mHasRepeatAuto &&
6149       !(state.mGridStyle->mGridAutoFlow & NS_STYLE_GRID_AUTO_FLOW_ROW)) {
6150     // Only 'grid-auto-flow:column' can create new implicit columns, so that's
6151     // the only case where our block-size can affect the number of columns.
6152     GetDefiniteSizes(state.mGridStyle->MinBSize(state.mWM),
6153                      state.mGridStyle->BSize(state.mWM),
6154                      state.mGridStyle->MaxBSize(state.mWM),
6155                      &min.BSize(state.mWM), &sz.BSize(state.mWM),
6156                      &max.BSize(state.mWM));
6157   }
6158 
6159   Grid grid;
6160   grid.PlaceGridItems(state, min, sz, max);  // XXX optimize
6161   if (grid.mGridColEnd == 0) {
6162     return 0;
6163   }
6164   state.mCols.Initialize(state.mColFunctions, state.mGridStyle->mGridColumnGap,
6165                          grid.mGridColEnd, NS_UNCONSTRAINEDSIZE);
6166   auto constraint = aType == nsLayoutUtils::MIN_ISIZE
6167                         ? SizingConstraint::eMinContent
6168                         : SizingConstraint::eMaxContent;
6169   state.mCols.CalculateSizes(state, state.mGridItems, state.mColFunctions,
6170                              NS_UNCONSTRAINEDSIZE, &GridArea::mCols,
6171                              constraint);
6172   return state.mCols.BackComputedIntrinsicSize(
6173       state.mColFunctions, state.mGridStyle->mGridColumnGap);
6174 }
6175 
GetMinISize(gfxContext * aRC)6176 nscoord nsGridContainerFrame::GetMinISize(gfxContext* aRC) {
6177   DISPLAY_MIN_WIDTH(this, mCachedMinISize);
6178   if (mCachedMinISize == NS_INTRINSIC_WIDTH_UNKNOWN) {
6179     mCachedMinISize = IntrinsicISize(aRC, nsLayoutUtils::MIN_ISIZE);
6180   }
6181   return mCachedMinISize;
6182 }
6183 
GetPrefISize(gfxContext * aRC)6184 nscoord nsGridContainerFrame::GetPrefISize(gfxContext* aRC) {
6185   DISPLAY_PREF_WIDTH(this, mCachedPrefISize);
6186   if (mCachedPrefISize == NS_INTRINSIC_WIDTH_UNKNOWN) {
6187     mCachedPrefISize = IntrinsicISize(aRC, nsLayoutUtils::PREF_ISIZE);
6188   }
6189   return mCachedPrefISize;
6190 }
6191 
MarkIntrinsicISizesDirty()6192 void nsGridContainerFrame::MarkIntrinsicISizesDirty() {
6193   mCachedMinISize = NS_INTRINSIC_WIDTH_UNKNOWN;
6194   mCachedPrefISize = NS_INTRINSIC_WIDTH_UNKNOWN;
6195   mBaseline[0][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
6196   mBaseline[0][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
6197   mBaseline[1][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
6198   mBaseline[1][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
6199   nsContainerFrame::MarkIntrinsicISizesDirty();
6200 }
6201 
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)6202 void nsGridContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
6203                                             const nsDisplayListSet& aLists) {
6204   DisplayBorderBackgroundOutline(aBuilder, aLists);
6205   if (GetPrevInFlow()) {
6206     DisplayOverflowContainers(aBuilder, aLists);
6207   }
6208 
6209   // Our children are all grid-level boxes, which behave the same as
6210   // inline-blocks in painting, so their borders/backgrounds all go on
6211   // the BlockBorderBackgrounds list.
6212   typedef CSSOrderAwareFrameIterator::OrderState OrderState;
6213   OrderState order =
6214       HasAnyStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
6215           ? OrderState::eKnownOrdered
6216           : OrderState::eKnownUnordered;
6217   CSSOrderAwareFrameIterator iter(
6218       this, kPrincipalList, CSSOrderAwareFrameIterator::eIncludeAll, order);
6219   for (; !iter.AtEnd(); iter.Next()) {
6220     nsIFrame* child = *iter;
6221     BuildDisplayListForChild(aBuilder, child, aLists,
6222                              ::GetDisplayFlagsForGridItem(child));
6223   }
6224 }
6225 
DrainSelfOverflowList()6226 bool nsGridContainerFrame::DrainSelfOverflowList() {
6227   // Unlike nsContainerFrame::DrainSelfOverflowList we need to merge these lists
6228   // so that the resulting mFrames is in document content order.
6229   // NOTE: nsContainerFrame::AppendFrames/InsertFrames calls this method.
6230   AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
6231   if (overflowFrames) {
6232     ::MergeSortedFrameLists(mFrames, *overflowFrames, GetContent());
6233     return true;
6234   }
6235   return false;
6236 }
6237 
AppendFrames(ChildListID aListID,nsFrameList & aFrameList)6238 void nsGridContainerFrame::AppendFrames(ChildListID aListID,
6239                                         nsFrameList& aFrameList) {
6240   NoteNewChildren(aListID, aFrameList);
6241   nsContainerFrame::AppendFrames(aListID, aFrameList);
6242 }
6243 
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,nsFrameList & aFrameList)6244 void nsGridContainerFrame::InsertFrames(ChildListID aListID,
6245                                         nsIFrame* aPrevFrame,
6246                                         nsFrameList& aFrameList) {
6247   NoteNewChildren(aListID, aFrameList);
6248   nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
6249 }
6250 
RemoveFrame(ChildListID aListID,nsIFrame * aOldFrame)6251 void nsGridContainerFrame::RemoveFrame(ChildListID aListID,
6252                                        nsIFrame* aOldFrame) {
6253 #ifdef DEBUG
6254   ChildListIDs supportedLists =
6255       kAbsoluteList | kFixedList | kPrincipalList | kNoReflowPrincipalList;
6256   // We don't handle the kBackdropList frames in any way, but it only contains
6257   // a placeholder for ::backdrop which is OK to not reflow (for now anyway).
6258   supportedLists |= kBackdropList;
6259   MOZ_ASSERT(supportedLists.Contains(aListID), "unexpected child list");
6260 
6261   // Note that kPrincipalList doesn't mean aOldFrame must be on that list.
6262   // It can also be on kOverflowList, in which case it might be a pushed
6263   // item, and if it's the only pushed item our DID_PUSH_ITEMS bit will lie.
6264   if (aListID == kPrincipalList && !aOldFrame->GetPrevInFlow()) {
6265     // Since the bit may lie, set the mDidPushItemsBitMayLie value to true for
6266     // ourself and for all our contiguous previous-in-flow
6267     // nsGridContainerFrames.
6268     nsGridContainerFrame* frameThatMayLie = this;
6269     do {
6270       frameThatMayLie->mDidPushItemsBitMayLie = true;
6271       frameThatMayLie =
6272           static_cast<nsGridContainerFrame*>(frameThatMayLie->GetPrevInFlow());
6273     } while (frameThatMayLie);
6274   }
6275 #endif
6276 
6277   nsContainerFrame::RemoveFrame(aListID, aOldFrame);
6278 }
6279 
CSSAlignmentForAbsPosChild(const ReflowInput & aChildRI,LogicalAxis aLogicalAxis) const6280 uint16_t nsGridContainerFrame::CSSAlignmentForAbsPosChild(
6281     const ReflowInput& aChildRI, LogicalAxis aLogicalAxis) const {
6282   MOZ_ASSERT(aChildRI.mFrame->IsAbsolutelyPositioned(),
6283              "This method should only be called for abspos children");
6284 
6285   uint16_t alignment =
6286       (aLogicalAxis == eLogicalAxisInline)
6287           ? aChildRI.mStylePosition->UsedJustifySelf(StyleContext())
6288           : aChildRI.mStylePosition->UsedAlignSelf(StyleContext());
6289 
6290   // XXX strip off <overflow-position> bits until we implement it
6291   // (bug 1311892)
6292   alignment &= ~NS_STYLE_ALIGN_FLAG_BITS;
6293 
6294   if (alignment == NS_STYLE_ALIGN_NORMAL) {
6295     // "the 'normal' keyword behaves as 'start' on replaced
6296     // absolutely-positioned boxes, and behaves as 'stretch' on all other
6297     // absolutely-positioned boxes."
6298     // https://drafts.csswg.org/css-align/#align-abspos
6299     // https://drafts.csswg.org/css-align/#justify-abspos
6300     alignment = aChildRI.mFrame->IsFrameOfType(nsIFrame::eReplaced)
6301                     ? NS_STYLE_ALIGN_START
6302                     : NS_STYLE_ALIGN_STRETCH;
6303   } else if (alignment == NS_STYLE_ALIGN_FLEX_START) {
6304     alignment = NS_STYLE_ALIGN_START;
6305   } else if (alignment == NS_STYLE_ALIGN_FLEX_END) {
6306     alignment = NS_STYLE_ALIGN_END;
6307   } else if (alignment == NS_STYLE_ALIGN_LEFT ||
6308              alignment == NS_STYLE_ALIGN_RIGHT) {
6309     if (aLogicalAxis == eLogicalAxisInline) {
6310       const bool isLeft = (alignment == NS_STYLE_ALIGN_LEFT);
6311       WritingMode wm = GetWritingMode();
6312       alignment = (isLeft == wm.IsBidiLTR()) ? NS_STYLE_ALIGN_START
6313                                              : NS_STYLE_ALIGN_END;
6314     } else {
6315       alignment = NS_STYLE_ALIGN_START;
6316     }
6317   } else if (alignment == NS_STYLE_ALIGN_BASELINE) {
6318     alignment = NS_STYLE_ALIGN_START;
6319   } else if (alignment == NS_STYLE_ALIGN_LAST_BASELINE) {
6320     alignment = NS_STYLE_ALIGN_END;
6321   }
6322 
6323   return alignment;
6324 }
6325 
SynthesizeBaseline(const FindItemInGridOrderResult & aGridOrderItem,LogicalAxis aAxis,BaselineSharingGroup aGroup,const nsSize & aCBPhysicalSize,nscoord aCBSize,WritingMode aCBWM)6326 nscoord nsGridContainerFrame::SynthesizeBaseline(
6327     const FindItemInGridOrderResult& aGridOrderItem, LogicalAxis aAxis,
6328     BaselineSharingGroup aGroup, const nsSize& aCBPhysicalSize, nscoord aCBSize,
6329     WritingMode aCBWM) {
6330   if (MOZ_UNLIKELY(!aGridOrderItem.mItem)) {
6331     // No item in this fragment - synthesize a baseline from our border-box.
6332     return ::SynthesizeBaselineFromBorderBox(aGroup, aCBWM, aCBSize);
6333   }
6334   auto GetBBaseline = [](BaselineSharingGroup aGroup, WritingMode aWM,
6335                          const nsIFrame* aFrame, nscoord* aBaseline) {
6336     return aGroup == BaselineSharingGroup::eFirst
6337                ? nsLayoutUtils::GetFirstLineBaseline(aWM, aFrame, aBaseline)
6338                : nsLayoutUtils::GetLastLineBaseline(aWM, aFrame, aBaseline);
6339   };
6340   nsIFrame* child = aGridOrderItem.mItem->mFrame;
6341   nsGridContainerFrame* grid = do_QueryFrame(child);
6342   auto childWM = child->GetWritingMode();
6343   bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
6344   nscoord baseline;
6345   nscoord start;
6346   nscoord size;
6347   if (aAxis == eLogicalAxisBlock) {
6348     start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM);
6349     size = child->BSize(aCBWM);
6350     if (grid && aGridOrderItem.mIsInEdgeTrack) {
6351       isOrthogonal ? grid->GetIBaseline(aGroup, &baseline)
6352                    : grid->GetBBaseline(aGroup, &baseline);
6353     } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack) {
6354       baseline =
6355           child->BaselineBOffset(childWM, aGroup, AlignmentContext::eGrid);
6356     } else {
6357       baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
6358     }
6359   } else {
6360     start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).I(aCBWM);
6361     size = child->ISize(aCBWM);
6362     if (grid && aGridOrderItem.mIsInEdgeTrack) {
6363       isOrthogonal ? grid->GetBBaseline(aGroup, &baseline)
6364                    : grid->GetIBaseline(aGroup, &baseline);
6365     } else if (isOrthogonal && aGridOrderItem.mIsInEdgeTrack &&
6366                GetBBaseline(aGroup, childWM, child, &baseline)) {
6367       if (aGroup == BaselineSharingGroup::eLast) {
6368         baseline = size - baseline;  // convert to distance from border-box end
6369       }
6370     } else {
6371       baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
6372     }
6373   }
6374   return aGroup == BaselineSharingGroup::eFirst
6375              ? start + baseline
6376              : aCBSize - start - size + baseline;
6377 }
6378 
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)6379 void nsGridContainerFrame::CalculateBaselines(
6380     BaselineSet aBaselineSet, CSSOrderAwareFrameIterator* aIter,
6381     const nsTArray<GridItemInfo>* aGridItems, const Tracks& aTracks,
6382     uint32_t aFragmentStartTrack, uint32_t aFirstExcludedTrack, WritingMode aWM,
6383     const nsSize& aCBPhysicalSize, nscoord aCBBorderPaddingStart,
6384     nscoord aCBBorderPaddingEnd, nscoord aCBSize) {
6385   const auto axis = aTracks.mAxis;
6386   auto firstBaseline = aTracks.mBaseline[BaselineSharingGroup::eFirst];
6387   if (!(aBaselineSet & BaselineSet::eFirst)) {
6388     mBaseline[axis][BaselineSharingGroup::eFirst] =
6389         ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::eFirst, aWM,
6390                                           aCBSize);
6391   } else if (firstBaseline == NS_INTRINSIC_WIDTH_UNKNOWN) {
6392     FindItemInGridOrderResult gridOrderFirstItem = FindFirstItemInGridOrder(
6393         *aIter, *aGridItems,
6394         axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols,
6395         axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows,
6396         aFragmentStartTrack);
6397     mBaseline[axis][BaselineSharingGroup::eFirst] = SynthesizeBaseline(
6398         gridOrderFirstItem, axis, BaselineSharingGroup::eFirst, aCBPhysicalSize,
6399         aCBSize, aWM);
6400   } else {
6401     // We have a 'first baseline' group in the start track in this fragment.
6402     // Convert it from track to grid container border-box coordinates.
6403     MOZ_ASSERT(!aGridItems->IsEmpty());
6404     nscoord gapBeforeStartTrack =
6405         aFragmentStartTrack == 0
6406             ? aTracks.GridLineEdge(aFragmentStartTrack,
6407                                    GridLineSide::eAfterGridGap)
6408             : nscoord(0);  // no content gap at start of fragment
6409     mBaseline[axis][BaselineSharingGroup::eFirst] =
6410         aCBBorderPaddingStart + gapBeforeStartTrack + firstBaseline;
6411   }
6412 
6413   auto lastBaseline = aTracks.mBaseline[BaselineSharingGroup::eLast];
6414   if (!(aBaselineSet & BaselineSet::eLast)) {
6415     mBaseline[axis][BaselineSharingGroup::eLast] =
6416         ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::eLast, aWM,
6417                                           aCBSize);
6418   } else if (lastBaseline == NS_INTRINSIC_WIDTH_UNKNOWN) {
6419     // For finding items for the 'last baseline' we need to create a reverse
6420     // iterator ('aIter' is the forward iterator from the GridReflowInput).
6421     using Iter = ReverseCSSOrderAwareFrameIterator;
6422     auto orderState = aIter->ItemsAreAlreadyInOrder()
6423                           ? Iter::OrderState::eKnownOrdered
6424                           : Iter::OrderState::eKnownUnordered;
6425     Iter iter(this, kPrincipalList, Iter::ChildFilter::eSkipPlaceholders,
6426               orderState);
6427     iter.SetItemCount(aGridItems->Length());
6428     FindItemInGridOrderResult gridOrderLastItem = FindLastItemInGridOrder(
6429         iter, *aGridItems,
6430         axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols,
6431         axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows,
6432         aFragmentStartTrack, aFirstExcludedTrack);
6433     mBaseline[axis][BaselineSharingGroup::eLast] =
6434         SynthesizeBaseline(gridOrderLastItem, axis, BaselineSharingGroup::eLast,
6435                            aCBPhysicalSize, aCBSize, aWM);
6436   } else {
6437     // We have a 'last baseline' group in the end track in this fragment.
6438     // Convert it from track to grid container border-box coordinates.
6439     MOZ_ASSERT(!aGridItems->IsEmpty());
6440     auto borderBoxStartToEndOfEndTrack =
6441         aCBBorderPaddingStart +
6442         aTracks.GridLineEdge(aFirstExcludedTrack,
6443                              GridLineSide::eBeforeGridGap) -
6444         aTracks.GridLineEdge(aFragmentStartTrack, GridLineSide::eBeforeGridGap);
6445     mBaseline[axis][BaselineSharingGroup::eLast] =
6446         (aCBSize - borderBoxStartToEndOfEndTrack) + lastBaseline;
6447   }
6448 }
6449 
6450 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const6451 nsresult nsGridContainerFrame::GetFrameName(nsAString& aResult) const {
6452   return MakeFrameName(NS_LITERAL_STRING("GridContainer"), aResult);
6453 }
6454 #endif
6455 
NoteNewChildren(ChildListID aListID,const nsFrameList & aFrameList)6456 void nsGridContainerFrame::NoteNewChildren(ChildListID aListID,
6457                                            const nsFrameList& aFrameList) {
6458 #ifdef DEBUG
6459   ChildListIDs supportedLists =
6460       kAbsoluteList | kFixedList | kPrincipalList | kNoReflowPrincipalList;
6461   // We don't handle the kBackdropList frames in any way, but it only contains
6462   // a placeholder for ::backdrop which is OK to not reflow (for now anyway).
6463   supportedLists |= kBackdropList;
6464   MOZ_ASSERT(supportedLists.Contains(aListID), "unexpected child list");
6465 #endif
6466 
6467   nsIPresShell* shell = PresShell();
6468   for (auto pif = GetPrevInFlow(); pif; pif = pif->GetPrevInFlow()) {
6469     if (aListID == kPrincipalList) {
6470       pif->AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
6471     }
6472     shell->FrameNeedsReflow(pif, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
6473   }
6474 }
6475 
MergeSortedOverflow(nsFrameList & aList)6476 void nsGridContainerFrame::MergeSortedOverflow(nsFrameList& aList) {
6477   if (aList.IsEmpty()) {
6478     return;
6479   }
6480   MOZ_ASSERT(
6481       !aList.FirstChild()->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
6482       "this is the wrong list to put this child frame");
6483   MOZ_ASSERT(aList.FirstChild()->GetParent() == this);
6484   nsFrameList* overflow = GetOverflowFrames();
6485   if (overflow) {
6486     ::MergeSortedFrameLists(*overflow, aList, GetContent());
6487   } else {
6488     SetOverflowFrames(aList);
6489   }
6490 }
6491 
MergeSortedExcessOverflowContainers(nsFrameList & aList)6492 void nsGridContainerFrame::MergeSortedExcessOverflowContainers(
6493     nsFrameList& aList) {
6494   if (aList.IsEmpty()) {
6495     return;
6496   }
6497   MOZ_ASSERT(
6498       aList.FirstChild()->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
6499       "this is the wrong list to put this child frame");
6500   MOZ_ASSERT(aList.FirstChild()->GetParent() == this);
6501   nsFrameList* eoc = GetPropTableFrames(ExcessOverflowContainersProperty());
6502   if (eoc) {
6503     ::MergeSortedFrameLists(*eoc, aList, GetContent());
6504   } else {
6505     SetPropTableFrames(new (PresShell()) nsFrameList(aList),
6506                        ExcessOverflowContainersProperty());
6507   }
6508 }
6509 
6510 /* static */ nsGridContainerFrame::FindItemInGridOrderResult
FindFirstItemInGridOrder(CSSOrderAwareFrameIterator & aIter,const nsTArray<GridItemInfo> & aGridItems,LineRange GridArea::* aMajor,LineRange GridArea::* aMinor,uint32_t aFragmentStartTrack)6511 nsGridContainerFrame::FindFirstItemInGridOrder(
6512     CSSOrderAwareFrameIterator& aIter, const nsTArray<GridItemInfo>& aGridItems,
6513     LineRange GridArea::*aMajor, LineRange GridArea::*aMinor,
6514     uint32_t aFragmentStartTrack) {
6515   FindItemInGridOrderResult result = {nullptr, false};
6516   uint32_t minMajor = kTranslatedMaxLine + 1;
6517   uint32_t minMinor = kTranslatedMaxLine + 1;
6518   aIter.Reset();
6519   for (; !aIter.AtEnd(); aIter.Next()) {
6520     const GridItemInfo& item = aGridItems[aIter.ItemIndex()];
6521     if ((item.mArea.*aMajor).mEnd <= aFragmentStartTrack) {
6522       continue;  // item doesn't span any track in this fragment
6523     }
6524     uint32_t major = (item.mArea.*aMajor).mStart;
6525     uint32_t minor = (item.mArea.*aMinor).mStart;
6526     if (major < minMajor || (major == minMajor && minor < minMinor)) {
6527       minMajor = major;
6528       minMinor = minor;
6529       result.mItem = &item;
6530       result.mIsInEdgeTrack = major == 0U;
6531     }
6532   }
6533   return result;
6534 }
6535 
6536 /* static */ nsGridContainerFrame::FindItemInGridOrderResult
FindLastItemInGridOrder(ReverseCSSOrderAwareFrameIterator & aIter,const nsTArray<GridItemInfo> & aGridItems,LineRange GridArea::* aMajor,LineRange GridArea::* aMinor,uint32_t aFragmentStartTrack,uint32_t aFirstExcludedTrack)6537 nsGridContainerFrame::FindLastItemInGridOrder(
6538     ReverseCSSOrderAwareFrameIterator& aIter,
6539     const nsTArray<GridItemInfo>& aGridItems, LineRange GridArea::*aMajor,
6540     LineRange GridArea::*aMinor, uint32_t aFragmentStartTrack,
6541     uint32_t aFirstExcludedTrack) {
6542   FindItemInGridOrderResult result = {nullptr, false};
6543   int32_t maxMajor = -1;
6544   int32_t maxMinor = -1;
6545   aIter.Reset();
6546   int32_t lastMajorTrack = int32_t(aFirstExcludedTrack) - 1;
6547   for (; !aIter.AtEnd(); aIter.Next()) {
6548     const GridItemInfo& item = aGridItems[aIter.ItemIndex()];
6549     // Subtract 1 from the end line to get the item's last track index.
6550     int32_t major = (item.mArea.*aMajor).mEnd - 1;
6551     // Currently, this method is only called with aFirstExcludedTrack ==
6552     // the first track in the next fragment, so we take the opportunity
6553     // to assert this item really belongs to this fragment.
6554     MOZ_ASSERT((item.mArea.*aMajor).mStart < aFirstExcludedTrack,
6555                "found an item that belongs to some later fragment");
6556     if (major < int32_t(aFragmentStartTrack)) {
6557       continue;  // item doesn't span any track in this fragment
6558     }
6559     int32_t minor = (item.mArea.*aMinor).mEnd - 1;
6560     MOZ_ASSERT(minor >= 0 && major >= 0, "grid item must have span >= 1");
6561     if (major > maxMajor || (major == maxMajor && minor > maxMinor)) {
6562       maxMajor = major;
6563       maxMinor = minor;
6564       result.mItem = &item;
6565       result.mIsInEdgeTrack = major == lastMajorTrack;
6566     }
6567   }
6568   return result;
6569 }
6570 
6571 #ifdef DEBUG
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)6572 void nsGridContainerFrame::SetInitialChildList(ChildListID aListID,
6573                                                nsFrameList& aChildList) {
6574 #ifdef DEBUG
6575   ChildListIDs supportedLists = kAbsoluteList | kFixedList | kPrincipalList;
6576   // We don't handle the kBackdropList frames in any way, but it only contains
6577   // a placeholder for ::backdrop which is OK to not reflow (for now anyway).
6578   supportedLists |= kBackdropList;
6579   MOZ_ASSERT(supportedLists.Contains(aListID), "unexpected child list");
6580 #endif
6581 
6582   return nsContainerFrame::SetInitialChildList(aListID, aChildList);
6583 }
6584 
SanityCheckGridItemsBeforeReflow() const6585 void nsGridContainerFrame::SanityCheckGridItemsBeforeReflow() const {
6586   ChildListIDs absLists = kAbsoluteList | kFixedList | kOverflowContainersList |
6587                           kExcessOverflowContainersList;
6588   ChildListIDs itemLists = kPrincipalList | kOverflowList;
6589   for (const nsIFrame* f = this; f; f = f->GetNextInFlow()) {
6590     MOZ_ASSERT(!f->HasAnyStateBits(NS_STATE_GRID_DID_PUSH_ITEMS),
6591                "At start of reflow, we should've pulled items back from all "
6592                "NIFs and cleared NS_STATE_GRID_DID_PUSH_ITEMS in the process");
6593     for (nsIFrame::ChildListIterator childLists(f); !childLists.IsDone();
6594          childLists.Next()) {
6595       if (!itemLists.Contains(childLists.CurrentID())) {
6596         MOZ_ASSERT(absLists.Contains(childLists.CurrentID()) ||
6597                        childLists.CurrentID() == kBackdropList,
6598                    "unexpected non-empty child list");
6599         continue;
6600       }
6601       for (auto child : childLists.CurrentList()) {
6602         MOZ_ASSERT(f == this || child->GetPrevInFlow(),
6603                    "all pushed items must be pulled up before reflow");
6604       }
6605     }
6606   }
6607   // If we have a prev-in-flow, each of its children's next-in-flow
6608   // should be one of our children or be null.
6609   const auto pif = static_cast<nsGridContainerFrame*>(GetPrevInFlow());
6610   if (pif) {
6611     const nsFrameList* oc = GetPropTableFrames(OverflowContainersProperty());
6612     const nsFrameList* eoc =
6613         GetPropTableFrames(ExcessOverflowContainersProperty());
6614     const nsFrameList* pifEOC =
6615         pif->GetPropTableFrames(ExcessOverflowContainersProperty());
6616     for (const nsIFrame* child : pif->GetChildList(kPrincipalList)) {
6617       const nsIFrame* childNIF = child->GetNextInFlow();
6618       MOZ_ASSERT(!childNIF || mFrames.ContainsFrame(childNIF) ||
6619                  (pifEOC && pifEOC->ContainsFrame(childNIF)) ||
6620                  (oc && oc->ContainsFrame(childNIF)) ||
6621                  (eoc && eoc->ContainsFrame(childNIF)));
6622     }
6623   }
6624 }
6625 
Dump() const6626 void nsGridContainerFrame::TrackSize::Dump() const {
6627   printf("mPosition=%d mBase=%d mLimit=%d", mPosition, mBase, mLimit);
6628 
6629   printf(" min:");
6630   if (mState & eAutoMinSizing) {
6631     printf("auto ");
6632   } else if (mState & eMinContentMinSizing) {
6633     printf("min-content ");
6634   } else if (mState & eMaxContentMinSizing) {
6635     printf("max-content ");
6636   }
6637 
6638   printf(" max:");
6639   if (mState & eAutoMaxSizing) {
6640     printf("auto ");
6641   } else if (mState & eMinContentMaxSizing) {
6642     printf("min-content ");
6643   } else if (mState & eMaxContentMaxSizing) {
6644     printf("max-content ");
6645   } else if (mState & eFlexMaxSizing) {
6646     printf("flex ");
6647   }
6648 
6649   if (mState & eFrozen) {
6650     printf("frozen ");
6651   }
6652   if (mState & eModified) {
6653     printf("modified ");
6654   }
6655   if (mState & eBreakBefore) {
6656     printf("break-before ");
6657   }
6658 }
6659 
6660 #endif  // DEBUG
6661 
GetGridFrameWithComputedInfo(nsIFrame * aFrame)6662 nsGridContainerFrame* nsGridContainerFrame::GetGridFrameWithComputedInfo(
6663     nsIFrame* aFrame) {
6664   // Prepare a lambda function that we may need to call multiple times.
6665   auto GetGridContainerFrame = [](nsIFrame* aFrame) {
6666     // Return the aFrame's content insertion frame, iff it is
6667     // a grid container.
6668     nsGridContainerFrame* gridFrame = nullptr;
6669 
6670     if (aFrame) {
6671       nsIFrame* contentFrame = aFrame->GetContentInsertionFrame();
6672       if (contentFrame && (contentFrame->IsGridContainerFrame())) {
6673         gridFrame = static_cast<nsGridContainerFrame*>(contentFrame);
6674       }
6675     }
6676     return gridFrame;
6677   };
6678 
6679   nsGridContainerFrame* gridFrame = GetGridContainerFrame(aFrame);
6680   if (gridFrame) {
6681     // if any of our properties are missing, generate them
6682     bool reflowNeeded = (!gridFrame->HasProperty(GridColTrackInfo()) ||
6683                          !gridFrame->HasProperty(GridRowTrackInfo()) ||
6684                          !gridFrame->HasProperty(GridColumnLineInfo()) ||
6685                          !gridFrame->HasProperty(GridRowLineInfo()));
6686 
6687     if (reflowNeeded) {
6688       // Trigger a reflow that generates additional grid property data.
6689       // Hold onto aFrame while we do this, in case reflow destroys it.
6690       AutoWeakFrame weakFrameRef(aFrame);
6691 
6692       nsIPresShell* shell = gridFrame->PresShell();
6693       gridFrame->AddStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
6694       shell->FrameNeedsReflow(gridFrame, nsIPresShell::eResize,
6695                               NS_FRAME_IS_DIRTY);
6696       shell->FlushPendingNotifications(FlushType::Layout);
6697 
6698       // Since the reflow may have side effects, get the grid frame
6699       // again. But if the weakFrameRef is no longer valid, then we
6700       // must bail out.
6701       if (!weakFrameRef.IsAlive()) {
6702         return nullptr;
6703       }
6704 
6705       gridFrame = GetGridContainerFrame(weakFrameRef.GetFrame());
6706 
6707       // Assert the grid properties are present
6708       MOZ_ASSERT(!gridFrame || gridFrame->HasProperty(GridColTrackInfo()));
6709       MOZ_ASSERT(!gridFrame || gridFrame->HasProperty(GridRowTrackInfo()));
6710       MOZ_ASSERT(!gridFrame || gridFrame->HasProperty(GridColumnLineInfo()));
6711       MOZ_ASSERT(!gridFrame || gridFrame->HasProperty(GridRowLineInfo()));
6712     }
6713   }
6714 
6715   return gridFrame;
6716 }
6717