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