Coverage Report

Created: 2018-09-25 14:53

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