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