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