tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsGridContainerFrame.cpp (465449B)


      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 <stdlib.h>  // for div()
     12 
     13 #include <functional>
     14 #include <type_traits>
     15 
     16 #include "fmt/format.h"
     17 #include "gfxContext.h"
     18 #include "mozilla/AbsoluteContainingBlock.h"
     19 #include "mozilla/AutoRestore.h"
     20 #include "mozilla/Baseline.h"
     21 #include "mozilla/CSSAlignUtils.h"
     22 #include "mozilla/ComputedStyle.h"
     23 #include "mozilla/IntegerRange.h"
     24 #include "mozilla/Maybe.h"
     25 #include "mozilla/PodOperations.h"  // for PodZero
     26 #include "mozilla/PresShell.h"
     27 #include "mozilla/ScrollContainerFrame.h"
     28 #include "mozilla/StaticPrefs_layout.h"
     29 #include "mozilla/dom/Grid.h"
     30 #include "mozilla/dom/GridBinding.h"
     31 #include "nsCSSFrameConstructor.h"
     32 #include "nsDisplayList.h"
     33 #include "nsFieldSetFrame.h"
     34 #include "nsHashKeys.h"
     35 #include "nsIFrameInlines.h"  // for nsIFrame::GetLogicalNormalPosition (don't remove)
     36 #include "nsLayoutUtils.h"
     37 #include "nsPlaceholderFrame.h"
     38 #include "nsPresContext.h"
     39 #include "nsReadableUtils.h"
     40 #include "nsTableWrapperFrame.h"
     41 
     42 using namespace mozilla;
     43 
     44 using AlignJustifyFlag = CSSAlignUtils::AlignJustifyFlag;
     45 using AlignJustifyFlags = CSSAlignUtils::AlignJustifyFlags;
     46 using GridItemCachedBAxisMeasurement =
     47    nsGridContainerFrame::CachedBAxisMeasurement;
     48 using GridTemplate = StyleGridTemplateComponent;
     49 using NameList = StyleOwnedSlice<StyleCustomIdent>;
     50 using SizingConstraint = nsGridContainerFrame::SizingConstraint;
     51 using TrackListValue =
     52    StyleGenericTrackListValue<LengthPercentage, StyleInteger>;
     53 using TrackRepeat = StyleGenericTrackRepeat<LengthPercentage, StyleInteger>;
     54 using TrackSize = nsGridContainerFrame::TrackSize;
     55 
     56 static mozilla::LazyLogModule gGridContainerLog("GridContainer");
     57 #define GRID_LOG(...) \
     58  MOZ_LOG(gGridContainerLog, LogLevel::Debug, (__VA_ARGS__));
     59 
     60 static const int32_t kMaxLine = StyleMAX_GRID_LINE;
     61 static const int32_t kMinLine = StyleMIN_GRID_LINE;
     62 // The maximum line number, in the zero-based translated grid.
     63 static const uint32_t kTranslatedMaxLine = uint32_t(kMaxLine - kMinLine);
     64 static const uint32_t kAutoLine = kTranslatedMaxLine + 3457U;
     65 
     66 static const nsFrameState kIsSubgridBits =
     67    (NS_STATE_GRID_IS_COL_SUBGRID | NS_STATE_GRID_IS_ROW_SUBGRID);
     68 
     69 namespace mozilla {
     70 
     71 template <>
     72 inline Span<const StyleOwnedSlice<StyleCustomIdent>>
     73 GridTemplate::LineNameLists(bool aIsSubgrid) const {
     74  if (IsTrackList()) {
     75    return AsTrackList()->line_names.AsSpan();
     76  }
     77  if (IsSubgrid() && aIsSubgrid) {
     78    // For subgrid, we need to resolve <line-name-list> from each
     79    // StyleGenericLineNameListValue, so return empty.
     80    return {};
     81  }
     82  MOZ_ASSERT(IsNone() || IsMasonry() || (IsSubgrid() && !aIsSubgrid));
     83  return {};
     84 }
     85 
     86 template <>
     87 inline const StyleTrackBreadth& StyleTrackSize::GetMax() const {
     88  if (IsBreadth()) {
     89    return AsBreadth();
     90  }
     91  if (IsMinmax()) {
     92    return AsMinmax()._1;
     93  }
     94  MOZ_ASSERT(IsFitContent());
     95  return AsFitContent();
     96 }
     97 
     98 template <>
     99 inline const StyleTrackBreadth& StyleTrackSize::GetMin() const {
    100  static const StyleTrackBreadth kAuto = StyleTrackBreadth::Auto();
    101  if (IsBreadth()) {
    102    // <flex> behaves like minmax(auto, <flex>)
    103    return AsBreadth().IsFr() ? kAuto : AsBreadth();
    104  }
    105  if (IsMinmax()) {
    106    return AsMinmax()._0;
    107  }
    108  MOZ_ASSERT(IsFitContent());
    109  return kAuto;
    110 }
    111 
    112 }  // namespace mozilla
    113 
    114 static nscoord ClampToCSSMaxBSize(nscoord aSize,
    115                                  const ReflowInput* aReflowInput) {
    116  auto maxSize = aReflowInput->ComputedMaxBSize();
    117  if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
    118    MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
    119    aSize = std::min(aSize, maxSize);
    120  }
    121  return aSize;
    122 }
    123 
    124 // Same as above and set aStatus INCOMPLETE if aSize wasn't clamped.
    125 // (If we clamp aSize it means our size is less than the break point,
    126 // i.e. we're effectively breaking in our overflow, so we should leave
    127 // aStatus as is (it will likely be set to OVERFLOW_INCOMPLETE later)).
    128 static nscoord ClampToCSSMaxBSize(nscoord aSize,
    129                                  const ReflowInput* aReflowInput,
    130                                  nsReflowStatus* aStatus) {
    131  auto maxSize = aReflowInput->ComputedMaxBSize();
    132  if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
    133    MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
    134    if (aSize < maxSize) {
    135      aStatus->SetIncomplete();
    136    } else {
    137      aSize = maxSize;
    138    }
    139  } else {
    140    aStatus->SetIncomplete();
    141  }
    142  return aSize;
    143 }
    144 
    145 template <typename Size>
    146 static bool IsPercentOfIndefiniteSize(const Size& aCoord,
    147                                      nscoord aPercentBasis) {
    148  return aPercentBasis == NS_UNCONSTRAINEDSIZE && aCoord.HasPercent();
    149 }
    150 
    151 static nscoord ResolveToDefiniteSize(const StyleTrackBreadth& aBreadth,
    152                                     nscoord aPercentBasis) {
    153  MOZ_ASSERT(aBreadth.IsBreadth());
    154  if (::IsPercentOfIndefiniteSize(aBreadth.AsBreadth(), aPercentBasis)) {
    155    return nscoord(0);
    156  }
    157  return std::max(nscoord(0), aBreadth.AsBreadth().Resolve(aPercentBasis));
    158 }
    159 
    160 // Synthesize a baseline from a border box.  For an alphabetical baseline
    161 // this is the end edge of the border box.  For a central baseline it's
    162 // the center of the border box.
    163 // https://drafts.csswg.org/css-align-3/#synthesize-baseline
    164 // For a 'first baseline' the measure is from the border-box start edge and
    165 // for a 'last baseline' the measure is from the border-box end edge.
    166 //
    167 // The 'LogicalAxis aAxis' represents the axis (in terms of aWM) that the
    168 // baseline corresponds to.  (Typically, baselines are a measurement in the
    169 // block axis; e.g. for English horizontal-tb text, a traditional baseline
    170 // would be a y-axis measurement.  But in some cases (e.g. orthogonal WMs), we
    171 // may need to synthesize a baseline in a child's inline axis, which is when
    172 // this function might receive an aAxis of LogicalAxis::Inline. In that case, we
    173 // assume that the writing mode's preference for central vs. alphabetic
    174 // baselines is irrelevant, since that's a choice about its block-axis
    175 // baselines, and we just unconditionally use the alphabetic baseline
    176 // (e.g. border-box bottom edge).
    177 static nscoord SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup,
    178                                               WritingMode aWM,
    179                                               LogicalAxis aAxis,
    180                                               nscoord aBorderBoxSize) {
    181  const bool useAlphabeticBaseline =
    182      (aAxis == LogicalAxis::Inline) ? true : aWM.IsAlphabeticalBaseline();
    183 
    184  if (aGroup == BaselineSharingGroup::First) {
    185    return useAlphabeticBaseline ? aBorderBoxSize : aBorderBoxSize / 2;
    186  }
    187  MOZ_ASSERT(aGroup == BaselineSharingGroup::Last);
    188  // Round up for central baseline offset, to be consistent with eFirst.
    189  return useAlphabeticBaseline ? 0
    190                               : (aBorderBoxSize / 2) + (aBorderBoxSize % 2);
    191 }
    192 
    193 // The helper struct to hold the box sizing adjustment.
    194 struct BoxSizingAdjustment {
    195  BoxSizingAdjustment() = delete;
    196  BoxSizingAdjustment(const WritingMode aWM, const ComputedStyle& aStyle)
    197      : mWM(aWM), mStyle(aStyle) {}
    198 
    199  const LogicalSize& EnsureAndGet() {
    200    if (mValue) {
    201      return mValue.ref();
    202    }
    203 
    204    if (mStyle.StylePosition()->mBoxSizing != StyleBoxSizing::Border) {
    205      // Use default, (0, 0).
    206      mValue.emplace(mWM);
    207      return mValue.ref();
    208    }
    209 
    210    const auto& padding = mStyle.StylePadding()->mPadding;
    211    LogicalMargin border(mWM, mStyle.StyleBorder()->GetComputedBorder());
    212    // We can use zero percentage basis since this is only called from
    213    // intrinsic sizing code.
    214    const nscoord percentageBasis = 0;
    215    const nscoord iBP =
    216        std::max(padding.GetIStart(mWM).Resolve(percentageBasis), 0) +
    217        std::max(padding.GetIEnd(mWM).Resolve(percentageBasis), 0) +
    218        border.IStartEnd(mWM);
    219    const nscoord bBP =
    220        std::max(padding.GetBStart(mWM).Resolve(percentageBasis), 0) +
    221        std::max(padding.GetBEnd(mWM).Resolve(percentageBasis), 0) +
    222        border.BStartEnd(mWM);
    223    mValue.emplace(mWM, iBP, bBP);
    224    return mValue.ref();
    225  }
    226 
    227 private:
    228  const WritingMode mWM;
    229  const ComputedStyle& mStyle;
    230  // The wrapped value we would like to use for the box sizing adjustment.
    231  Maybe<LogicalSize> mValue;
    232 };
    233 
    234 static Maybe<nscoord> GetPercentageBasisForAR(
    235    const LogicalAxis aRatioDeterminingAxis, const WritingMode aWM,
    236    const Maybe<LogicalSize>& aContainingBlockSize) {
    237  if (!aContainingBlockSize) {
    238    return Nothing();
    239  }
    240 
    241  const nscoord basis = aContainingBlockSize->Size(aRatioDeterminingAxis, aWM);
    242  // If the basis is unconstrained (because we are still computing the
    243  // containing block size), we should treat it as no basis.
    244  return basis == NS_UNCONSTRAINEDSIZE ? Nothing() : Some(basis);
    245 }
    246 
    247 template <typename Type>
    248 static Maybe<nscoord> ComputeTransferredSize(
    249    const Type& aRatioDeterminingSize, const LogicalAxis aAxis,
    250    const WritingMode aWM, const AspectRatio& aAspectRatio,
    251    BoxSizingAdjustment& aBoxSizingAdjustment,
    252    const Maybe<LogicalSize>& aContainingBlockSize) {
    253  // Use GetOrthogonalAxis() to get the ratio-determining axis.
    254  const Maybe<nscoord> basis = GetPercentageBasisForAR(
    255      GetOrthogonalAxis(aAxis), aWM, aContainingBlockSize);
    256  nscoord rdSize = 0;
    257  if (aRatioDeterminingSize->ConvertsToLength()) {
    258    rdSize = aRatioDeterminingSize->ToLength();
    259  } else if (aRatioDeterminingSize->HasPercent() && basis) {
    260    rdSize = aRatioDeterminingSize->AsLengthPercentage().Resolve(*basis);
    261  } else {
    262    // Either we are not using LengthPercentage or there is no percentage basis.
    263    return Nothing();
    264  }
    265  return Some(aAspectRatio.ComputeRatioDependentSize(
    266      aAxis, aWM, rdSize, aBoxSizingAdjustment.EnsureAndGet()));
    267 }
    268 
    269 // A cached result for a grid item's block-axis measuring reflow. This
    270 // cache prevents us from doing exponential reflows in cases of deeply
    271 // nested grid frames.
    272 //
    273 // We store the cached value in the grid item's frame property table.
    274 //
    275 // We cache the following as a "key"
    276 //   - The size of the grid area in the item's inline axis
    277 //   - The item's block axis baseline padding
    278 // ...and we cache the following as the "value",
    279 //   - The item's border-box BSize
    280 class nsGridContainerFrame::CachedBAxisMeasurement final {
    281 public:
    282  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, CachedBAxisMeasurement)
    283 
    284  CachedBAxisMeasurement(const nsIFrame* aFrame, const LogicalSize& aCBSize,
    285                         const nscoord aBSize)
    286      : mKey(aFrame, aCBSize), mBSize(aBSize) {}
    287 
    288  bool IsValidFor(const nsIFrame* aFrame, const LogicalSize& aCBSize) const {
    289    if (aFrame->IsSubtreeDirty()) {
    290      return false;
    291    }
    292    return mKey == Key(aFrame, aCBSize);
    293  }
    294 
    295  nscoord BSize() const { return mBSize; }
    296 
    297  void Update(const nsIFrame* aFrame, const LogicalSize& aCBSize,
    298              const nscoord aBSize) {
    299    mKey.Update(aFrame, aCBSize);
    300    mBSize = aBSize;
    301  }
    302 
    303 private:
    304  struct Key final {
    305    nscoord mCBSizeInItemInlineAxis;
    306    nscoord mBaselinePaddingInItemBlockAxis;
    307 
    308    Key(const nsIFrame* aFrame, const LogicalSize& aCBSize) {
    309      Update(aFrame, aCBSize);
    310    }
    311 
    312    void Update(const nsIFrame* aFrame, const LogicalSize& aCBSize) {
    313      mCBSizeInItemInlineAxis = aCBSize.ISize(aFrame->GetWritingMode());
    314      mBaselinePaddingInItemBlockAxis =
    315          aFrame->GetProperty(nsIFrame::BBaselinePadProperty());
    316    }
    317 
    318    bool operator==(const Key& aOther) const = default;
    319  };
    320 
    321  Key mKey;
    322  nscoord mBSize;
    323 };
    324 
    325 // The input sizes for calculating the number of repeat(auto-fill/fit) tracks.
    326 // https://drafts.csswg.org/css-grid-2/#auto-repeat
    327 struct RepeatTrackSizingInput {
    328  explicit RepeatTrackSizingInput(WritingMode aWM)
    329      : mMin(aWM, 0, 0),
    330        mSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
    331        mMax(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) {}
    332 
    333  RepeatTrackSizingInput(const LogicalSize& aMin, const LogicalSize& aSize,
    334                         const LogicalSize& aMax)
    335      : mMin(aMin), mSize(aSize), mMax(aMax) {}
    336 
    337  // This should be used in intrinsic sizing (i.e. when we can't initialize
    338  // the sizes directly from ReflowInput values).
    339  void InitFromStyle(LogicalAxis aAxis, WritingMode aWM, const nsIFrame* aFrame,
    340                     const ComputedStyle* aStyle,
    341                     const AspectRatio& aAspectRatio,
    342                     const Maybe<LogicalSize>& aContainingBlockSize) {
    343    const auto& pos = aStyle->StylePosition();
    344    const AnchorPosResolutionParams anchorResolutionParams{
    345        aFrame, aStyle->StyleDisplay()->mPosition};
    346    BoxSizingAdjustment boxSizingAdjustment(aWM, *aStyle);
    347    const nscoord cbSizeInAxis = aContainingBlockSize
    348                                     ? aContainingBlockSize->Size(aAxis, aWM)
    349                                     : NS_UNCONSTRAINEDSIZE;
    350 
    351    auto adjustForBoxSizing = [aWM, aAxis,
    352                               &boxSizingAdjustment](nscoord aSize) {
    353      return std::max(
    354          aSize - boxSizingAdjustment.EnsureAndGet().Size(aAxis, aWM), 0);
    355    };
    356 
    357    nscoord& min = mMin.Size(aAxis, aWM);
    358    const auto styleMinSize = pos->MinSize(aAxis, aWM, anchorResolutionParams);
    359    if (styleMinSize->ConvertsToLength()) {
    360      min = adjustForBoxSizing(styleMinSize->ToLength());
    361    } else if (styleMinSize->HasPercent() &&
    362               cbSizeInAxis != NS_UNCONSTRAINEDSIZE) {
    363      min = adjustForBoxSizing(
    364          styleMinSize->AsLengthPercentage().Resolve(cbSizeInAxis));
    365    } else if (aAspectRatio && styleMinSize->BehavesLikeInitialValue(aAxis)) {
    366      // Use GetOrthogonalAxis() to get the ratio-determining axis. Same for max
    367      // and size below in this function.
    368      const auto styleRDMinSize =
    369          pos->MinSize(GetOrthogonalAxis(aAxis), aWM, anchorResolutionParams);
    370      if (Maybe<nscoord> resolvedMinSize = ComputeTransferredSize(
    371              styleRDMinSize, aAxis, aWM, aAspectRatio, boxSizingAdjustment,
    372              aContainingBlockSize)) {
    373        min = *resolvedMinSize;
    374      }
    375    }
    376 
    377    nscoord& max = mMax.Size(aAxis, aWM);
    378    const auto styleMaxSize = pos->MaxSize(aAxis, aWM, anchorResolutionParams);
    379    if (styleMaxSize->ConvertsToLength()) {
    380      max = std::max(min, adjustForBoxSizing(styleMaxSize->ToLength()));
    381    } else if (styleMaxSize->HasPercent() &&
    382               cbSizeInAxis != NS_UNCONSTRAINEDSIZE) {
    383      max = std::max(
    384          min, adjustForBoxSizing(
    385                   styleMaxSize->AsLengthPercentage().Resolve(cbSizeInAxis)));
    386    } else if (aAspectRatio && styleMaxSize->BehavesLikeInitialValue(aAxis)) {
    387      const auto styleRDMaxSize =
    388          pos->MaxSize(GetOrthogonalAxis(aAxis), aWM, anchorResolutionParams);
    389      if (Maybe<nscoord> resolvedMaxSize = ComputeTransferredSize(
    390              styleRDMaxSize, aAxis, aWM, aAspectRatio, boxSizingAdjustment,
    391              aContainingBlockSize)) {
    392        max = std::max(min, *resolvedMaxSize);
    393      }
    394    }
    395 
    396    nscoord& size = mSize.Size(aAxis, aWM);
    397    // When computing the intrinsic inline size, disregard the explicit
    398    // inline-size property as it should not affect the final result.
    399    const auto styleSize = aAxis == LogicalAxis::Inline
    400                               ? AnchorResolvedSizeHelper::Auto()
    401                               : pos->BSize(aWM, anchorResolutionParams);
    402    if (styleSize->ConvertsToLength()) {
    403      size = std::clamp(adjustForBoxSizing(styleSize->ToLength()), min, max);
    404    } else if (styleSize->HasPercent() &&
    405               cbSizeInAxis != NS_UNCONSTRAINEDSIZE) {
    406      size =
    407          std::clamp(adjustForBoxSizing(
    408                         styleSize->AsLengthPercentage().Resolve(cbSizeInAxis)),
    409                     min, max);
    410    } else if (aAspectRatio && styleSize->BehavesLikeInitialValue(aAxis)) {
    411      const auto styleRDSize =
    412          pos->Size(GetOrthogonalAxis(aAxis), aWM, anchorResolutionParams);
    413      if (Maybe<nscoord> resolvedSize = ComputeTransferredSize(
    414              styleRDSize, aAxis, aWM, aAspectRatio, boxSizingAdjustment,
    415              aContainingBlockSize)) {
    416        size = std::clamp(*resolvedSize, min, max);
    417      }
    418    }
    419  }
    420 
    421  LogicalSize mMin;
    422  LogicalSize mSize;
    423  LogicalSize mMax;
    424 };
    425 
    426 enum class GridLineSide {
    427  BeforeGridGap,
    428  AfterGridGap,
    429 };
    430 
    431 struct nsGridContainerFrame::TrackSize {
    432  enum StateBits : uint16_t {
    433    eAutoMinSizing = 1 << 0,
    434    eMinContentMinSizing = 1 << 1,
    435    eMaxContentMinSizing = 1 << 2,
    436    eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing,
    437    eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing,
    438    eModified = 1 << 3,
    439    eAutoMaxSizing = 1 << 4,
    440    eMinContentMaxSizing = 1 << 5,
    441    eMaxContentMaxSizing = 1 << 6,
    442    eAutoOrMaxContentMaxSizing = eAutoMaxSizing | eMaxContentMaxSizing,
    443    eIntrinsicMaxSizing = eAutoOrMaxContentMaxSizing | eMinContentMaxSizing,
    444    eFlexMaxSizing = 1 << 7,
    445    eFrozen = 1 << 8,
    446    eSkipGrowUnlimited1 = 1 << 9,
    447    eSkipGrowUnlimited2 = 1 << 10,
    448    eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2,
    449    eBreakBefore = 1 << 11,
    450    eApplyFitContentClamping = 1 << 12,
    451    eInfinitelyGrowable = 1 << 13,
    452 
    453    // These are only used in the masonry axis.  They share the same value
    454    // as *MinSizing above, but that's OK because we don't use those in
    455    // the masonry axis.
    456    //
    457    // This track corresponds to an item margin-box size that is stretching.
    458    eItemStretchSize = 1 << 0,
    459    // This bit says that we should clamp that size to mLimit.
    460    eClampToLimit = 1 << 1,
    461    // This bit says that the corresponding item has `auto` margin(s).
    462    eItemHasAutoMargin = 1 << 2,
    463  };
    464 
    465  StateBits Initialize(nscoord aPercentageBasis, const StyleTrackSize&);
    466  bool IsFrozen() const { return mState & eFrozen; }
    467 #ifdef DEBUG
    468  static void DumpStateBits(StateBits aState);
    469  void Dump() const;
    470 #endif
    471 
    472  static bool IsDefiniteMaxSizing(StateBits aStateBits) {
    473    return (aStateBits & (eIntrinsicMaxSizing | eFlexMaxSizing)) == 0;
    474  }
    475 
    476  // Base size of this track.
    477  // https://drafts.csswg.org/css-grid-2/#base-size
    478  nscoord mBase;
    479 
    480  // Growth limit of this track.
    481  // https://drafts.csswg.org/css-grid-2/#growth-limit
    482  nscoord mLimit;
    483 
    484  nscoord mPosition;  // zero until we apply 'align/justify-content'
    485  // mBaselineSubtreeSize is the size of a baseline-aligned subtree within
    486  // this track.  One subtree per baseline-sharing group (per track).
    487  PerBaseline<nscoord> mBaselineSubtreeSize;
    488  StateBits mState;
    489 };
    490 
    491 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits)
    492 
    493 static_assert(
    494    std::is_trivially_copyable<nsGridContainerFrame::TrackSize>::value,
    495    "Must be trivially copyable");
    496 static_assert(
    497    std::is_trivially_destructible<nsGridContainerFrame::TrackSize>::value,
    498    "Must be trivially destructible");
    499 
    500 TrackSize::StateBits nsGridContainerFrame::TrackSize::Initialize(
    501    nscoord aPercentageBasis, const StyleTrackSize& aSize) {
    502  using Tag = StyleTrackBreadth::Tag;
    503 
    504  MOZ_ASSERT(mBase == 0 && mLimit == 0 && mState == 0,
    505             "track size data is expected to be initialized to zero");
    506  mBaselineSubtreeSize[BaselineSharingGroup::First] = nscoord(0);
    507  mBaselineSubtreeSize[BaselineSharingGroup::Last] = nscoord(0);
    508 
    509  auto& min = aSize.GetMin();
    510  auto& max = aSize.GetMax();
    511 
    512  Tag minSizeTag = min.tag;
    513  Tag maxSizeTag = max.tag;
    514  if (aSize.IsFitContent()) {
    515    // In layout, fit-content(size) behaves as minmax(auto, max-content), with
    516    // 'size' as an additional upper-bound.
    517    if (!::IsPercentOfIndefiniteSize(aSize.AsFitContent(), aPercentageBasis)) {
    518      mState = eApplyFitContentClamping;
    519    }
    520    minSizeTag = Tag::Auto;
    521    maxSizeTag = Tag::MaxContent;
    522  }
    523  if (::IsPercentOfIndefiniteSize(min, aPercentageBasis)) {
    524    // https://drafts.csswg.org/css-grid-2/#valdef-grid-template-columns-length-percentage-0
    525    // "If the inline or block size of the grid container is indefinite,
    526    //  <percentage> values relative to that size are treated as 'auto'."
    527    minSizeTag = Tag::Auto;
    528  }
    529  if (::IsPercentOfIndefiniteSize(max, aPercentageBasis)) {
    530    maxSizeTag = Tag::Auto;
    531  }
    532 
    533  // https://drafts.csswg.org/css-grid-2/#algo-init
    534  switch (minSizeTag) {
    535    case Tag::Auto:
    536      mState |= eAutoMinSizing;
    537      break;
    538    case Tag::MinContent:
    539      mState |= eMinContentMinSizing;
    540      break;
    541    case Tag::MaxContent:
    542      mState |= eMaxContentMinSizing;
    543      break;
    544    default:
    545      MOZ_ASSERT(!min.IsFr(), "<flex> min-sizing is invalid as a track size");
    546      mBase = ::ResolveToDefiniteSize(min, aPercentageBasis);
    547  }
    548  switch (maxSizeTag) {
    549    case Tag::Auto:
    550      mState |= eAutoMaxSizing;
    551      mLimit = NS_UNCONSTRAINEDSIZE;
    552      break;
    553    case Tag::MinContent:
    554    case Tag::MaxContent:
    555      mState |= maxSizeTag == Tag::MinContent ? eMinContentMaxSizing
    556                                              : eMaxContentMaxSizing;
    557      mLimit = NS_UNCONSTRAINEDSIZE;
    558      break;
    559    case Tag::Fr:
    560      mState |= eFlexMaxSizing;
    561      mLimit = NS_UNCONSTRAINEDSIZE;
    562      break;
    563    default:
    564      mLimit = ::ResolveToDefiniteSize(max, aPercentageBasis);
    565      if (mLimit < mBase) {
    566        mLimit = mBase;
    567      }
    568  }
    569  return mState;
    570 }
    571 
    572 // Indicates if we are in intrinsic sizing step 3 (spanning items not
    573 // spanning any flex tracks) or step 4 (spanning items that span one or more
    574 // flex tracks).
    575 // https://drafts.csswg.org/css-grid-2/#algo-content
    576 enum class TrackSizingStep {
    577  NotFlex,  // https://drafts.csswg.org/css-grid-2/#algo-spanning-items
    578  Flex,     // https://drafts.csswg.org/css-grid-2/#algo-spanning-flex-items
    579 };
    580 
    581 // Sizing phases, used in intrinsic sizing steps 3 and 4.
    582 // https://drafts.csswg.org/css-grid-2/#algo-spanning-items
    583 enum class TrackSizingPhase {
    584  IntrinsicMinimums,
    585  ContentBasedMinimums,
    586  MaxContentMinimums,
    587  IntrinsicMaximums,
    588  MaxContentMaximums,
    589 };
    590 
    591 // Used for grid items intrinsic size types.
    592 // See CachedIntrinsicSizes which uses this for content contributions.
    593 enum class GridIntrinsicSizeType {
    594  // MinContribution is the "minimum contribution", defined at
    595  // https://drafts.csswg.org/css-grid-2/#min-size-contribution
    596  MinContribution,
    597  MinContentContribution,
    598  MaxContentContribution
    599 };
    600 
    601 static constexpr GridIntrinsicSizeType kAllGridIntrinsicSizeTypes[] = {
    602    GridIntrinsicSizeType::MinContribution,
    603    GridIntrinsicSizeType::MinContentContribution,
    604    GridIntrinsicSizeType::MaxContentContribution};
    605 
    606 // Glue to make mozilla::EnumeratedArray work with GridIntrinsicSizeType.
    607 namespace mozilla {
    608 template <>
    609 struct MaxContiguousEnumValue<GridIntrinsicSizeType> {
    610  static constexpr GridIntrinsicSizeType value =
    611      GridIntrinsicSizeType::MaxContentContribution;
    612 };
    613 }  // namespace mozilla
    614 
    615 // Convert a track sizing phase into which GridIntrinsicSizeType is applicable.
    616 static GridIntrinsicSizeType SizeTypeForPhase(TrackSizingPhase aPhase) {
    617  switch (aPhase) {
    618    case TrackSizingPhase::IntrinsicMinimums:
    619      return GridIntrinsicSizeType::MinContribution;
    620    case TrackSizingPhase::ContentBasedMinimums:
    621    case TrackSizingPhase::IntrinsicMaximums:
    622      return GridIntrinsicSizeType::MinContentContribution;
    623    case TrackSizingPhase::MaxContentMinimums:
    624    case TrackSizingPhase::MaxContentMaximums:
    625      return GridIntrinsicSizeType::MaxContentContribution;
    626  }
    627  MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
    628 }
    629 
    630 class nsGridContainerFrame::TrackPlan {
    631 public:
    632  TrackPlan() = default;
    633 
    634  explicit TrackPlan(size_t aCapacity) : mTrackSizes(aCapacity) {}
    635 
    636  TrackPlan(const TrackPlan& aOther) : mTrackSizes(aOther.mTrackSizes) {}
    637 
    638  TrackPlan(TrackPlan&& aOther) : mTrackSizes(std::move(aOther.mTrackSizes)) {}
    639 
    640  ~TrackPlan() = default;
    641 
    642  TrackPlan& operator=(const TrackPlan& aOther) {
    643    mTrackSizes.Assign(aOther.mTrackSizes);
    644    return *this;
    645  }
    646  TrackPlan& operator=(TrackPlan&& aOther) {
    647    mTrackSizes.Assign(std::move(aOther.mTrackSizes));
    648    return *this;
    649  }
    650 
    651  size_t Length() const { return mTrackSizes.Length(); }
    652 
    653  void SetLength(size_t aSize) { mTrackSizes.SetLength(aSize); }
    654 
    655  bool IsEmpty() const { return mTrackSizes.IsEmpty(); }
    656 
    657  void Assign(const TrackPlan& aRHS) { mTrackSizes.Assign(aRHS.mTrackSizes); }
    658 
    659  nsGridContainerFrame::TrackSize* AppendElement(
    660      nsGridContainerFrame::TrackSize aElement) {
    661    return mTrackSizes.AppendElement(aElement);
    662  }
    663 
    664  nsGridContainerFrame::TrackSize& LastElement() {
    665    return mTrackSizes.LastElement();
    666  }
    667 
    668  nsGridContainerFrame::TrackSize& operator[](size_t aIndex) {
    669    return mTrackSizes[aIndex];
    670  }
    671 
    672  const nsGridContainerFrame::TrackSize& operator[](size_t aIndex) const {
    673    return mTrackSizes[aIndex];
    674  }
    675 
    676  void ClearAndRetainStorage() { mTrackSizes.ClearAndRetainStorage(); }
    677 
    678  void ZeroInitialize() {
    679    PodZero(mTrackSizes.Elements(), mTrackSizes.Length());
    680  }
    681 
    682  using iterator = nsTArray<nsGridContainerFrame::TrackSize>::iterator;
    683  iterator begin() { return mTrackSizes.begin(); }
    684  iterator end() { return mTrackSizes.end(); }
    685 
    686  using const_iterator =
    687      nsTArray<nsGridContainerFrame::TrackSize>::const_iterator;
    688  const_iterator begin() const { return mTrackSizes.begin(); }
    689  const_iterator end() const { return mTrackSizes.end(); }
    690 
    691  void Initialize(TrackSizingPhase aPhase, const Tracks& aTracks);
    692 
    693  // Distribute space to all flex tracks this item spans.
    694  // https://drafts.csswg.org/css-grid-2/#algo-spanning-flex-items
    695  nscoord DistributeToFlexTrackSizes(
    696      nscoord aAvailableSpace, const nsTArray<uint32_t>& aGrowableTracks,
    697      const TrackSizingFunctions& aFunctions,
    698      const nsGridContainerFrame::Tracks& aTracks);
    699 
    700 private:
    701  CopyableTArray<nsGridContainerFrame::TrackSize> mTrackSizes;
    702 };
    703 
    704 using TrackPlan = nsGridContainerFrame::TrackPlan;
    705 
    706 class nsGridContainerFrame::ItemPlan {
    707 public:
    708  ItemPlan() = default;
    709 
    710  explicit ItemPlan(size_t aCapacity) : mTrackSizes(aCapacity) {}
    711 
    712  ~ItemPlan() = default;
    713 
    714  void SetLength(size_t aSize) { mTrackSizes.SetLength(aSize); }
    715 
    716  nsGridContainerFrame::TrackSize& operator[](size_t aIndex) {
    717    return mTrackSizes[aIndex];
    718  }
    719 
    720  void Initialize(TrackSizingPhase aPhase,
    721                  const nsTArray<uint32_t>& aGrowableTracks,
    722                  const nsGridContainerFrame::Tracks& aTracks);
    723 
    724  using FitContentClamper =
    725      std::function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
    726 
    727  /**
    728   * Grow the planned size for tracks in aGrowableTracks up to their limit
    729   * and then freeze them (all aGrowableTracks must be unfrozen on entry).
    730   * Subtract the space added from aAvailableSpace and return that.
    731   */
    732  nscoord GrowTracksToLimit(nscoord aAvailableSpace,
    733                            const nsTArray<uint32_t>& aGrowableTracks,
    734                            const FitContentClamper& aFitContentClamper);
    735 
    736  /**
    737   * Helper for GrowSelectedTracksUnlimited.  For the set of tracks (S) that
    738   * match aMinSizingSelector: if a track in S doesn't match aMaxSizingSelector
    739   * then mark it with aSkipFlag.  If all tracks in S were marked then unmark
    740   * them.  Return aNumGrowable minus the number of tracks marked.  It is
    741   * assumed that this plan has no aSkipFlag set for tracks in aGrowableTracks
    742   * on entry to this method.
    743   */
    744  uint32_t MarkExcludedTracks(uint32_t aNumGrowable,
    745                              const nsTArray<uint32_t>& aGrowableTracks,
    746                              TrackSize::StateBits aMinSizingSelector,
    747                              TrackSize::StateBits aMaxSizingSelector,
    748                              TrackSize::StateBits aSkipFlag);
    749 
    750  /**
    751   * Mark all tracks in aGrowableTracks with an eSkipGrowUnlimited bit if
    752   * they *shouldn't* grow unlimited in §12.5.1.2.4 "Distribute space beyond
    753   * growth limits" https://drafts.csswg.org/css-grid-2/#extra-space
    754   * Return the number of tracks that are still growable.
    755   */
    756  uint32_t MarkExcludedTracks(TrackSizingPhase aPhase,
    757                              const nsTArray<uint32_t>& aGrowableTracks,
    758                              SizingConstraint aConstraint);
    759 
    760  /**
    761   * Increase the planned size for tracks in aGrowableTracks that aren't
    762   * marked with a eSkipGrowUnlimited flag beyond their limit.
    763   * This implements the "Distribute space beyond growth limits" step in
    764   * https://drafts.csswg.org/css-grid-2/#distribute-extra-space
    765   */
    766  void GrowSelectedTracksUnlimited(nscoord aAvailableSpace,
    767                                   const nsTArray<uint32_t>& aGrowableTracks,
    768                                   uint32_t aNumGrowable,
    769                                   const FitContentClamper& aFitContentClamper);
    770 
    771 private:
    772  nsTArray<nsGridContainerFrame::TrackSize> mTrackSizes;
    773 };
    774 
    775 using ItemPlan = nsGridContainerFrame::ItemPlan;
    776 
    777 /**
    778 * A LineRange can be definite or auto - when it's definite it represents
    779 * a consecutive set of tracks between a starting line and an ending line.
    780 * Before it's definite it can also represent an auto position with a span,
    781 * where mStart == kAutoLine and mEnd is the (non-zero positive) span.
    782 * For normal-flow items, the invariant mStart < mEnd holds when both
    783 * lines are definite.
    784 *
    785 * For abs.pos. grid items, mStart and mEnd may both be kAutoLine, meaning
    786 * "attach this side to the grid container containing block edge".
    787 * Additionally, mStart <= mEnd holds when both are definite (non-kAutoLine),
    788 * i.e. the invariant is slightly relaxed compared to normal flow items.
    789 */
    790 struct nsGridContainerFrame::LineRange {
    791  LineRange(int32_t aStart, int32_t aEnd)
    792      : mUntranslatedStart(aStart), mUntranslatedEnd(aEnd) {
    793 #ifdef DEBUG
    794    if (!IsAutoAuto()) {
    795      if (IsAuto()) {
    796        MOZ_ASSERT(aEnd >= kMinLine && aEnd <= kMaxLine, "invalid span");
    797      } else {
    798        MOZ_ASSERT(aStart >= kMinLine && aStart <= kMaxLine,
    799                   "invalid start line");
    800        MOZ_ASSERT(aEnd == int32_t(kAutoLine) ||
    801                       (aEnd >= kMinLine && aEnd <= kMaxLine),
    802                   "invalid end line");
    803      }
    804    }
    805 #endif
    806  }
    807  bool IsAutoAuto() const { return mStart == kAutoLine && mEnd == kAutoLine; }
    808  bool IsAuto() const { return mStart == kAutoLine; }
    809  bool IsDefinite() const { return mStart != kAutoLine; }
    810  uint32_t Extent() const {
    811    MOZ_ASSERT(mEnd != kAutoLine, "Extent is undefined for abs.pos. 'auto'");
    812    if (IsAuto()) {
    813      MOZ_ASSERT(mEnd >= 1 && mEnd < uint32_t(kMaxLine), "invalid span");
    814      return mEnd;
    815    }
    816    return mEnd - mStart;
    817  }
    818 
    819  /**
    820   * Return an object suitable for iterating this range.
    821   */
    822  auto Range() const { return IntegerRange<uint32_t>(mStart, mEnd); }
    823 
    824  /**
    825   * Resolve this auto range to start at aStart, making it definite.
    826   * @param aClampMaxLine the maximum allowed line number (zero-based)
    827   * Precondition: this range IsAuto()
    828   */
    829  void ResolveAutoPosition(uint32_t aStart, uint32_t aClampMaxLine) {
    830    MOZ_ASSERT(IsAuto(), "Why call me?");
    831    mStart = aStart;
    832    mEnd += aStart;
    833    // Clamp to aClampMaxLine, which is where kMaxLine is in the explicit
    834    // grid in a non-subgrid axis; this implements clamping per
    835    // https://drafts.csswg.org/css-grid-2/#overlarge-grids
    836    // In a subgrid axis it's the end of the grid in that axis.
    837    if (MOZ_UNLIKELY(mStart >= aClampMaxLine)) {
    838      mEnd = aClampMaxLine;
    839      mStart = mEnd - 1;
    840    } else if (MOZ_UNLIKELY(mEnd > aClampMaxLine)) {
    841      mEnd = aClampMaxLine;
    842    }
    843  }
    844  /**
    845   * Translate the lines to account for (empty) removed tracks.  This method
    846   * is only for grid items and should only be called after placement.
    847   * aNumRemovedTracks contains a count for each line in the grid how many
    848   * tracks were removed between the start of the grid and that line.
    849   */
    850  void AdjustForRemovedTracks(const nsTArray<uint32_t>& aNumRemovedTracks) {
    851    MOZ_ASSERT(mStart != kAutoLine, "invalid resolved line for a grid item");
    852    MOZ_ASSERT(mEnd != kAutoLine, "invalid resolved line for a grid item");
    853    uint32_t numRemovedTracks = aNumRemovedTracks[mStart];
    854    MOZ_ASSERT(numRemovedTracks == aNumRemovedTracks[mEnd],
    855               "tracks that a grid item spans can't be removed");
    856    mStart -= numRemovedTracks;
    857    mEnd -= numRemovedTracks;
    858  }
    859  /**
    860   * Translate the lines to account for (empty) removed tracks.  This method
    861   * is only for abs.pos. children and should only be called after placement.
    862   * Same as for in-flow items, but we don't touch 'auto' lines here and we
    863   * also need to adjust areas that span into the removed tracks.
    864   */
    865  void AdjustAbsPosForRemovedTracks(
    866      const nsTArray<uint32_t>& aNumRemovedTracks) {
    867    if (mStart != kAutoLine) {
    868      mStart -= aNumRemovedTracks[mStart];
    869    }
    870    if (mEnd != kAutoLine) {
    871      MOZ_ASSERT(mStart == kAutoLine || mEnd > mStart, "invalid line range");
    872      mEnd -= aNumRemovedTracks[mEnd];
    873    }
    874  }
    875 
    876  /**
    877   * Return the contribution of this line range for step 2 in
    878   * https://drafts.csswg.org/css-grid-2/#auto-placement-algo
    879   */
    880  uint32_t HypotheticalEnd() const { return mEnd; }
    881 
    882  /**
    883   * Given an array of track sizes, return the starting position and length
    884   * of the tracks in this line range.
    885   */
    886  void ToPositionAndLength(const TrackPlan& aTrackPlan, nscoord* aPos,
    887                           nscoord* aLength) const;
    888 
    889  /**
    890   * Given an array of track sizes, return the length of the tracks in this
    891   * line range.
    892   */
    893  nscoord ToLength(const TrackPlan& aTrackPlan) const;
    894 
    895  /**
    896   * Given an array of track sizes and a grid origin coordinate, adjust the
    897   * abs.pos. containing block along an axis given by aPos and aLength.
    898   * aPos and aLength should already be initialized to the grid container
    899   * containing block for this axis before calling this method.
    900   */
    901  void ToPositionAndLengthForAbsPos(const Tracks& aTracks, nscoord aGridOrigin,
    902                                    nscoord* aPos, nscoord* aLength) const;
    903 
    904  void Translate(int32_t aOffset) {
    905    MOZ_ASSERT(IsDefinite());
    906    mStart += aOffset;
    907    mEnd += aOffset;
    908  }
    909 
    910  /** Swap the start/end sides of this range. */
    911  void ReverseDirection(uint32_t aGridEnd) {
    912    MOZ_ASSERT(IsDefinite());
    913    MOZ_ASSERT(aGridEnd >= mEnd);
    914    uint32_t newStart = aGridEnd - mEnd;
    915    mEnd = aGridEnd - mStart;
    916    mStart = newStart;
    917  }
    918 
    919  /**
    920   * @note We'll use the signed member while resolving definite positions
    921   * to line numbers (1-based), which may become negative for implicit lines
    922   * to the top/left of the explicit grid.  PlaceGridItems() then translates
    923   * the whole grid to a 0,0 origin and we'll use the unsigned member from
    924   * there on.
    925   */
    926  union {
    927    uint32_t mStart;
    928    int32_t mUntranslatedStart;
    929  };
    930  union {
    931    uint32_t mEnd;
    932    int32_t mUntranslatedEnd;
    933  };
    934 
    935 protected:
    936  LineRange() : mStart(0), mEnd(0) {}
    937 };
    938 
    939 /**
    940 * Helper class to construct a LineRange from translated lines.
    941 * The ctor only accepts translated definite line numbers.
    942 */
    943 struct nsGridContainerFrame::TranslatedLineRange : public LineRange {
    944  TranslatedLineRange(uint32_t aStart, uint32_t aEnd) {
    945    MOZ_ASSERT(aStart < aEnd && aEnd <= kTranslatedMaxLine);
    946    mStart = aStart;
    947    mEnd = aEnd;
    948  }
    949 };
    950 
    951 /**
    952 * A GridArea is the area in the grid for a grid item.
    953 * The area is represented by two LineRanges, both of which can be auto
    954 * (@see LineRange) in intermediate steps while the item is being placed.
    955 * @see PlaceGridItems
    956 */
    957 struct nsGridContainerFrame::GridArea {
    958  GridArea(const LineRange& aCols, const LineRange& aRows)
    959      : mCols(aCols), mRows(aRows) {}
    960  bool IsDefinite() const { return mCols.IsDefinite() && mRows.IsDefinite(); }
    961  LineRange& LineRangeForAxis(LogicalAxis aAxis) {
    962    return aAxis == LogicalAxis::Inline ? mCols : mRows;
    963  }
    964  const LineRange& LineRangeForAxis(LogicalAxis aAxis) const {
    965    return aAxis == LogicalAxis::Inline ? mCols : mRows;
    966  }
    967  LineRange mCols;
    968  LineRange mRows;
    969 };
    970 
    971 struct nsGridContainerFrame::GridItemInfo {
    972  /**
    973   * Item state per axis.
    974   */
    975  enum StateBits : uint16_t {
    976    // Does the item span a flex track?
    977    eIsFlexing = 0x1,
    978 
    979    // First or last baseline alignment preference. They are mutually exclusive.
    980    // This does *NOT* represent the baseline alignment group. See the member
    981    // variable for that.
    982    // <https://drafts.csswg.org/css-align-3/#baseline-alignment-preference>
    983    eFirstBaseline = 0x2,
    984    eLastBaseline = 0x4,
    985    eIsBaselineAligned = eFirstBaseline | eLastBaseline,
    986 
    987    // One of e[Self|Content]Baseline is set when eIsBaselineAligned is true
    988    eSelfBaseline = 0x8,  // is it *-self:[last ]baseline alignment?
    989    // Ditto *-content:[last ]baseline. Mutually exclusive w. eSelfBaseline.
    990    eContentBaseline = 0x10,
    991 
    992    // The baseline affects the margin or padding on the item's end side when
    993    // this bit is set.  In a grid-axis it's always set for eLastBaseline and
    994    // always unset for eFirstBaseline.  In a masonry-axis, it's set for
    995    // baseline groups in the EndStretch set and unset for the StartStretch set.
    996    eEndSideBaseline = 0x20,
    997 
    998    // Set when the grid item is in the last baseline sharing group, otherwise
    999    // assume the first baseline sharing group. The baseline sharing group might
   1000    // differ from the specified baseline alignment due to baseline alignment
   1001    // rules.
   1002    eLastBaselineSharingGroup = 0x40,
   1003 
   1004    eAllBaselineBits = eIsBaselineAligned | eSelfBaseline | eContentBaseline |
   1005                       eEndSideBaseline | eLastBaselineSharingGroup,
   1006 
   1007    // Automatic Minimum Size is content based. If not set, automatic minimum
   1008    // size is zero.
   1009    // https://drafts.csswg.org/css-grid-2/#min-size-auto
   1010    // https://drafts.csswg.org/css-grid-2/#content-based-minimum-size
   1011    eContentBasedAutoMinSize = 0x80,
   1012    // Clamp per https://drafts.csswg.org/css-grid-2/#min-size-auto
   1013    eClampMarginBoxMinSize = 0x100,
   1014    eIsSubgrid = 0x200,
   1015    // set on subgrids and items in subgrids if they are adjacent to the grid
   1016    // start/end edge (excluding grid-aligned abs.pos. frames)
   1017    eStartEdge = 0x400,
   1018    eEndEdge = 0x800,
   1019    eEdgeBits = eStartEdge | eEndEdge,
   1020    // Set if this item was auto-placed in this axis.
   1021    eAutoPlacement = 0x1000,
   1022    // Set if this item is the last item in its track (masonry layout only)
   1023    eIsLastItemInMasonryTrack = 0x2000,
   1024 
   1025    // Bits set during the track sizing step.
   1026    eTrackSizingBits =
   1027        eIsFlexing | eContentBasedAutoMinSize | eClampMarginBoxMinSize,
   1028  };
   1029 
   1030  GridItemInfo(nsIFrame* aFrame, const GridArea& aArea);
   1031 
   1032  GridItemInfo(const GridItemInfo& aOther)
   1033      : mFrame(aOther.mFrame), mArea(aOther.mArea) {
   1034    mBaselineOffset = aOther.mBaselineOffset;
   1035    mState = aOther.mState;
   1036  }
   1037 
   1038  GridItemInfo& operator=(const GridItemInfo&) = delete;
   1039 
   1040  static bool BaselineAlignmentAffectsEndSide(StateBits state) {
   1041    return state & StateBits::eEndSideBaseline;
   1042  }
   1043 
   1044  /**
   1045   * Inhibit subgrid layout unless the item is placed in the first "track" in
   1046   * a parent masonry-axis, or has definite placement or spans all tracks in
   1047   * the parent grid-axis.
   1048   * TODO: this is stricter than what the Masonry proposal currently states
   1049   *       (bug 1627581)
   1050   */
   1051  void MaybeInhibitSubgridInMasonry(nsGridContainerFrame* aParent,
   1052                                    uint32_t aGridAxisTrackCount);
   1053 
   1054  /**
   1055   * Inhibit subgridding in aAxis for this item.
   1056   */
   1057  void InhibitSubgrid(nsGridContainerFrame* aParent, LogicalAxis aAxis);
   1058 
   1059  /**
   1060   * Return a copy of this item with its row/column data swapped.
   1061   */
   1062  GridItemInfo Transpose() const {
   1063    GridItemInfo info(mFrame, GridArea(mArea.mRows, mArea.mCols));
   1064    info.mState[LogicalAxis::Block] = mState[LogicalAxis::Inline];
   1065    info.mState[LogicalAxis::Inline] = mState[LogicalAxis::Block];
   1066    info.mBaselineOffset[LogicalAxis::Block] =
   1067        mBaselineOffset[LogicalAxis::Inline];
   1068    info.mBaselineOffset[LogicalAxis::Inline] =
   1069        mBaselineOffset[LogicalAxis::Block];
   1070    return info;
   1071  }
   1072 
   1073  // Reset bits in mState in aAxis that were set during the track sizing step.
   1074  void ResetTrackSizingBits(LogicalAxis aAxis);
   1075 
   1076  /** Swap the start/end sides in aAxis. */
   1077  inline void ReverseDirection(LogicalAxis aAxis, uint32_t aGridEnd);
   1078 
   1079  // Is this item a subgrid in the given container axis?
   1080  bool IsSubgrid(LogicalAxis aAxis) const {
   1081    return mState[aAxis] & StateBits::eIsSubgrid;
   1082  }
   1083 
   1084  // Is this item a subgrid in either axis?
   1085  bool IsSubgrid() const {
   1086    return IsSubgrid(LogicalAxis::Inline) || IsSubgrid(LogicalAxis::Block);
   1087  }
   1088 
   1089  // Return the (inner) grid container frame associated with this subgrid item.
   1090  nsGridContainerFrame* SubgridFrame() const {
   1091    MOZ_ASSERT(IsSubgrid());
   1092    nsGridContainerFrame* gridFrame = GetGridContainerFrame(mFrame);
   1093    MOZ_ASSERT(gridFrame && gridFrame->IsSubgrid());
   1094    return gridFrame;
   1095  }
   1096 
   1097  /**
   1098   * Adjust our grid areas to account for removed auto-fit tracks in aAxis.
   1099   */
   1100  void AdjustForRemovedTracks(LogicalAxis aAxis,
   1101                              const nsTArray<uint32_t>& aNumRemovedTracks);
   1102 
   1103  /**
   1104   * If the item is [align|justify]-self:[last ]baseline aligned in the given
   1105   * axis then set aBaselineOffset to the baseline offset and return aAlign.
   1106   * Otherwise, return a fallback alignment.
   1107   */
   1108  StyleAlignFlags GetSelfBaseline(StyleAlignFlags aAlign, LogicalAxis aAxis,
   1109                                  nscoord* aBaselineOffset) const {
   1110    MOZ_ASSERT(aAlign == StyleAlignFlags::BASELINE ||
   1111               aAlign == StyleAlignFlags::LAST_BASELINE);
   1112    if (!(mState[aAxis] & eSelfBaseline)) {
   1113      return aAlign == StyleAlignFlags::BASELINE ? StyleAlignFlags::SELF_START
   1114                                                 : StyleAlignFlags::SELF_END;
   1115    }
   1116    *aBaselineOffset = mBaselineOffset[aAxis];
   1117    return aAlign;
   1118  }
   1119 
   1120  // Return true if we should use MinContribution on items that do not span
   1121  // any flex tracks to determine the minimum contribution, and if we should
   1122  // set the eContentBasedAutoMinSize flag on grid items.
   1123  //
   1124  // In part this is determined by whether or not the minimum contribution
   1125  // of the item is content-based.
   1126  // https://drafts.csswg.org/css-grid-2/#min-size-auto
   1127  //
   1128  // @note the caller should also check that the item has a span length of 1,
   1129  // and that the item's track has a min track sizing function that is 'auto'.
   1130  bool MinContributionDependsOnAutoMinSize(WritingMode aContainerWM,
   1131                                           LogicalAxis aContainerAxis) const {
   1132    MOZ_ASSERT(
   1133        mArea.LineRangeForAxis(aContainerAxis).Extent() == 1,
   1134        "Should not be called with grid items that span multiple tracks.");
   1135    const LogicalAxis itemAxis =
   1136        aContainerWM.ConvertAxisTo(aContainerAxis, mFrame->GetWritingMode());
   1137    const auto* styleFrame = mFrame->IsTableWrapperFrame()
   1138                                 ? mFrame->PrincipalChildList().FirstChild()
   1139                                 : mFrame;
   1140    const auto* pos = styleFrame->StylePosition();
   1141    const auto anchorResolutionParams =
   1142        AnchorPosResolutionParams::From(styleFrame);
   1143    const auto size =
   1144        pos->Size(aContainerAxis, aContainerWM, anchorResolutionParams);
   1145    // max-content and min-content should behave as initial value in block axis.
   1146    // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
   1147    // for block size dimension on sizing properties (e.g. height), so we
   1148    // treat it as `auto`.
   1149    bool isAuto = size->BehavesLikeInitialValue(itemAxis);
   1150    // This check for HasPercent is intended to correspond to whether or not
   1151    // the item's preferred size depends on the size of its containing block.
   1152    //
   1153    // TODO alaskanemily: This probably shouldn't be a special case here.
   1154    // This is part of how EnsureContributions with the MinSize flag is
   1155    // implemented, where this forces ResolveIntrinsicSizeForNonSpanningItems
   1156    // to use MinSize instead of Min/MaxContentContribution, which
   1157    // EnsureContributions will then translate to/from MinContentContribution
   1158    //
   1159    // https://drafts.csswg.org/css-grid-2/#algo-single-span-items
   1160    // Section "For auto minimums"
   1161    if (!isAuto && !size->HasPercent()) {
   1162      return false;
   1163    }
   1164    const auto minSize =
   1165        pos->MinSize(aContainerAxis, aContainerWM, anchorResolutionParams);
   1166    // max-content and min-content should behave as initial value in block axis.
   1167    // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
   1168    // for block size dimension on sizing properties (e.g. height), so we
   1169    // treat it as `auto`.
   1170    isAuto = minSize->BehavesLikeInitialValue(itemAxis);
   1171    return isAuto && !mFrame->StyleDisplay()->IsScrollableOverflow();
   1172  }
   1173 
   1174 #ifdef DEBUG
   1175  void Dump() const;
   1176 #endif
   1177 
   1178  static bool IsStartRowLessThan(const GridItemInfo* a, const GridItemInfo* b) {
   1179    return a->mArea.mRows.mStart < b->mArea.mRows.mStart;
   1180  }
   1181 
   1182  // Sorting functions for 'masonry-auto-flow:next'.  We sort the items that
   1183  // were placed into the first track by the Grid placement algorithm first
   1184  // (to honor that placement).  All other items will be placed by the Masonry
   1185  // layout algorithm (their Grid placement in the masonry axis is irrelevant).
   1186  static bool RowMasonryOrdered(const GridItemInfo* a, const GridItemInfo* b) {
   1187    return a->mArea.mRows.mStart == 0 && b->mArea.mRows.mStart != 0 &&
   1188           !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
   1189  }
   1190  static bool ColMasonryOrdered(const GridItemInfo* a, const GridItemInfo* b) {
   1191    return a->mArea.mCols.mStart == 0 && b->mArea.mCols.mStart != 0 &&
   1192           !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
   1193  }
   1194 
   1195  // Sorting functions for 'masonry-auto-flow:definite-first'.  Similar to
   1196  // the above, but here we also sort items with a definite item placement in
   1197  // the grid axis in track order before 'auto'-placed items. We also sort all
   1198  // continuations first since they use the same placement as their
   1199  // first-in-flow (we treat them as "definite" regardless of eAutoPlacement).
   1200  static bool RowMasonryDefiniteFirst(const GridItemInfo* a,
   1201                                      const GridItemInfo* b) {
   1202    bool isContinuationA = a->mFrame->GetPrevInFlow();
   1203    bool isContinuationB = b->mFrame->GetPrevInFlow();
   1204    if (isContinuationA != isContinuationB) {
   1205      return isContinuationA;
   1206    }
   1207    auto masonryA = a->mArea.mRows.mStart;
   1208    auto gridA = a->mState[LogicalAxis::Inline] & StateBits::eAutoPlacement;
   1209    auto masonryB = b->mArea.mRows.mStart;
   1210    auto gridB = b->mState[LogicalAxis::Inline] & StateBits::eAutoPlacement;
   1211    return (masonryA == 0 ? masonryB != 0 : (masonryB != 0 && gridA < gridB)) &&
   1212           !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
   1213  }
   1214  static bool ColMasonryDefiniteFirst(const GridItemInfo* a,
   1215                                      const GridItemInfo* b) {
   1216    MOZ_ASSERT(!a->mFrame->GetPrevInFlow() && !b->mFrame->GetPrevInFlow(),
   1217               "fragmentation not supported in inline axis");
   1218    auto masonryA = a->mArea.mCols.mStart;
   1219    auto gridA = a->mState[LogicalAxis::Block] & StateBits::eAutoPlacement;
   1220    auto masonryB = b->mArea.mCols.mStart;
   1221    auto gridB = b->mState[LogicalAxis::Block] & StateBits::eAutoPlacement;
   1222    return (masonryA == 0 ? masonryB != 0 : (masonryB != 0 && gridA < gridB)) &&
   1223           !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
   1224  }
   1225 
   1226  // Return true if this items block size is dependent on the size of the
   1227  // container it is in.
   1228  bool IsBSizeDependentOnContainerSize(WritingMode aContainerWM) const {
   1229    const auto IsDependentOnContainerSize = [](const auto& size) -> bool {
   1230      // XXXdholbert The BehavesLikeStretchOnInlineAxis usage seems like
   1231      // maybe it should be considering block-axis instead?
   1232      return size.HasPercent() || size.BehavesLikeStretchOnInlineAxis();
   1233    };
   1234 
   1235    const nsStylePosition* stylePos = mFrame->StylePosition();
   1236    const auto anchorResolutionParams = AnchorPosResolutionParams::From(mFrame);
   1237    bool isItemAutoSize = IsDependentOnContainerSize(*stylePos->BSize(
   1238                              aContainerWM, anchorResolutionParams)) ||
   1239                          IsDependentOnContainerSize(*stylePos->MinBSize(
   1240                              aContainerWM, anchorResolutionParams)) ||
   1241                          IsDependentOnContainerSize(*stylePos->MaxBSize(
   1242                              aContainerWM, anchorResolutionParams));
   1243 
   1244    return isItemAutoSize;
   1245  }
   1246 
   1247  nsIFrame* const mFrame;
   1248  GridArea mArea;
   1249 
   1250  // Offset from the margin edge to the baseline (LogicalAxis index).  It's from
   1251  // the start edge for first baseline sharing group, otherwise from the end
   1252  // edge.
   1253  // It's mutable since we update the value fairly late (just before reflowing
   1254  // the item).
   1255  mutable PerLogicalAxis<nscoord> mBaselineOffset;
   1256 
   1257  // State bits per axis.
   1258  mutable PerLogicalAxis<StateBits> mState;
   1259 };
   1260 
   1261 using GridItemInfo = nsGridContainerFrame::GridItemInfo;
   1262 using ItemState = GridItemInfo::StateBits;
   1263 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ItemState)
   1264 
   1265 GridItemInfo::GridItemInfo(nsIFrame* aFrame, const GridArea& aArea)
   1266    : mFrame(aFrame), mArea(aArea), mBaselineOffset{0, 0} {
   1267  mState[LogicalAxis::Block] =
   1268      StateBits(mArea.mRows.mStart == kAutoLine ? eAutoPlacement : 0);
   1269  mState[LogicalAxis::Inline] =
   1270      StateBits(mArea.mCols.mStart == kAutoLine ? eAutoPlacement : 0);
   1271 
   1272  if (auto* gridFrame = GetGridContainerFrame(mFrame)) {
   1273    auto parentWM = aFrame->GetParent()->GetWritingMode();
   1274    bool isOrthogonal = parentWM.IsOrthogonalTo(gridFrame->GetWritingMode());
   1275    if (gridFrame->IsColSubgrid()) {
   1276      mState[isOrthogonal ? LogicalAxis::Block : LogicalAxis::Inline] |=
   1277          StateBits::eIsSubgrid;
   1278    }
   1279    if (gridFrame->IsRowSubgrid()) {
   1280      mState[isOrthogonal ? LogicalAxis::Inline : LogicalAxis::Block] |=
   1281          StateBits::eIsSubgrid;
   1282    }
   1283  }
   1284 }
   1285 
   1286 void GridItemInfo::ResetTrackSizingBits(LogicalAxis aAxis) {
   1287  mState[aAxis] &= ~StateBits::eTrackSizingBits;
   1288 }
   1289 
   1290 void GridItemInfo::ReverseDirection(LogicalAxis aAxis, uint32_t aGridEnd) {
   1291  mArea.LineRangeForAxis(aAxis).ReverseDirection(aGridEnd);
   1292  ItemState& state = mState[aAxis];
   1293  ItemState newState = state & ~ItemState::eEdgeBits;
   1294  if (state & ItemState::eStartEdge) {
   1295    newState |= ItemState::eEndEdge;
   1296  }
   1297  if (state & ItemState::eEndEdge) {
   1298    newState |= ItemState::eStartEdge;
   1299  }
   1300  state = newState;
   1301 }
   1302 
   1303 void GridItemInfo::InhibitSubgrid(nsGridContainerFrame* aParent,
   1304                                  LogicalAxis aAxis) {
   1305  MOZ_ASSERT(IsSubgrid(aAxis));
   1306  auto bit = NS_STATE_GRID_IS_COL_SUBGRID;
   1307  if (aParent->GetWritingMode().IsOrthogonalTo(mFrame->GetWritingMode()) !=
   1308      (aAxis == LogicalAxis::Block)) {
   1309    bit = NS_STATE_GRID_IS_ROW_SUBGRID;
   1310  }
   1311  MOZ_ASSERT(SubgridFrame()->HasAnyStateBits(bit));
   1312  SubgridFrame()->RemoveStateBits(bit);
   1313  mState[aAxis] &= StateBits(~StateBits::eIsSubgrid);
   1314 }
   1315 
   1316 void GridItemInfo::MaybeInhibitSubgridInMasonry(nsGridContainerFrame* aParent,
   1317                                                uint32_t aGridAxisTrackCount) {
   1318  if (IsSubgrid(LogicalAxis::Inline) && aParent->IsRowMasonry() &&
   1319      mArea.mRows.mStart != 0 && mArea.mCols.Extent() != aGridAxisTrackCount &&
   1320      (mState[LogicalAxis::Inline] & eAutoPlacement)) {
   1321    InhibitSubgrid(aParent, LogicalAxis::Inline);
   1322    return;
   1323  }
   1324  if (IsSubgrid(LogicalAxis::Block) && aParent->IsColMasonry() &&
   1325      mArea.mCols.mStart != 0 && mArea.mRows.Extent() != aGridAxisTrackCount &&
   1326      (mState[LogicalAxis::Block] & eAutoPlacement)) {
   1327    InhibitSubgrid(aParent, LogicalAxis::Block);
   1328  }
   1329 }
   1330 
   1331 // Each subgrid stores this data about its items etc on a frame property.
   1332 struct nsGridContainerFrame::Subgrid {
   1333  Subgrid(const GridArea& aArea, bool aIsOrthogonal, WritingMode aCBWM)
   1334      : mArea(aArea),
   1335        mGridColEnd(0),
   1336        mGridRowEnd(0),
   1337        mMarginBorderPadding(aCBWM),
   1338        mIsOrthogonal(aIsOrthogonal) {}
   1339 
   1340  // Return the relevant line range for the subgrid column axis.
   1341  const LineRange& SubgridCols() const {
   1342    return mIsOrthogonal ? mArea.mRows : mArea.mCols;
   1343  }
   1344  // Return the relevant line range for the subgrid row axis.
   1345  const LineRange& SubgridRows() const {
   1346    return mIsOrthogonal ? mArea.mCols : mArea.mRows;
   1347  }
   1348 
   1349  // The subgrid's items.
   1350  nsTArray<GridItemInfo> mGridItems;
   1351  // The subgrid's abs.pos. items.
   1352  nsTArray<GridItemInfo> mAbsPosItems;
   1353  // The subgrid's area as a grid item, i.e. in its parent's grid space.
   1354  GridArea mArea;
   1355  // The (inner) grid size for the subgrid, zero-based.
   1356  uint32_t mGridColEnd;
   1357  uint32_t mGridRowEnd;
   1358  // The margin+border+padding for the subgrid box in its parent grid's WM.
   1359  // (This also includes the size of any scrollbars.)
   1360  LogicalMargin mMarginBorderPadding;
   1361  // Does the subgrid frame have orthogonal writing-mode to its parent grid
   1362  // container?
   1363  bool mIsOrthogonal;
   1364 
   1365  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, Subgrid)
   1366 };
   1367 using Subgrid = nsGridContainerFrame::Subgrid;
   1368 
   1369 void GridItemInfo::AdjustForRemovedTracks(
   1370    LogicalAxis aAxis, const nsTArray<uint32_t>& aNumRemovedTracks) {
   1371  const bool abspos = mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
   1372  auto& lines = mArea.LineRangeForAxis(aAxis);
   1373  if (abspos) {
   1374    lines.AdjustAbsPosForRemovedTracks(aNumRemovedTracks);
   1375  } else {
   1376    lines.AdjustForRemovedTracks(aNumRemovedTracks);
   1377  }
   1378  if (IsSubgrid()) {
   1379    auto* subgrid = SubgridFrame()->GetProperty(Subgrid::Prop());
   1380    if (subgrid) {
   1381      auto& lines = subgrid->mArea.LineRangeForAxis(aAxis);
   1382      if (abspos) {
   1383        lines.AdjustAbsPosForRemovedTracks(aNumRemovedTracks);
   1384      } else {
   1385        lines.AdjustForRemovedTracks(aNumRemovedTracks);
   1386      }
   1387    }
   1388  }
   1389 }
   1390 
   1391 /**
   1392 * Track size data for use by subgrids (which don't do sizing of their own
   1393 * in a subgridded axis).  A non-subgrid container stores its resolved sizes,
   1394 * but only if it has any subgrid children.  A subgrid always stores one.
   1395 * In a subgridded axis, we copy the parent's sizes (see CopyUsedTrackSizes).
   1396 *
   1397 * This struct us stored on a frame property, which may be null before the track
   1398 * sizing step for the given container.  A null property is semantically
   1399 * equivalent to mCanResolveLineRangeSize being false in both axes.
   1400 * @note the axis used to access this data is in the grid container's own
   1401 * writing-mode, same as in other track-sizing functions.
   1402 */
   1403 struct nsGridContainerFrame::UsedTrackSizes {
   1404  UsedTrackSizes() : mCanResolveLineRangeSize{false, false} {}
   1405 
   1406  /**
   1407   * Setup mTrackPlans by copying track sizes from aFrame's grid container
   1408   * parent when aAxis is subgridded (and recurse if the parent is a subgrid
   1409   * that doesn't have sizes yet), or by running the Track Sizing Algo when
   1410   * the axis is not subgridded (for a subgrid).
   1411   * Set mCanResolveLineRangeSize[aAxis] to true once we have obtained
   1412   * sizes for an axis (if it's already true then this method is a NOP).
   1413   */
   1414  void ResolveTrackSizesForAxis(nsGridContainerFrame* aFrame, LogicalAxis aAxis,
   1415                                gfxContext& aRC);
   1416 
   1417  /** Helper function for the above method */
   1418  void ResolveSubgridTrackSizesForAxis(nsGridContainerFrame* aFrame,
   1419                                       LogicalAxis aAxis, Subgrid* aSubgrid,
   1420                                       gfxContext& aRC,
   1421                                       nscoord aContentBoxSize);
   1422 
   1423  // This only has valid sizes when mCanResolveLineRangeSize is true in
   1424  // the same axis.  It may have zero tracks (a grid with only abs.pos.
   1425  // subgrids/items may have zero tracks).
   1426  PerLogicalAxis<TrackPlan> mTrackPlans;
   1427  // True if mTrackPlans can be used to resolve line range sizes in an axis.
   1428  PerLogicalAxis<bool> mCanResolveLineRangeSize;
   1429 
   1430  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, UsedTrackSizes)
   1431 };
   1432 using UsedTrackSizes = nsGridContainerFrame::UsedTrackSizes;
   1433 
   1434 #ifdef DEBUG
   1435 void nsGridContainerFrame::GridItemInfo::Dump() const {
   1436  auto Dump1 = [this](const char* aMsg, LogicalAxis aAxis) {
   1437    auto state = mState[aAxis];
   1438    if (!state) {
   1439      return;
   1440    }
   1441    printf("%s", aMsg);
   1442    if (state & ItemState::eEdgeBits) {
   1443      printf("subgrid-adjacent-edges(");
   1444      if (state & ItemState::eStartEdge) {
   1445        printf("start ");
   1446      }
   1447      if (state & ItemState::eEndEdge) {
   1448        printf("end");
   1449      }
   1450      printf(") ");
   1451    }
   1452    if (state & ItemState::eAutoPlacement) {
   1453      printf("masonry-auto ");
   1454    }
   1455    if (state & ItemState::eIsSubgrid) {
   1456      printf("subgrid ");
   1457    }
   1458    if (state & ItemState::eIsFlexing) {
   1459      printf("flexing ");
   1460    }
   1461    if (state & ItemState::eContentBasedAutoMinSize) {
   1462      printf("auto-min-size ");
   1463    }
   1464    if (state & ItemState::eClampMarginBoxMinSize) {
   1465      printf("clamp ");
   1466    }
   1467    if (state & ItemState::eIsLastItemInMasonryTrack) {
   1468      printf("last-in-track ");
   1469    }
   1470    if (state & ItemState::eFirstBaseline) {
   1471      printf("first baseline %s-alignment ",
   1472             (state & ItemState::eSelfBaseline) ? "self" : "content");
   1473    }
   1474    if (state & ItemState::eLastBaseline) {
   1475      printf("last baseline %s-alignment ",
   1476             (state & ItemState::eSelfBaseline) ? "self" : "content");
   1477    }
   1478    if (state & ItemState::eIsBaselineAligned) {
   1479      printf("%.2fpx", NSAppUnitsToFloatPixels(mBaselineOffset[aAxis],
   1480                                               AppUnitsPerCSSPixel()));
   1481    }
   1482    printf("\n");
   1483  };
   1484  printf("grid-row: %d %d\n", mArea.mRows.mStart, mArea.mRows.mEnd);
   1485  Dump1("  grid block-axis: ", LogicalAxis::Block);
   1486  printf("grid-column: %d %d\n", mArea.mCols.mStart, mArea.mCols.mEnd);
   1487  Dump1("  grid inline-axis: ", LogicalAxis::Inline);
   1488 }
   1489 #endif
   1490 
   1491 /**
   1492 * Encapsulates CSS track-sizing functions.
   1493 */
   1494 struct nsGridContainerFrame::TrackSizingFunctions {
   1495 private:
   1496  TrackSizingFunctions(const GridTemplate& aTemplate,
   1497                       const StyleImplicitGridTracks& aAutoSizing,
   1498                       const Maybe<size_t>& aRepeatAutoIndex, bool aIsSubgrid)
   1499      : mTemplate(aTemplate),
   1500        mTrackListValues(aTemplate.TrackListValues()),
   1501        mAutoSizing(aAutoSizing),
   1502        mExplicitGridOffset(0),
   1503        mRepeatAutoStart(aRepeatAutoIndex.valueOr(0)),
   1504        mRepeatAutoEnd(mRepeatAutoStart),
   1505        mHasRepeatAuto(aRepeatAutoIndex.isSome()) {
   1506    MOZ_ASSERT(!mHasRepeatAuto || !aIsSubgrid,
   1507               "a track-list for a subgrid can't have an <auto-repeat> track");
   1508    if (!aIsSubgrid) {
   1509      ExpandNonRepeatAutoTracks();
   1510    }
   1511 
   1512 #ifdef DEBUG
   1513    if (mHasRepeatAuto) {
   1514      MOZ_ASSERT(mExpandedTracks.Length() >= 1);
   1515      const unsigned maxTrack = kMaxLine - 1;
   1516      // If the exanded tracks are out of range of the maximum track, we
   1517      // can't compare the repeat-auto start. It will be removed later during
   1518      // grid item placement in that situation.
   1519      if (mExpandedTracks.Length() < maxTrack) {
   1520        MOZ_ASSERT(mRepeatAutoStart < mExpandedTracks.Length());
   1521      }
   1522    }
   1523 #endif
   1524  }
   1525 
   1526 public:
   1527  TrackSizingFunctions(const GridTemplate& aGridTemplate,
   1528                       const StyleImplicitGridTracks& aAutoSizing,
   1529                       bool aIsSubgrid)
   1530      : TrackSizingFunctions(aGridTemplate, aAutoSizing,
   1531                             aGridTemplate.RepeatAutoIndex(), aIsSubgrid) {}
   1532 
   1533 private:
   1534  enum { ForSubgridFallbackTag };
   1535  TrackSizingFunctions(const GridTemplate& aGridTemplate,
   1536                       const StyleImplicitGridTracks& aAutoSizing,
   1537                       decltype(ForSubgridFallbackTag))
   1538      : TrackSizingFunctions(aGridTemplate, aAutoSizing, Nothing(),
   1539                             /* aIsSubgrid */ true) {}
   1540 
   1541 public:
   1542  /**
   1543   * This is used in a subgridded axis to resolve sizes before its parent's
   1544   * sizes are known for intrinsic sizing purposes.  It copies the slice of
   1545   * the nearest non-subgridded axis' track sizing functions spanned by
   1546   * the subgrid.
   1547   *
   1548   * FIXME: this was written before there was a spec... the spec now says:
   1549   * "If calculating the layout of a grid item in this step depends on
   1550   *  the available space in the block axis, assume the available space
   1551   *  that it would have if any row with a definite max track sizing
   1552   *  function had that size and all other rows were infinite."
   1553   * https://drafts.csswg.org/css-grid-2/#subgrid-sizing
   1554   */
   1555  static TrackSizingFunctions ForSubgridFallback(
   1556      nsGridContainerFrame* aSubgridFrame, const Subgrid* aSubgrid,
   1557      nsGridContainerFrame* aParentGridContainer, LogicalAxis aParentAxis) {
   1558    MOZ_ASSERT(aSubgrid);
   1559    MOZ_ASSERT(aSubgridFrame->IsSubgrid(aSubgrid->mIsOrthogonal
   1560                                            ? GetOrthogonalAxis(aParentAxis)
   1561                                            : aParentAxis));
   1562    nsGridContainerFrame* parent = aParentGridContainer;
   1563    auto parentAxis = aParentAxis;
   1564    LineRange range = aSubgrid->mArea.LineRangeForAxis(parentAxis);
   1565    // Find our nearest non-subgridded axis and use its track sizing functions.
   1566    while (parent->IsSubgrid(parentAxis)) {
   1567      const auto* parentSubgrid = parent->GetProperty(Subgrid::Prop());
   1568      auto* grandParent = parent->ParentGridContainerForSubgrid();
   1569      auto grandParentWM = grandParent->GetWritingMode();
   1570      bool isSameDirInAxis =
   1571          parent->GetWritingMode().ParallelAxisStartsOnSameSide(parentAxis,
   1572                                                                grandParentWM);
   1573      if (MOZ_UNLIKELY(!isSameDirInAxis)) {
   1574        auto end = parentAxis == LogicalAxis::Block
   1575                       ? parentSubgrid->mGridRowEnd
   1576                       : parentSubgrid->mGridColEnd;
   1577        range.ReverseDirection(end);
   1578        // range is now in the same direction as the grand-parent's axis
   1579      }
   1580      auto grandParentAxis = parentSubgrid->mIsOrthogonal
   1581                                 ? GetOrthogonalAxis(parentAxis)
   1582                                 : parentAxis;
   1583      const auto& parentRange =
   1584          parentSubgrid->mArea.LineRangeForAxis(grandParentAxis);
   1585      range.Translate(parentRange.mStart);
   1586      // range is now in the grand-parent's coordinates
   1587      parentAxis = grandParentAxis;
   1588      parent = grandParent;
   1589    }
   1590    const auto* pos = parent->StylePosition();
   1591    const auto isInlineAxis = parentAxis == LogicalAxis::Inline;
   1592    const auto& szf =
   1593        isInlineAxis ? pos->mGridTemplateRows : pos->mGridTemplateColumns;
   1594    const auto& autoSizing =
   1595        isInlineAxis ? pos->mGridAutoColumns : pos->mGridAutoRows;
   1596    return TrackSizingFunctions(szf, autoSizing, ForSubgridFallbackTag);
   1597  }
   1598 
   1599  /**
   1600   * Initialize the number of auto-fill/fit tracks to use.
   1601   * This can be zero if no auto-fill/fit track was specified, or if the repeat
   1602   * begins after the maximum allowed track.
   1603   */
   1604  void InitRepeatTracks(const NonNegativeLengthPercentageOrNormal& aGridGap,
   1605                        nscoord aMinSize, nscoord aSize, nscoord aMaxSize) {
   1606    const uint32_t maxTrack = kMaxLine - 1;
   1607    // Check for a repeat after the maximum allowed track.
   1608    if (MOZ_UNLIKELY(mRepeatAutoStart >= maxTrack)) {
   1609      mHasRepeatAuto = false;
   1610      mRepeatAutoStart = 0;
   1611      mRepeatAutoEnd = 0;
   1612      return;
   1613    }
   1614    uint32_t repeatTracks =
   1615        CalculateRepeatFillCount(aGridGap, aMinSize, aSize, aMaxSize) *
   1616        NumRepeatTracks();
   1617    // Clamp the number of repeat tracks to the maximum possible track.
   1618    repeatTracks = std::min(repeatTracks, maxTrack - mRepeatAutoStart);
   1619    SetNumRepeatTracks(repeatTracks);
   1620    // Blank out the removed flags for each of these tracks.
   1621    mRemovedRepeatTracks.SetLength(repeatTracks);
   1622    for (auto& track : mRemovedRepeatTracks) {
   1623      track = false;
   1624    }
   1625  }
   1626 
   1627  uint32_t CalculateRepeatFillCount(
   1628      const NonNegativeLengthPercentageOrNormal& aGridGap, nscoord aMinSize,
   1629      nscoord aSize, nscoord aMaxSize) const {
   1630    if (!mHasRepeatAuto) {
   1631      return 0;
   1632    }
   1633    // At this point no tracks will have been collapsed, so the RepeatEndDelta
   1634    // should not be negative.
   1635    MOZ_ASSERT(RepeatEndDelta() >= 0);
   1636    // Note that this uses NumRepeatTracks and mRepeatAutoStart/End, although
   1637    // the result of this method is used to change those values to a fully
   1638    // expanded value. Spec quotes are from
   1639    // https://drafts.csswg.org/css-grid-2/#repeat-notation
   1640    const uint32_t numTracks = mExpandedTracks.Length() + RepeatEndDelta();
   1641    MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track");
   1642    if (MOZ_UNLIKELY(numTracks >= kMaxLine)) {
   1643      // The fixed tracks plus an entire repetition is either larger or as
   1644      // large as the maximum track, so we do not need to measure how many
   1645      // repetitions will fit. This also avoids needing to check for if
   1646      // kMaxLine - numTracks would underflow at the end where we clamp the
   1647      // result.
   1648      return 1;
   1649    }
   1650    nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize;
   1651    if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) {
   1652      // "Otherwise, the specified track list repeats only once."
   1653      return 1;
   1654    }
   1655    nscoord repeatTrackSum = 0;
   1656    // Note that one repeat() track size is included in |sum| in this loop.
   1657    nscoord sum = 0;
   1658    const nscoord percentBasis = aSize;
   1659    for (uint32_t i = 0; i < numTracks; ++i) {
   1660      // "treating each track as its max track sizing function if that is
   1661      // definite or as its minimum track sizing function otherwise"
   1662      // https://drafts.csswg.org/css-grid-2/#valdef-repeat-auto-fill
   1663      nscoord trackSize;
   1664      {
   1665        const auto& sizingFunction = SizingFor(i);
   1666        const auto& maxCoord = sizingFunction.GetMax();
   1667        const auto& minCoord = sizingFunction.GetMin();
   1668        if (maxCoord.IsBreadth() && minCoord.IsBreadth()) {
   1669          // If the max is less than the min, then the max will be floored by
   1670          // the min (essentially yielding minmax(min, min))
   1671          // https://drafts.csswg.org/css-grid-2/#funcdef-grid-template-columns-minmax
   1672          const nscoord minSize =
   1673              ::ResolveToDefiniteSize(minCoord, percentBasis);
   1674          const nscoord maxSize =
   1675              ::ResolveToDefiniteSize(maxCoord, percentBasis);
   1676          trackSize = std::max(maxSize, minSize);
   1677        } else {
   1678          const auto* coord = &maxCoord;
   1679          if (!coord->IsBreadth()) {
   1680            coord = &minCoord;
   1681            if (!coord->IsBreadth()) {
   1682              return 1;
   1683            }
   1684          }
   1685          trackSize = ::ResolveToDefiniteSize(*coord, percentBasis);
   1686        }
   1687      }
   1688 
   1689      if (i >= mRepeatAutoStart && i < mRepeatAutoEnd) {
   1690        // Use a minimum 1px for the repeat() track-size.
   1691        if (trackSize < AppUnitsPerCSSPixel()) {
   1692          trackSize = AppUnitsPerCSSPixel();
   1693        }
   1694        repeatTrackSum += trackSize;
   1695      }
   1696      sum += trackSize;
   1697    }
   1698    nscoord gridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aSize);
   1699    if (numTracks > 1) {
   1700      // Add grid-gaps for all the tracks including the repeat() track.
   1701      sum += gridGap * (numTracks - 1);
   1702    }
   1703    // Calculate the max number of tracks that fits without overflow.
   1704    nscoord available = maxFill != NS_UNCONSTRAINEDSIZE ? maxFill : aMinSize;
   1705    nscoord spaceToFill = available - sum;
   1706    if (spaceToFill <= 0) {
   1707      // "if any number of repetitions would overflow, then 1 repetition"
   1708      return 1;
   1709    }
   1710    // Calculate the max number of tracks that fits without overflow.
   1711    // Since we already have one repetition in sum, we can simply add one grid
   1712    // gap for each element in the repeat.
   1713    div_t q = div(spaceToFill, repeatTrackSum + gridGap * NumRepeatTracks());
   1714    // The +1 here is for the one repeat track we already accounted for above.
   1715    uint32_t numRepeatTracks = q.quot + 1;
   1716    if (q.rem != 0 && maxFill == NS_UNCONSTRAINEDSIZE) {
   1717      // "Otherwise, if the grid container has a definite min size in
   1718      // the relevant axis, the number of repetitions is the largest possible
   1719      // positive integer that fulfills that minimum requirement."
   1720      ++numRepeatTracks;  // one more to ensure the grid is at least min-size
   1721    }
   1722    // Clamp the number of repeat tracks so that the last line <= kMaxLine.
   1723    // (note that |numTracks| already includes one repeat() track)
   1724    MOZ_ASSERT(numTracks >= NumRepeatTracks());
   1725    const uint32_t maxRepeatTrackCount = kMaxLine - numTracks;
   1726    const uint32_t maxRepetitions = maxRepeatTrackCount / NumRepeatTracks();
   1727    return std::min(numRepeatTracks, maxRepetitions);
   1728  }
   1729 
   1730  /**
   1731   * Compute the explicit grid end line number (in a zero-based grid).
   1732   * @param aGridTemplateAreasEnd 'grid-template-areas' end line in this axis
   1733   */
   1734  uint32_t ComputeExplicitGridEnd(uint32_t aGridTemplateAreasEnd) {
   1735    uint32_t end = NumExplicitTracks() + 1;
   1736    end = std::max(end, aGridTemplateAreasEnd);
   1737    end = std::min(end, uint32_t(kMaxLine));
   1738    return end;
   1739  }
   1740  const StyleTrackSize& SizingFor(uint32_t aTrackIndex) const {
   1741    static const StyleTrackSize kAutoTrackSize =
   1742        StyleTrackSize::Breadth(StyleTrackBreadth::Auto());
   1743    // |aIndex| is the relative index to mAutoSizing. A negative value means it
   1744    // is the last Nth element.
   1745    auto getImplicitSize = [this](int32_t aIndex) -> const StyleTrackSize& {
   1746      MOZ_ASSERT(!(mAutoSizing.Length() == 1 &&
   1747                   mAutoSizing.AsSpan()[0] == kAutoTrackSize),
   1748                 "It's impossible to have one track with auto value because we "
   1749                 "filter out this case during parsing");
   1750 
   1751      if (mAutoSizing.IsEmpty()) {
   1752        return kAutoTrackSize;
   1753      }
   1754 
   1755      // If multiple track sizes are given, the pattern is repeated as necessary
   1756      // to find the size of the implicit tracks.
   1757      int32_t i = aIndex % int32_t(mAutoSizing.Length());
   1758      if (i < 0) {
   1759        i += mAutoSizing.Length();
   1760      }
   1761      return mAutoSizing.AsSpan()[i];
   1762    };
   1763 
   1764    if (MOZ_UNLIKELY(aTrackIndex < mExplicitGridOffset)) {
   1765      // The last implicit grid track before the explicit grid receives the
   1766      // last specified size, and so on backwards. Therefore we pass the
   1767      // negative relative index to imply that we should get the implicit size
   1768      // from the last Nth specified grid auto size.
   1769      return getImplicitSize(int32_t(aTrackIndex) -
   1770                             int32_t(mExplicitGridOffset));
   1771    }
   1772    uint32_t index = aTrackIndex - mExplicitGridOffset;
   1773    MOZ_ASSERT(mRepeatAutoStart <= mRepeatAutoEnd);
   1774 
   1775    if (index >= mRepeatAutoStart) {
   1776      if (index < mRepeatAutoEnd) {
   1777        // Expand the repeat tracks.
   1778        const auto& indices = mExpandedTracks[mRepeatAutoStart];
   1779        const TrackListValue& value = mTrackListValues[indices.first];
   1780 
   1781        // We expect the default to be used for all track repeats.
   1782        MOZ_ASSERT(indices.second == 0);
   1783 
   1784        const auto& repeatTracks = value.AsTrackRepeat().track_sizes.AsSpan();
   1785 
   1786        // Find the repeat track to use, skipping over any collapsed tracks.
   1787        const uint32_t finalRepeatIndex = (index - mRepeatAutoStart);
   1788        uint32_t repeatWithCollapsed = 0;
   1789        // NOTE: We need SizingFor before the final collapsed tracks are known.
   1790        // We know that it's invalid to have empty mRemovedRepeatTracks when
   1791        // there are any repeat tracks, so we can detect that situation here.
   1792        if (mRemovedRepeatTracks.IsEmpty()) {
   1793          repeatWithCollapsed = finalRepeatIndex;
   1794        } else {
   1795          // Count up through the repeat tracks, until we have seen
   1796          // finalRepeatIndex number of non-collapsed tracks.
   1797          for (uint32_t repeatNoCollapsed = 0;
   1798               repeatNoCollapsed < finalRepeatIndex; repeatWithCollapsed++) {
   1799            if (!mRemovedRepeatTracks[repeatWithCollapsed]) {
   1800              repeatNoCollapsed++;
   1801            }
   1802          }
   1803          // If we stopped iterating on a collapsed track, continue to the next
   1804          // non-collapsed track.
   1805          while (mRemovedRepeatTracks[repeatWithCollapsed]) {
   1806            repeatWithCollapsed++;
   1807          }
   1808        }
   1809        return repeatTracks[repeatWithCollapsed % repeatTracks.Length()];
   1810      } else {
   1811        // The index is after the repeat auto range, adjust it to skip over the
   1812        // repeat value. This will have no effect if there is no auto repeat,
   1813        // since then RepeatEndDelta will return zero.
   1814        index -= RepeatEndDelta();
   1815      }
   1816    }
   1817    if (index >= mExpandedTracks.Length()) {
   1818      return getImplicitSize(index - mExpandedTracks.Length());
   1819    }
   1820    auto& indices = mExpandedTracks[index];
   1821    const TrackListValue& value = mTrackListValues[indices.first];
   1822    if (value.IsTrackSize()) {
   1823      MOZ_ASSERT(indices.second == 0);
   1824      return value.AsTrackSize();
   1825    }
   1826    return value.AsTrackRepeat().track_sizes.AsSpan()[indices.second];
   1827  }
   1828  const StyleTrackBreadth& MaxSizingFor(uint32_t aTrackIndex) const {
   1829    return SizingFor(aTrackIndex).GetMax();
   1830  }
   1831  const StyleTrackBreadth& MinSizingFor(uint32_t aTrackIndex) const {
   1832    return SizingFor(aTrackIndex).GetMin();
   1833  }
   1834  uint32_t NumExplicitTracks() const {
   1835    return mExpandedTracks.Length() + RepeatEndDelta();
   1836  }
   1837  uint32_t NumRepeatTracks() const { return mRepeatAutoEnd - mRepeatAutoStart; }
   1838  // The difference between mExplicitGridEnd and mSizingFunctions.Length().
   1839  int32_t RepeatEndDelta() const {
   1840    return mHasRepeatAuto ? int32_t(NumRepeatTracks()) - 1 : 0;
   1841  }
   1842  void SetNumRepeatTracks(uint32_t aNumRepeatTracks) {
   1843    MOZ_ASSERT(mHasRepeatAuto || aNumRepeatTracks == 0);
   1844    mRepeatAutoEnd = mRepeatAutoStart + aNumRepeatTracks;
   1845  }
   1846 
   1847  // Store mTrackListValues into mExpandedTracks with `repeat(INTEGER, ...)`
   1848  // tracks expanded.
   1849  void ExpandNonRepeatAutoTracks() {
   1850    for (size_t i = 0; i < mTrackListValues.Length(); ++i) {
   1851      auto& value = mTrackListValues[i];
   1852      if (value.IsTrackSize()) {
   1853        mExpandedTracks.EmplaceBack(i, 0);
   1854        continue;
   1855      }
   1856      auto& repeat = value.AsTrackRepeat();
   1857      if (!repeat.count.IsNumber()) {
   1858        MOZ_ASSERT(i == mRepeatAutoStart);
   1859        mRepeatAutoStart = mExpandedTracks.Length();
   1860        mRepeatAutoEnd = mRepeatAutoStart + repeat.track_sizes.Length();
   1861        mExpandedTracks.EmplaceBack(i, 0);
   1862        continue;
   1863      }
   1864      for (auto j : IntegerRange(repeat.count.AsNumber())) {
   1865        (void)j;
   1866        size_t trackSizesCount = repeat.track_sizes.Length();
   1867        for (auto k : IntegerRange(trackSizesCount)) {
   1868          mExpandedTracks.EmplaceBack(i, k);
   1869        }
   1870      }
   1871    }
   1872    if (MOZ_UNLIKELY(mExpandedTracks.Length() > kMaxLine - 1)) {
   1873      mExpandedTracks.TruncateLength(kMaxLine - 1);
   1874      if (mHasRepeatAuto && mRepeatAutoStart > kMaxLine - 1) {
   1875        // The `repeat(auto-fill/fit)` track is outside the clamped grid.
   1876        mHasRepeatAuto = false;
   1877      }
   1878    }
   1879  }
   1880 
   1881  // Some style data references, for easy access.
   1882  const GridTemplate& mTemplate;
   1883  const Span<const TrackListValue> mTrackListValues;
   1884  const StyleImplicitGridTracks& mAutoSizing;
   1885  // An array from expanded track sizes (without expanding auto-repeat, which is
   1886  // included just once at `mRepeatAutoStart`).
   1887  //
   1888  // Each entry contains two indices, the first into mTrackListValues, and a
   1889  // second one inside mTrackListValues' repeat value, if any, or zero
   1890  // otherwise.
   1891  nsTArray<std::pair<size_t, size_t>> mExpandedTracks;
   1892  // Offset from the start of the implicit grid to the first explicit track.
   1893  uint32_t mExplicitGridOffset;
   1894  // The index of the repeat(auto-fill/fit) track, or zero if there is none.
   1895  // Relative to mExplicitGridOffset (repeat tracks are explicit by definition).
   1896  uint32_t mRepeatAutoStart;
   1897  // The (hypothetical) index of the last such repeat() track.
   1898  uint32_t mRepeatAutoEnd;
   1899  // True if there is a specified repeat(auto-fill/fit) track.
   1900  bool mHasRepeatAuto;
   1901  // True if this track (relative to mRepeatAutoStart) is a removed auto-fit.
   1902  // Indexed relative to mExplicitGridOffset + mRepeatAutoStart.
   1903  nsTArray<bool> mRemovedRepeatTracks;
   1904 };
   1905 
   1906 /**
   1907 * Utility class to find line names.  It provides an interface to lookup line
   1908 * names with a dynamic number of repeat(auto-fill/fit) tracks taken into
   1909 * account.
   1910 */
   1911 class MOZ_STACK_CLASS nsGridContainerFrame::LineNameMap {
   1912 public:
   1913  /**
   1914   * Create a LineNameMap.
   1915   * @param aStylePosition the style for the grid container
   1916   * @param aImplicitNamedAreas the implicit areas for the grid container
   1917   * @param aGridTemplate is the grid-template-rows/columns data for this axis
   1918   * @param aParentLineNameMap the parent grid's map parallel to this map, or
   1919   *                           null if this map isn't for a subgrid
   1920   * @param aRange the subgrid's range in the parent grid, or null
   1921   * @param aIsSameDirection true if our axis progresses in the same direction
   1922   *                              in the subgrid and parent
   1923   */
   1924  LineNameMap(const nsStylePosition* aStylePosition,
   1925              const ImplicitNamedAreas* aImplicitNamedAreas,
   1926              const TrackSizingFunctions& aTracks,
   1927              const LineNameMap* aParentLineNameMap, const LineRange* aRange,
   1928              bool aIsSameDirection)
   1929      : mStylePosition(aStylePosition),
   1930        mAreas(aImplicitNamedAreas),
   1931        mRepeatAutoStart(aTracks.mRepeatAutoStart),
   1932        mRepeatAutoEnd(aTracks.mRepeatAutoEnd),
   1933        mRepeatEndDelta(aTracks.RepeatEndDelta()),
   1934        mParentLineNameMap(aParentLineNameMap),
   1935        mRange(aRange),
   1936        mIsSameDirection(aIsSameDirection),
   1937        mHasRepeatAuto(aTracks.mHasRepeatAuto) {
   1938    if (MOZ_UNLIKELY(aRange)) {  // subgrid case
   1939      mClampMinLine = 1;
   1940      mClampMaxLine = 1 + aRange->Extent();
   1941      MOZ_ASSERT(aTracks.mTemplate.IsSubgrid(), "Should be subgrid type");
   1942      ExpandRepeatLineNamesForSubgrid(*aTracks.mTemplate.AsSubgrid());
   1943      // we've expanded all subgrid auto-fill lines in
   1944      // ExpandRepeatLineNamesForSubgrid()
   1945      mRepeatAutoStart = 0;
   1946      mRepeatAutoEnd = mRepeatAutoStart;
   1947      mHasRepeatAuto = false;
   1948    } else {
   1949      mClampMinLine = kMinLine;
   1950      mClampMaxLine = kMaxLine;
   1951      if (mHasRepeatAuto) {
   1952        mTrackAutoRepeatLineNames =
   1953            aTracks.mTemplate.GetRepeatAutoValue()->line_names.AsSpan();
   1954      }
   1955      ExpandRepeatLineNames(aTracks);
   1956    }
   1957    if (mHasRepeatAuto) {
   1958      // We need mTemplateLinesEnd to be after all line names.
   1959      // mExpandedLineNames has one repetition of the repeat(auto-fit/fill)
   1960      // track name lists already, so we must subtract the number of repeat
   1961      // track name lists to get to the number of non-repeat tracks, minus 2
   1962      // because the first and last line name lists are shared with the
   1963      // preceding and following non-repeat line name lists. We then add
   1964      // mRepeatEndDelta to include the interior line name lists from repeat
   1965      // tracks.
   1966      mTemplateLinesEnd = mExpandedLineNames.Length() -
   1967                          (mTrackAutoRepeatLineNames.Length() - 2) +
   1968                          mRepeatEndDelta;
   1969    } else {
   1970      mTemplateLinesEnd = mExpandedLineNames.Length();
   1971    }
   1972    MOZ_ASSERT(mHasRepeatAuto || mRepeatEndDelta <= 0);
   1973    MOZ_ASSERT(!mHasRepeatAuto || aRange ||
   1974               (mExpandedLineNames.Length() >= 2 &&
   1975                mRepeatAutoStart <= mExpandedLineNames.Length()));
   1976  }
   1977 
   1978  // Store line names into mExpandedLineNames with `repeat(INTEGER, ...)`
   1979  // expanded for non-subgrid.
   1980  void ExpandRepeatLineNames(const TrackSizingFunctions& aTracks) {
   1981    auto lineNameLists = aTracks.mTemplate.LineNameLists(false);
   1982 
   1983    const auto& trackListValues = aTracks.mTrackListValues;
   1984    const NameList* nameListToMerge = nullptr;
   1985    // NOTE(emilio): We rely on std::move clearing out the array.
   1986    SmallPointerArray<const NameList> names;
   1987    const uint32_t end =
   1988        std::min<uint32_t>(lineNameLists.Length(), mClampMaxLine + 1);
   1989    for (uint32_t i = 0; i < end; ++i) {
   1990      if (nameListToMerge) {
   1991        names.AppendElement(nameListToMerge);
   1992        nameListToMerge = nullptr;
   1993      }
   1994      names.AppendElement(&lineNameLists[i]);
   1995      if (i >= trackListValues.Length()) {
   1996        mExpandedLineNames.AppendElement(std::move(names));
   1997        continue;
   1998      }
   1999      const auto& value = trackListValues[i];
   2000      if (value.IsTrackSize()) {
   2001        mExpandedLineNames.AppendElement(std::move(names));
   2002        continue;
   2003      }
   2004      const auto& repeat = value.AsTrackRepeat();
   2005      if (!repeat.count.IsNumber()) {
   2006        const auto repeatNames = repeat.line_names.AsSpan();
   2007        // If the repeat was truncated due to more than kMaxLine tracks, then
   2008        // the repeat will no longer be set on mRepeatAutoStart).
   2009        MOZ_ASSERT(!mHasRepeatAuto ||
   2010                   mRepeatAutoStart == mExpandedLineNames.Length());
   2011        MOZ_ASSERT(repeatNames.Length() >= 2);
   2012        for (const auto j : IntegerRange(repeatNames.Length() - 1)) {
   2013          names.AppendElement(&repeatNames[j]);
   2014          mExpandedLineNames.AppendElement(std::move(names));
   2015        }
   2016        nameListToMerge = &repeatNames[repeatNames.Length() - 1];
   2017        continue;
   2018      }
   2019      for (auto j : IntegerRange(repeat.count.AsNumber())) {
   2020        (void)j;
   2021        if (nameListToMerge) {
   2022          names.AppendElement(nameListToMerge);
   2023          nameListToMerge = nullptr;
   2024        }
   2025        size_t trackSizesCount = repeat.track_sizes.Length();
   2026        auto repeatLineNames = repeat.line_names.AsSpan();
   2027        MOZ_ASSERT(repeatLineNames.Length() == trackSizesCount ||
   2028                   repeatLineNames.Length() == trackSizesCount + 1);
   2029        for (auto k : IntegerRange(trackSizesCount)) {
   2030          names.AppendElement(&repeatLineNames[k]);
   2031          mExpandedLineNames.AppendElement(std::move(names));
   2032        }
   2033        if (repeatLineNames.Length() == trackSizesCount + 1) {
   2034          nameListToMerge = &repeatLineNames[trackSizesCount];
   2035        }
   2036      }
   2037    }
   2038 
   2039    if (MOZ_UNLIKELY(mExpandedLineNames.Length() > uint32_t(mClampMaxLine))) {
   2040      mExpandedLineNames.TruncateLength(mClampMaxLine);
   2041    }
   2042  }
   2043 
   2044  // Store line names into mExpandedLineNames with `repeat(INTEGER, ...)`
   2045  // expanded, and all `repeat(...)` expanded for subgrid.
   2046  // https://drafts.csswg.org/css-grid-2/#resolved-track-list-subgrid
   2047  void ExpandRepeatLineNamesForSubgrid(
   2048      const StyleGenericLineNameList<StyleInteger>& aStyleLineNameList) {
   2049    const auto& lineNameList = aStyleLineNameList.line_names.AsSpan();
   2050    const uint32_t maxCount = mClampMaxLine + 1;
   2051    const uint32_t end = lineNameList.Length();
   2052    for (uint32_t i = 0; i < end && mExpandedLineNames.Length() < maxCount;
   2053         ++i) {
   2054      const auto& item = lineNameList[i];
   2055      if (item.IsLineNames()) {
   2056        // <line-names> case. Just copy it.
   2057        SmallPointerArray<const NameList> names;
   2058        names.AppendElement(&item.AsLineNames());
   2059        mExpandedLineNames.AppendElement(std::move(names));
   2060        continue;
   2061      }
   2062 
   2063      MOZ_ASSERT(item.IsRepeat());
   2064      const auto& repeat = item.AsRepeat();
   2065      const auto repeatLineNames = repeat.line_names.AsSpan();
   2066 
   2067      if (repeat.count.IsNumber()) {
   2068        // Clone all <line-names>+ (repeated by N) into
   2069        // |mExpandedLineNames|.
   2070        for (uint32_t repeatCount = 0;
   2071             repeatCount < (uint32_t)repeat.count.AsNumber(); ++repeatCount) {
   2072          for (const NameList& lineNames : repeatLineNames) {
   2073            SmallPointerArray<const NameList> names;
   2074            names.AppendElement(&lineNames);
   2075            mExpandedLineNames.AppendElement(std::move(names));
   2076            if (mExpandedLineNames.Length() >= maxCount) {
   2077              break;
   2078            }
   2079          }
   2080        }
   2081        continue;
   2082      }
   2083 
   2084      MOZ_ASSERT(repeat.count.IsAutoFill(),
   2085                 "RepeatCount of subgrid is number or auto-fill");
   2086 
   2087      const size_t fillLen = repeatLineNames.Length();
   2088      const int32_t extraAutoFillLineCount =
   2089          mClampMaxLine -
   2090          (int32_t)aStyleLineNameList.expanded_line_names_length;
   2091      // Maximum possible number of repeat name lists.
   2092      // Note: |expanded_line_names_length| doesn't include auto repeat.
   2093      const uint32_t possibleRepeatLength =
   2094          std::max<int32_t>(0, extraAutoFillLineCount);
   2095      const uint32_t repeatRemainder = possibleRepeatLength % fillLen;
   2096 
   2097      // Note: Expand 'auto-fill' names for subgrid for now since
   2098      // HasNameAt() only deals with auto-repeat **tracks** currently.
   2099      const size_t len = possibleRepeatLength - repeatRemainder;
   2100      for (size_t j = 0; j < len; ++j) {
   2101        SmallPointerArray<const NameList> names;
   2102        names.AppendElement(&repeatLineNames[j % fillLen]);
   2103        mExpandedLineNames.AppendElement(std::move(names));
   2104        if (mExpandedLineNames.Length() >= maxCount) {
   2105          break;
   2106        }
   2107      }
   2108    }
   2109 
   2110    if (MOZ_UNLIKELY(mExpandedLineNames.Length() > uint32_t(mClampMaxLine))) {
   2111      mExpandedLineNames.TruncateLength(mClampMaxLine);
   2112    }
   2113  }
   2114 
   2115  /**
   2116   * Find the aNth occurrence of aName, searching forward if aNth is positive,
   2117   * and in reverse if aNth is negative (aNth == 0 is invalid), starting from
   2118   * aFromIndex (not inclusive), and return a 1-based line number.
   2119   * Also take into account there is an unconditional match at the lines in
   2120   * aImplicitLines.
   2121   * Return zero if aNth occurrences can't be found.  In that case, aNth has
   2122   * been decremented with the number of occurrences that were found (if any).
   2123   *
   2124   * E.g. to search for "A 2" forward from the start of the grid: aName is "A"
   2125   * aNth is 2 and aFromIndex is zero.  To search for "A -2", aNth is -2 and
   2126   * aFromIndex is ExplicitGridEnd + 1 (which is the line "before" the last
   2127   * line when we're searching in reverse).  For "span A 2", aNth is 2 when
   2128   * used on a grid-[row|column]-end property and -2 for a *-start property,
   2129   * and aFromIndex is the line (which we should skip) on the opposite property.
   2130   */
   2131  uint32_t FindNamedLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
   2132                         const nsTArray<uint32_t>& aImplicitLines) const {
   2133    MOZ_ASSERT(aName);
   2134    MOZ_ASSERT(!aName->IsEmpty());
   2135    MOZ_ASSERT(aNth && *aNth != 0);
   2136    if (*aNth > 0) {
   2137      return FindLine(aName, aNth, aFromIndex, aImplicitLines);
   2138    }
   2139    int32_t nth = -*aNth;
   2140    int32_t line = RFindLine(aName, &nth, aFromIndex, aImplicitLines);
   2141    *aNth = -nth;
   2142    return line;
   2143  }
   2144 
   2145  /**
   2146   * Return a set of lines in aImplicitLines which matches the area name aName
   2147   * on aSide.  For example, for aName "a" and aSide being an end side, it
   2148   * returns the line numbers which would match "a-end" in the relevant axis.
   2149   * For subgrids it includes searching the relevant axis in all ancestor
   2150   * grids too (within this subgrid's spanned area).  If an ancestor has
   2151   * opposite direction, we switch aSide to the opposite logical side so we
   2152   * match on the same physical side as the original subgrid we're resolving
   2153   * the name for.
   2154   */
   2155  void FindNamedAreas(nsAtom* aName, LogicalSide aSide,
   2156                      nsTArray<uint32_t>& aImplicitLines) const {
   2157    // True if we're currently in a map that has the same direction as 'this'.
   2158    bool sameDirectionAsThis = true;
   2159    uint32_t min = !mParentLineNameMap ? 1 : mClampMinLine;
   2160    uint32_t max = mClampMaxLine;
   2161    for (auto* map = this; true;) {
   2162      uint32_t line = map->FindNamedArea(aName, aSide, min, max);
   2163      if (line > 0) {
   2164        if (MOZ_LIKELY(sameDirectionAsThis)) {
   2165          line -= min - 1;
   2166        } else {
   2167          line = max - line + 1;
   2168        }
   2169        aImplicitLines.AppendElement(line);
   2170      }
   2171      auto* parent = map->mParentLineNameMap;
   2172      if (!parent) {
   2173        if (MOZ_UNLIKELY(aImplicitLines.Length() > 1)) {
   2174          // Remove duplicates and sort in ascending order.
   2175          aImplicitLines.Sort();
   2176          for (size_t i = 0; i < aImplicitLines.Length(); ++i) {
   2177            uint32_t prev = aImplicitLines[i];
   2178            auto j = i + 1;
   2179            const auto start = j;
   2180            while (j < aImplicitLines.Length() && aImplicitLines[j] == prev) {
   2181              ++j;
   2182            }
   2183            if (j != start) {
   2184              aImplicitLines.RemoveElementsAt(start, j - start);
   2185            }
   2186          }
   2187        }
   2188        return;
   2189      }
   2190      if (MOZ_UNLIKELY(!map->mIsSameDirection)) {
   2191        aSide = GetOppositeSide(aSide);
   2192        sameDirectionAsThis = !sameDirectionAsThis;
   2193      }
   2194      min = map->TranslateToParentMap(min);
   2195      max = map->TranslateToParentMap(max);
   2196      if (min > max) {
   2197        MOZ_ASSERT(!map->mIsSameDirection);
   2198        std::swap(min, max);
   2199      }
   2200      map = parent;
   2201    }
   2202  }
   2203 
   2204  /**
   2205   * Return true if any implicit named areas match aName, in this map or
   2206   * in any of our ancestor maps.
   2207   */
   2208  bool HasImplicitNamedArea(nsAtom* aName) const {
   2209    const auto* map = this;
   2210    do {
   2211      if (map->mAreas && map->mAreas->has(aName)) {
   2212        return true;
   2213      }
   2214      map = map->mParentLineNameMap;
   2215    } while (map);
   2216    return false;
   2217  }
   2218 
   2219  // For generating line name data for devtools.
   2220  nsTArray<nsTArray<StyleCustomIdent>>
   2221  GetResolvedLineNamesForComputedGridTrackInfo() const {
   2222    nsTArray<nsTArray<StyleCustomIdent>> result;
   2223    for (auto& expandedLine : mExpandedLineNames) {
   2224      nsTArray<StyleCustomIdent> line;
   2225      for (auto* chunk : expandedLine) {
   2226        for (auto& name : chunk->AsSpan()) {
   2227          line.AppendElement(name);
   2228        }
   2229      }
   2230      result.AppendElement(std::move(line));
   2231    }
   2232    return result;
   2233  }
   2234 
   2235  nsTArray<RefPtr<nsAtom>> GetExplicitLineNamesAtIndex(uint32_t aIndex) const {
   2236    nsTArray<RefPtr<nsAtom>> lineNames;
   2237    if (aIndex < mTemplateLinesEnd) {
   2238      const auto nameLists = GetLineNamesAt(aIndex);
   2239      for (const NameList* nameList : nameLists) {
   2240        for (const auto& name : nameList->AsSpan()) {
   2241          lineNames.AppendElement(name.AsAtom());
   2242        }
   2243      }
   2244    }
   2245    return lineNames;
   2246  }
   2247 
   2248  const nsTArray<SmallPointerArray<const NameList>>& ExpandedLineNames() const {
   2249    return mExpandedLineNames;
   2250  }
   2251  const Span<const StyleOwnedSlice<StyleCustomIdent>>&
   2252  TrackAutoRepeatLineNames() const {
   2253    return mTrackAutoRepeatLineNames;
   2254  }
   2255  bool HasRepeatAuto() const { return mHasRepeatAuto; }
   2256  uint32_t NumRepeatTracks() const { return mRepeatAutoEnd - mRepeatAutoStart; }
   2257  uint32_t RepeatAutoStart() const { return mRepeatAutoStart; }
   2258 
   2259  // The min/max line number (1-based) for clamping.
   2260  int32_t mClampMinLine;
   2261  int32_t mClampMaxLine;
   2262 
   2263 private:
   2264  // Return true if this map represents a subgridded axis.
   2265  bool IsSubgridded() const { return mParentLineNameMap != nullptr; }
   2266 
   2267  /**
   2268   * @see FindNamedLine, this function searches forward.
   2269   */
   2270  uint32_t FindLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
   2271                    const nsTArray<uint32_t>& aImplicitLines) const {
   2272    MOZ_ASSERT(aNth && *aNth > 0);
   2273    int32_t nth = *aNth;
   2274    // For a subgrid we need to search to the end of the grid rather than
   2275    // the end of the local name list, since ancestors might match.
   2276    const uint32_t end = IsSubgridded() ? mClampMaxLine : mTemplateLinesEnd;
   2277    uint32_t line;
   2278    uint32_t i = aFromIndex;
   2279    for (; i < end; i = line) {
   2280      line = i + 1;
   2281      if (Contains(i, aName) || aImplicitLines.Contains(line)) {
   2282        if (--nth == 0) {
   2283          return line;
   2284        }
   2285      }
   2286    }
   2287    for (auto implicitLine : aImplicitLines) {
   2288      if (implicitLine > i) {
   2289        // implicitLine is after the lines we searched above so it's last.
   2290        // (grid-template-areas has more tracks than
   2291        // grid-template-[rows|columns])
   2292        if (--nth == 0) {
   2293          return implicitLine;
   2294        }
   2295      }
   2296    }
   2297    MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
   2298    *aNth = nth;
   2299    return 0;
   2300  }
   2301 
   2302  /**
   2303   * @see FindNamedLine, this function searches in reverse.
   2304   */
   2305  uint32_t RFindLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
   2306                     const nsTArray<uint32_t>& aImplicitLines) const {
   2307    MOZ_ASSERT(aNth && *aNth > 0);
   2308    if (MOZ_UNLIKELY(aFromIndex == 0)) {
   2309      return 0;  // There are no named lines beyond the start of the explicit
   2310                 // grid.
   2311    }
   2312    --aFromIndex;  // (shift aFromIndex so we can treat it as inclusive)
   2313    int32_t nth = *aNth;
   2314    // Implicit lines may be beyond the explicit grid so we match those
   2315    // first if it's within the mTemplateLinesEnd..aFromIndex range.
   2316    // aImplicitLines is presumed sorted.
   2317    // For a subgrid we need to search to the end of the grid rather than
   2318    // the end of the local name list, since ancestors might match.
   2319    const uint32_t end = IsSubgridded() ? mClampMaxLine : mTemplateLinesEnd;
   2320    for (auto implicitLine : Reversed(aImplicitLines)) {
   2321      if (implicitLine <= end) {
   2322        break;
   2323      }
   2324      if (implicitLine < aFromIndex) {
   2325        if (--nth == 0) {
   2326          return implicitLine;
   2327        }
   2328      }
   2329    }
   2330    for (uint32_t i = std::min(aFromIndex, end); i; --i) {
   2331      if (Contains(i - 1, aName) || aImplicitLines.Contains(i)) {
   2332        if (--nth == 0) {
   2333          return i;
   2334        }
   2335      }
   2336    }
   2337    MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
   2338    *aNth = nth;
   2339    return 0;
   2340  }
   2341 
   2342  // Return true if aName exists at aIndex in this map or any parent map.
   2343  bool Contains(uint32_t aIndex, nsAtom* aName) const {
   2344    const auto* map = this;
   2345    while (true) {
   2346      if (aIndex < map->mTemplateLinesEnd && map->HasNameAt(aIndex, aName)) {
   2347        return true;
   2348      }
   2349      auto* parent = map->mParentLineNameMap;
   2350      if (!parent) {
   2351        return false;
   2352      }
   2353      uint32_t line = map->TranslateToParentMap(aIndex + 1);
   2354      MOZ_ASSERT(line >= 1, "expected a 1-based line number");
   2355      aIndex = line - 1;
   2356      map = parent;
   2357    }
   2358    MOZ_ASSERT_UNREACHABLE("we always return from inside the loop above");
   2359  }
   2360 
   2361  static bool Contains(Span<const StyleCustomIdent> aNames, nsAtom* aName) {
   2362    for (auto& name : aNames) {
   2363      if (name.AsAtom() == aName) {
   2364        return true;
   2365      }
   2366    }
   2367    return false;
   2368  }
   2369 
   2370  // Return true if aName exists at aIndex in this map.
   2371  bool HasNameAt(const uint32_t aIndex, nsAtom* const aName) const {
   2372    const auto nameLists = GetLineNamesAt(aIndex);
   2373    for (const NameList* nameList : nameLists) {
   2374      if (Contains(nameList->AsSpan(), aName)) {
   2375        return true;
   2376      }
   2377    }
   2378    return false;
   2379  }
   2380 
   2381  // Get the line names at an index.
   2382  // This accounts for auto repeat. The results may be spread over multiple name
   2383  // lists returned in the array, which is done to avoid unneccessarily copying
   2384  // the arrays to concatenate them.
   2385  SmallPointerArray<const NameList> GetLineNamesAt(
   2386      const uint32_t aIndex) const {
   2387    SmallPointerArray<const NameList> names;
   2388    // The index into mExpandedLineNames to use, if aIndex doesn't point to a
   2389    // name inside of a auto repeat.
   2390    uint32_t repeatAdjustedIndex = aIndex;
   2391    // Note: For subgrid, |mHasRepeatAuto| is always false because we have
   2392    // expanded it in the constructor of LineNameMap.
   2393    if (mHasRepeatAuto) {
   2394      // If the index is inside of the auto repeat, use the repeat line
   2395      // names. Otherwise, if the index is past the end of the repeat it must
   2396      // be adjusted to acount for the repeat tracks.
   2397      // mExpandedLineNames has the first and last line name lists from the
   2398      // repeat in it already, so we can just ignore aIndex == mRepeatAutoStart
   2399      // and treat when aIndex == mRepeatAutoEnd the same as any line after the
   2400      // the repeat.
   2401      const uint32_t maxRepeatLine = mTrackAutoRepeatLineNames.Length() - 1;
   2402      if (aIndex > mRepeatAutoStart && aIndex < mRepeatAutoEnd) {
   2403        // The index is inside the auto repeat. Calculate the lines to use,
   2404        // including the previous repetitions final names when we roll over
   2405        // from one repetition to the next.
   2406        const uint32_t repeatIndex =
   2407            (aIndex - mRepeatAutoStart) % maxRepeatLine;
   2408        if (repeatIndex == 0) {
   2409          // The index is at the start of a new repetition. The start of the
   2410          // first repetition is intentionally ignored above, so this will
   2411          // consider both the end of the previous repetition and the start
   2412          // the one that contains aIndex.
   2413          names.AppendElement(&mTrackAutoRepeatLineNames[maxRepeatLine]);
   2414        }
   2415        names.AppendElement(&mTrackAutoRepeatLineNames[repeatIndex]);
   2416        return names;
   2417      }
   2418      if (aIndex != mRepeatAutoStart && aIndex >= mRepeatAutoEnd) {
   2419        // Adjust the index to account for the line names of the repeat.
   2420        repeatAdjustedIndex -= mRepeatEndDelta;
   2421        repeatAdjustedIndex += mTrackAutoRepeatLineNames.Length() - 2;
   2422      }
   2423    }
   2424    MOZ_ASSERT(repeatAdjustedIndex < mExpandedLineNames.Length(),
   2425               "Incorrect repeatedAdjustedIndex");
   2426    MOZ_ASSERT(names.IsEmpty());
   2427    // The index is not inside the repeat tracks, or no repeat tracks exist.
   2428    const auto& nameLists = mExpandedLineNames[repeatAdjustedIndex];
   2429    for (const NameList* nameList : nameLists) {
   2430      names.AppendElement(nameList);
   2431    }
   2432    return names;
   2433  }
   2434 
   2435  // Translate a subgrid line (1-based) to a parent line (1-based).
   2436  uint32_t TranslateToParentMap(uint32_t aLine) const {
   2437    if (MOZ_LIKELY(mIsSameDirection)) {
   2438      return aLine + mRange->mStart;
   2439    }
   2440    MOZ_ASSERT(mRange->mEnd + 1 >= aLine);
   2441    return mRange->mEnd - (aLine - 1) + 1;
   2442  }
   2443 
   2444  /**
   2445   * Return the 1-based line that match aName in 'grid-template-areas'
   2446   * on the side aSide.  Clamp the result to aMin..aMax but require
   2447   * that some part of the area is inside for it to match.
   2448   * Return zero if there is no match.
   2449   */
   2450  uint32_t FindNamedArea(nsAtom* aName, LogicalSide aSide, int32_t aMin,
   2451                         int32_t aMax) const {
   2452    if (const NamedArea* area = FindNamedArea(aName)) {
   2453      int32_t start = IsBlock(aSide) ? area->rows.start : area->columns.start;
   2454      int32_t end = IsBlock(aSide) ? area->rows.end : area->columns.end;
   2455      if (IsStart(aSide)) {
   2456        if (start >= aMin) {
   2457          if (start <= aMax) {
   2458            return start;
   2459          }
   2460        } else if (end >= aMin) {
   2461          return aMin;
   2462        }
   2463      } else {
   2464        if (end <= aMax) {
   2465          if (end >= aMin) {
   2466            return end;
   2467          }
   2468        } else if (start <= aMax) {
   2469          return aMax;
   2470        }
   2471      }
   2472    }
   2473    return 0;  // no match
   2474  }
   2475 
   2476  /**
   2477   * A convenience method to lookup a name in 'grid-template-areas'.
   2478   * @return null if not found
   2479   */
   2480  const NamedArea* FindNamedArea(nsAtom* aName) const {
   2481    if (mStylePosition->mGridTemplateAreas.IsNone()) {
   2482      return nullptr;
   2483    }
   2484    const auto areas = mStylePosition->mGridTemplateAreas.AsAreas();
   2485    for (const NamedArea& area : areas->areas.AsSpan()) {
   2486      if (area.name.AsAtom() == aName) {
   2487        return &area;
   2488      }
   2489    }
   2490    return nullptr;
   2491  }
   2492 
   2493  // Some style data references, for easy access.
   2494  const nsStylePosition* mStylePosition;
   2495  const ImplicitNamedAreas* mAreas;
   2496  // The expanded list of line-names. Each entry is usually a single NameList,
   2497  // but can be multiple in the case where repeat() expands to something that
   2498  // has a line name list at the end.
   2499  nsTArray<SmallPointerArray<const NameList>> mExpandedLineNames;
   2500  // The repeat(auto-fill/fit) track value, if any. (always empty for subgrid)
   2501  Span<const StyleOwnedSlice<StyleCustomIdent>> mTrackAutoRepeatLineNames;
   2502  // The index of the repeat(auto-fill/fit) track, or zero if there is none.
   2503  uint32_t mRepeatAutoStart;
   2504  // The index one past the end of the repeat(auto-fill/fit) tracks. Equal to
   2505  // mRepeatAutoStart if there are no repeat(auto-fill/fit) tracks.
   2506  uint32_t mRepeatAutoEnd;
   2507  // The total number of repeat tracks minus 1.
   2508  int32_t mRepeatEndDelta;
   2509  // The end of the line name lists with repeat(auto-fill/fit) tracks accounted
   2510  // for.
   2511  uint32_t mTemplateLinesEnd;
   2512 
   2513  // The parent line map, or null if this map isn't for a subgrid.
   2514  const LineNameMap* mParentLineNameMap;
   2515  // The subgrid's range, or null if this map isn't for a subgrid.
   2516  const LineRange* mRange;
   2517  // True if the subgrid/parent axes progresses in the same direction.
   2518  const bool mIsSameDirection;
   2519 
   2520  // True if there is a specified repeat(auto-fill/fit) track.
   2521  bool mHasRepeatAuto;
   2522 };
   2523 
   2524 struct CachedIntrinsicSizes;
   2525 
   2526 /**
   2527 * State for the tracks in one dimension.
   2528 */
   2529 struct nsGridContainerFrame::Tracks {
   2530  explicit Tracks(LogicalAxis aAxis)
   2531      : mContentBoxSize(NS_UNCONSTRAINEDSIZE),
   2532        mGridGap(NS_UNCONSTRAINEDSIZE),
   2533        mStateUnion(TrackSize::StateBits{0}),
   2534        mAxis(aAxis),
   2535        mCanResolveLineRangeSize(false),
   2536        mIsMasonry(false) {
   2537    mBaselineSubtreeAlign[BaselineSharingGroup::First] = StyleAlignFlags::AUTO;
   2538    mBaselineSubtreeAlign[BaselineSharingGroup::Last] = StyleAlignFlags::AUTO;
   2539  }
   2540 
   2541  void Initialize(const TrackSizingFunctions& aFunctions,
   2542                  const NonNegativeLengthPercentageOrNormal& aGridGap,
   2543                  uint32_t aNumTracks, nscoord aContentBoxSize);
   2544 
   2545  /**
   2546   * Return the union of the state bits for the tracks in aRange.
   2547   */
   2548  TrackSize::StateBits StateBitsForRange(const LineRange& aRange) const;
   2549 
   2550  // Some data we collect for aligning baseline-aligned items.
   2551  struct ItemBaselineData {
   2552    uint32_t mBaselineTrack;
   2553    nscoord mBaseline;
   2554    nscoord mSize;
   2555    GridItemInfo* mGridItem;
   2556    static bool IsBaselineTrackLessThan(const ItemBaselineData& a,
   2557                                        const ItemBaselineData& b) {
   2558      return a.mBaselineTrack < b.mBaselineTrack;
   2559    }
   2560  };
   2561 
   2562  /**
   2563   * Calculate baseline offsets for the given set of items.
   2564   * Helper for InitialzeItemBaselines.
   2565   */
   2566  void CalculateItemBaselines(nsTArray<ItemBaselineData>& aBaselineItems,
   2567                              BaselineSharingGroup aBaselineGroup);
   2568 
   2569  /**
   2570   * Initialize grid item baseline state and offsets.
   2571   */
   2572  void InitializeItemBaselines(GridReflowInput& aGridRI,
   2573                               nsTArray<GridItemInfo>& aGridItems);
   2574 
   2575  /**
   2576   * A masonry axis has four baseline alignment sets and each set can have
   2577   * a first- and last-baseline alignment group, for a total of eight possible
   2578   * baseline alignment groups, as follows:
   2579   *   set 1: the first item in each `start` or `stretch` grid track
   2580   *   set 2: the last item in each `start` grid track
   2581   *   set 3: the last item in each `end` or `stretch` grid track
   2582   *   set 4: the first item in each `end` grid track
   2583   * (`start`/`end`/`stretch` refers to the relevant `align/justify-tracks`
   2584   * value of the (grid-axis) start track for the item) Baseline-alignment for
   2585   * set 1 and 2 always adjusts the item's padding or margin on the start side,
   2586   * and set 3 and 4 on the end side, for both first- and last-baseline groups
   2587   * in the set. (This is similar to regular grid which always adjusts
   2588   * first-baseline groups on the start side and last-baseline groups on the
   2589   * end-side.  The crux is that those groups are always aligned to the track's
   2590   * start/end side respectively.)
   2591   */
   2592  struct BaselineAlignmentSet {
   2593    bool MatchTrackAlignment(StyleAlignFlags aTrackAlignment) const {
   2594      if (mTrackAlignmentSet == BaselineAlignmentSet::StartStretch) {
   2595        return aTrackAlignment == StyleAlignFlags::START ||
   2596               (aTrackAlignment == StyleAlignFlags::STRETCH &&
   2597                mItemSet == BaselineAlignmentSet::FirstItems);
   2598      }
   2599      return aTrackAlignment == StyleAlignFlags::END ||
   2600             (aTrackAlignment == StyleAlignFlags::STRETCH &&
   2601              mItemSet == BaselineAlignmentSet::LastItems);
   2602    }
   2603 
   2604    enum ItemSet { FirstItems, LastItems };
   2605    ItemSet mItemSet = FirstItems;
   2606    enum TrackAlignmentSet { StartStretch, EndStretch };
   2607    TrackAlignmentSet mTrackAlignmentSet = StartStretch;
   2608  };
   2609  void InitializeItemBaselinesInMasonryAxis(
   2610      GridReflowInput& aGridRI, nsTArray<GridItemInfo>& aGridItems,
   2611      BaselineAlignmentSet aSet, const nsSize& aContainerSize,
   2612      nsTArray<nscoord>& aTrackSizes,
   2613      nsTArray<ItemBaselineData>& aFirstBaselineItems,
   2614      nsTArray<ItemBaselineData>& aLastBaselineItems);
   2615 
   2616  /**
   2617   * Apply the additional alignment needed to align the baseline-aligned subtree
   2618   * the item belongs to within its baseline track.
   2619   */
   2620  void AlignBaselineSubtree(const GridItemInfo& aGridItem) const;
   2621 
   2622  static TrackSize::StateBits SelectorForPhase(TrackSizingPhase aPhase,
   2623                                               SizingConstraint aConstraint) {
   2624    switch (aPhase) {
   2625      case TrackSizingPhase::IntrinsicMinimums:
   2626        return TrackSize::eIntrinsicMinSizing;
   2627      case TrackSizingPhase::ContentBasedMinimums:
   2628        return aConstraint == SizingConstraint::MinContent
   2629                   ? TrackSize::eIntrinsicMinSizing
   2630                   : TrackSize::eMinOrMaxContentMinSizing;
   2631      case TrackSizingPhase::MaxContentMinimums:
   2632        return aConstraint == SizingConstraint::MaxContent
   2633                   ? (TrackSize::eMaxContentMinSizing |
   2634                      TrackSize::eAutoMinSizing)
   2635                   : TrackSize::eMaxContentMinSizing;
   2636      case TrackSizingPhase::IntrinsicMaximums:
   2637        return TrackSize::eIntrinsicMaxSizing;
   2638      case TrackSizingPhase::MaxContentMaximums:
   2639        return TrackSize::eAutoOrMaxContentMaxSizing;
   2640    }
   2641    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
   2642  }
   2643 
   2644  // Some data we collect on each item that spans more than one track for step 3
   2645  // and 4 of the Track Sizing Algorithm in ResolveIntrinsicSize below.
   2646  // https://drafts.csswg.org/css-grid-2/#algo-spanning-items
   2647  struct SpanningItemData final {
   2648    uint32_t mSpan;
   2649    TrackSize::StateBits mState;
   2650    LineRange mLineRange;
   2651    EnumeratedArray<GridIntrinsicSizeType, nscoord> mSizes;
   2652    nsIFrame* mFrame;
   2653 
   2654    static bool IsSpanLessThan(const SpanningItemData& a,
   2655                               const SpanningItemData& b) {
   2656      return a.mSpan < b.mSpan;
   2657    }
   2658 
   2659    nscoord SizeContributionForPhase(TrackSizingPhase aPhase) const {
   2660      return mSizes[SizeTypeForPhase(aPhase)];
   2661    }
   2662 
   2663 #ifdef DEBUG
   2664    void Dump() const {
   2665      printf(
   2666          "SpanningItemData { mSpan: %d, mState: %d, mLineRange: (%d, %d), "
   2667          "mSizes: {MinContribution: %d, MinContentContribution: %d, "
   2668          "MaxContentContribution: %d}, mFrame: %p\n",
   2669          mSpan, mState, mLineRange.mStart, mLineRange.mEnd,
   2670          mSizes[GridIntrinsicSizeType::MinContribution],
   2671          mSizes[GridIntrinsicSizeType::MinContentContribution],
   2672          mSizes[GridIntrinsicSizeType::MaxContentContribution], mFrame);
   2673    }
   2674 #endif
   2675  };
   2676 
   2677  using FitContentClamper =
   2678      std::function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
   2679 
   2680  // Helper method for ResolveIntrinsicSize.
   2681  bool GrowSizeForSpanningItems(
   2682      TrackSizingStep aStep, TrackSizingPhase aPhase,
   2683      nsTArray<SpanningItemData>::iterator aIter,
   2684      nsTArray<SpanningItemData>::iterator aIterEnd,
   2685      nsTArray<uint32_t>& aTracks, TrackPlan& aTrackPlan, ItemPlan& aItemPlan,
   2686      SizingConstraint aConstraint, bool aIsGridIntrinsicSizing,
   2687      const TrackSizingFunctions& aFunctions,
   2688      const FitContentClamper& aFitContentClamper = nullptr,
   2689      bool aNeedInfinitelyGrowableFlag = false);
   2690 
   2691  // Helper method for calculating CachedIntrinsicSizes.mMinSizeClamp.
   2692  //
   2693  // The caller should set ItemState::eClampMarginBoxMinSize on the
   2694  // corresponding grid item if this returns something.
   2695  Maybe<nscoord> ComputeMinSizeClamp(const TrackSizingFunctions& aFunctions,
   2696                                     nscoord aPercentageBasis,
   2697                                     const LineRange& aLineRange) const {
   2698    return ComputeMinSizeClamp(aFunctions, aPercentageBasis, aLineRange,
   2699                               StateBitsForRange(aLineRange));
   2700  }
   2701 
   2702  // More efficient version of ComputeMinSizeClamp if the caller has already
   2703  // computed the state bits for this line range.
   2704  Maybe<nscoord> ComputeMinSizeClamp(const TrackSizingFunctions& aFunctions,
   2705                                     nscoord aPercentageBasis,
   2706                                     const LineRange& aLineRange,
   2707                                     const TrackSize::StateBits aState) const;
   2708 
   2709  /**
   2710   * Resolve Intrinsic Track Sizes.
   2711   * https://drafts.csswg.org/css-grid-2/#algo-content
   2712   */
   2713  void ResolveIntrinsicSize(GridReflowInput& aGridRI,
   2714                            nsTArray<GridItemInfo>& aGridItems,
   2715                            const TrackSizingFunctions& aFunctions,
   2716                            LineRange GridArea::* aRange,
   2717                            nscoord aPercentageBasis,
   2718                            SizingConstraint aConstraint);
   2719 
   2720  /**
   2721   * Helper for ResolveIntrinsicSize. It implements "Resolve Intrinsic Track
   2722   * Sizes" step 2: "Size tracks to fit non-spanning items" in the spec.
   2723   * https://drafts.csswg.org/css-grid-2/#algo-single-span-items
   2724   */
   2725  void ResolveIntrinsicSizeForNonSpanningItems(
   2726      GridReflowInput& aGridRI, const TrackSizingFunctions& aFunctions,
   2727      nscoord aPercentageBasis, SizingConstraint aConstraint,
   2728      const LineRange& aRange, const GridItemInfo& aGridItem);
   2729 
   2730  // Helper method that returns the track size to use in §12.5.1.2
   2731  // https://drafts.csswg.org/css-grid-2/#extra-space
   2732  static nscoord StartSizeInDistribution(TrackSizingPhase aPhase,
   2733                                         const TrackSize& aSize) {
   2734    switch (aPhase) {
   2735      case TrackSizingPhase::IntrinsicMinimums:
   2736      case TrackSizingPhase::ContentBasedMinimums:
   2737      case TrackSizingPhase::MaxContentMinimums:
   2738        return aSize.mBase;
   2739      case TrackSizingPhase::IntrinsicMaximums:
   2740      case TrackSizingPhase::MaxContentMaximums:
   2741        if (aSize.mLimit == NS_UNCONSTRAINEDSIZE) {
   2742          return aSize.mBase;
   2743        }
   2744        return aSize.mLimit;
   2745    }
   2746    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
   2747  }
   2748 
   2749  /**
   2750   * Collect the tracks which are growable (matching the sizing step/phase
   2751   * and sizing constraint) into aGrowableTracks, and return the amount of
   2752   * space that can be used to grow those tracks. This method implements
   2753   * CSS Grid 2 §12.5.1.2.
   2754   * https://drafts.csswg.org/css-grid-2/#extra-space
   2755   */
   2756  nscoord CollectGrowable(TrackSizingStep aStep, TrackSizingPhase aPhase,
   2757                          nscoord aAvailableSpace, const LineRange& aRange,
   2758                          SizingConstraint aConstraint,
   2759                          nsTArray<uint32_t>& aGrowableTracks) const {
   2760    MOZ_ASSERT(aAvailableSpace > 0, "why call me?");
   2761    nscoord space = aAvailableSpace - mGridGap * (aRange.Extent() - 1);
   2762    const TrackSize::StateBits selector = SelectorForPhase(aPhase, aConstraint);
   2763    for (auto i : aRange.Range()) {
   2764      const TrackSize& sz = mSizes[i];
   2765      space -= StartSizeInDistribution(aPhase, sz);
   2766      if (space <= 0) {
   2767        return 0;
   2768      }
   2769      // Only flex tracks can be modified during step 4.
   2770      if (aStep == TrackSizingStep::Flex &&
   2771          !(sz.mState & TrackSize::eFlexMaxSizing)) {
   2772        continue;
   2773      }
   2774      if (sz.mState & selector) {
   2775        aGrowableTracks.AppendElement(i);
   2776      }
   2777    }
   2778    return aGrowableTracks.IsEmpty() ? 0 : space;
   2779  }
   2780 
   2781  void CopyPlanToSize(TrackSizingPhase aPhase, const TrackPlan& aTrackPlan,
   2782                      bool aNeedInfinitelyGrowableFlag) {
   2783    MOZ_ASSERT(aTrackPlan.Length() == mSizes.Length());
   2784    auto plan = aTrackPlan.begin();
   2785    auto sz = mSizes.begin();
   2786    for (; plan != aTrackPlan.end() && sz != mSizes.end(); plan++, sz++) {
   2787      MOZ_ASSERT(plan->mBase >= 0);
   2788      switch (aPhase) {
   2789        case TrackSizingPhase::IntrinsicMinimums:
   2790        case TrackSizingPhase::ContentBasedMinimums:
   2791        case TrackSizingPhase::MaxContentMinimums:
   2792          sz->mBase = plan->mBase;
   2793          break;
   2794        case TrackSizingPhase::IntrinsicMaximums:
   2795          if (plan->mState & TrackSize::eModified) {
   2796            if (sz->mLimit == NS_UNCONSTRAINEDSIZE &&
   2797                aNeedInfinitelyGrowableFlag) {
   2798              sz->mState |= TrackSize::eInfinitelyGrowable;
   2799            }
   2800            sz->mLimit = plan->mBase;
   2801          }
   2802          break;
   2803        case TrackSizingPhase::MaxContentMaximums:
   2804          if (plan->mState & TrackSize::eModified) {
   2805            sz->mLimit = plan->mBase;
   2806          }
   2807          sz->mState &= ~TrackSize::eInfinitelyGrowable;
   2808          break;
   2809      }
   2810    }
   2811  }
   2812 
   2813  /**
   2814   * Distribute aAvailableSpace to the planned base size for aGrowableTracks
   2815   * up to their limits, then distribute the remaining space beyond the limits.
   2816   */
   2817  void DistributeToTrackSizes(
   2818      TrackSizingStep aStep, TrackSizingPhase aPhase, nscoord aAvailableSpace,
   2819      TrackPlan& aTrackPlan, ItemPlan& aItemPlan,
   2820      const nsTArray<uint32_t>& aGrowableTracks, SizingConstraint aConstraint,
   2821      const TrackSizingFunctions& aFunctions,
   2822      const FitContentClamper& aFitContentClamper) const {
   2823    aItemPlan.Initialize(aPhase, aGrowableTracks, *this);
   2824    nscoord space = aAvailableSpace;
   2825    if (aStep == TrackSizingStep::Flex) {
   2826      space = aTrackPlan.DistributeToFlexTrackSizes(space, aGrowableTracks,
   2827                                                    aFunctions, *this);
   2828    } else {
   2829      space = aItemPlan.GrowTracksToLimit(space, aGrowableTracks,
   2830                                          aFitContentClamper);
   2831    }
   2832 
   2833    if (space > 0) {
   2834      uint32_t numGrowable =
   2835          aItemPlan.MarkExcludedTracks(aPhase, aGrowableTracks, aConstraint);
   2836      aItemPlan.GrowSelectedTracksUnlimited(space, aGrowableTracks, numGrowable,
   2837                                            aFitContentClamper);
   2838    }
   2839 
   2840    for (uint32_t track : aGrowableTracks) {
   2841      nscoord& plannedSize = aTrackPlan[track].mBase;
   2842      nscoord itemIncurredSize = aItemPlan[track].mBase;
   2843      if (plannedSize < itemIncurredSize) {
   2844        plannedSize = itemIncurredSize;
   2845      }
   2846    }
   2847  }
   2848 
   2849  /**
   2850   * Distribute aAvailableSize to the tracks.  This implements 12.6 at:
   2851   * https://drafts.csswg.org/css-grid-2/#algo-grow-tracks
   2852   */
   2853  void DistributeFreeSpace(nscoord aAvailableSize) {
   2854    const uint32_t numTracks = mSizes.Length();
   2855    if (MOZ_UNLIKELY(numTracks == 0 || aAvailableSize <= 0)) {
   2856      return;
   2857    }
   2858    if (aAvailableSize == NS_UNCONSTRAINEDSIZE) {
   2859      for (TrackSize& sz : mSizes) {
   2860        sz.mBase = sz.mLimit;
   2861      }
   2862    } else {
   2863      // Compute free space and count growable tracks.
   2864      nscoord space = aAvailableSize;
   2865      uint32_t numGrowable = numTracks;
   2866      for (const TrackSize& sz : mSizes) {
   2867        space -= sz.mBase;
   2868        MOZ_ASSERT(sz.mBase <= sz.mLimit);
   2869        if (sz.mBase == sz.mLimit) {
   2870          --numGrowable;
   2871        }
   2872      }
   2873      // Distribute the free space evenly to the growable tracks. If not exactly
   2874      // divisable the remainder is added to the leading tracks.
   2875      while (space > 0 && numGrowable) {
   2876        nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
   2877        for (TrackSize& sz : mSizes) {
   2878          if (sz.mBase == sz.mLimit) {
   2879            continue;
   2880          }
   2881          nscoord newBase = sz.mBase + spacePerTrack;
   2882          if (newBase >= sz.mLimit) {
   2883            space -= sz.mLimit - sz.mBase;
   2884            sz.mBase = sz.mLimit;
   2885            --numGrowable;
   2886          } else {
   2887            space -= spacePerTrack;
   2888            sz.mBase = newBase;
   2889          }
   2890          if (space <= 0) {
   2891            break;
   2892          }
   2893        }
   2894      }
   2895    }
   2896  }
   2897 
   2898  /**
   2899   * Implements "12.7.1. Find the Size of an 'fr'".
   2900   * https://drafts.csswg.org/css-grid-2/#algo-find-fr-size
   2901   * (The returned value is a 'nscoord' divided by a factor - a floating type
   2902   * is used to avoid intermediary rounding errors.)
   2903   */
   2904  float FindFrUnitSize(const LineRange& aRange,
   2905                       const nsTArray<uint32_t>& aFlexTracks,
   2906                       const TrackSizingFunctions& aFunctions,
   2907                       nscoord aSpaceToFill) const;
   2908 
   2909  /**
   2910   * Implements the "find the used flex fraction" part of StretchFlexibleTracks.
   2911   * (The returned value is a 'nscoord' divided by a factor - a floating type
   2912   * is used to avoid intermediary rounding errors.)
   2913   */
   2914  float FindUsedFlexFraction(GridReflowInput& aGridRI,
   2915                             const nsTArray<GridItemInfo>& aGridItems,
   2916                             const nsTArray<uint32_t>& aFlexTracks,
   2917                             const TrackSizingFunctions& aFunctions,
   2918                             nscoord aAvailableSize) const;
   2919 
   2920  /**
   2921   * Implements "12.7. Expand Flexible Tracks"
   2922   * https://drafts.csswg.org/css-grid-2/#algo-flex-tracks
   2923   */
   2924  void StretchFlexibleTracks(GridReflowInput& aGridRI,
   2925                             const nsTArray<GridItemInfo>& aGridItems,
   2926                             const TrackSizingFunctions& aFunctions,
   2927                             nscoord aAvailableSize);
   2928 
   2929  /**
   2930   * Implements "12.3. Track Sizing Algorithm"
   2931   * https://drafts.csswg.org/css-grid-2/#algo-track-sizing
   2932   */
   2933  void CalculateSizes(GridReflowInput& aGridRI,
   2934                      nsTArray<GridItemInfo>& aGridItems,
   2935                      const TrackSizingFunctions& aFunctions,
   2936                      nscoord aContentBoxSize, LineRange GridArea::* aRange,
   2937                      SizingConstraint aConstraint);
   2938 
   2939  /**
   2940   * Apply 'align/justify-content', whichever is relevant for this axis.
   2941   * https://drafts.csswg.org/css-align-3/#propdef-align-content
   2942   */
   2943  void AlignJustifyContent(const nsStylePosition* aStyle,
   2944                           StyleContentDistribution aAligmentStyleValue,
   2945                           WritingMode aWM, nscoord aContentBoxSize,
   2946                           bool aIsSubgridded);
   2947 
   2948  /**
   2949   * Return the sum of the resolved track and gap sizes (without any packing
   2950   * space introduced by align-content or justify-content.
   2951   */
   2952  nscoord TotalTrackSizeWithoutAlignment(
   2953      const nsGridContainerFrame* aGridContainerFrame) const;
   2954 
   2955  nscoord GridLineEdge(uint32_t aLine, GridLineSide aSide) const {
   2956    if (MOZ_UNLIKELY(mSizes.IsEmpty())) {
   2957      // https://drafts.csswg.org/css-grid-2/#grid-definition
   2958      // "... the explicit grid still contains one grid line in each axis."
   2959      MOZ_ASSERT(aLine == 0, "We should only resolve line 1 in an empty grid");
   2960      return nscoord(0);
   2961    }
   2962    MOZ_ASSERT(aLine <= mSizes.Length(), "mSizes is too small");
   2963    if (aSide == GridLineSide::BeforeGridGap) {
   2964      if (aLine == 0) {
   2965        return nscoord(0);
   2966      }
   2967      const TrackSize& sz = mSizes[aLine - 1];
   2968      return sz.mPosition + sz.mBase;
   2969    }
   2970    if (aLine == mSizes.Length()) {
   2971      return mContentBoxSize;
   2972    }
   2973    return mSizes[aLine].mPosition;
   2974  }
   2975 
   2976  nscoord SumOfGridTracksAndGaps() const {
   2977    return SumOfGridTracks() + SumOfGridGaps();
   2978  }
   2979 
   2980  nscoord SumOfGridTracks() const {
   2981    nscoord result = 0;
   2982    for (const TrackSize& size : mSizes) {
   2983      result += size.mBase;
   2984    }
   2985    return result;
   2986  }
   2987 
   2988  nscoord SumOfGridGaps() const {
   2989    auto len = mSizes.Length();
   2990    return MOZ_LIKELY(len > 1) ? (len - 1) * mGridGap : 0;
   2991  }
   2992 
   2993  /**
   2994   * Break before aRow, i.e. set the eBreakBefore flag on aRow and set the grid
   2995   * gap before aRow to zero (and shift all rows after it by the removed gap).
   2996   */
   2997  void BreakBeforeRow(uint32_t aRow) {
   2998    MOZ_ASSERT(mAxis == LogicalAxis::Block,
   2999               "Should only be fragmenting in the block axis (between rows)");
   3000    nscoord prevRowEndPos = 0;
   3001    if (aRow != 0) {
   3002      auto& prevSz = mSizes[aRow - 1];
   3003      prevRowEndPos = prevSz.mPosition + prevSz.mBase;
   3004    }
   3005    auto& sz = mSizes[aRow];
   3006    const nscoord gap = sz.mPosition - prevRowEndPos;
   3007    sz.mState |= TrackSize::eBreakBefore;
   3008    if (gap != 0) {
   3009      for (uint32_t i = aRow, len = mSizes.Length(); i < len; ++i) {
   3010        mSizes[i].mPosition -= gap;
   3011      }
   3012    }
   3013  }
   3014 
   3015  /**
   3016   * Set the size of aRow to aSize and adjust the position of all rows after it.
   3017   */
   3018  void ResizeRow(uint32_t aRow, nscoord aNewSize) {
   3019    MOZ_ASSERT(mAxis == LogicalAxis::Block,
   3020               "Should only be fragmenting in the block axis (between rows)");
   3021    MOZ_ASSERT(aNewSize >= 0);
   3022    auto& sz = mSizes[aRow];
   3023    nscoord delta = aNewSize - sz.mBase;
   3024    NS_WARNING_ASSERTION(delta != nscoord(0), "Useless call to ResizeRow");
   3025    sz.mBase = aNewSize;
   3026    const uint32_t numRows = mSizes.Length();
   3027    for (uint32_t r = aRow + 1; r < numRows; ++r) {
   3028      mSizes[r].mPosition += delta;
   3029    }
   3030  }
   3031 
   3032  nscoord ResolveSize(const LineRange& aRange) const {
   3033    MOZ_ASSERT(mCanResolveLineRangeSize);
   3034    MOZ_ASSERT(aRange.Extent() > 0, "grid items cover at least one track");
   3035    return aRange.ToLength(mSizes);
   3036  }
   3037 
   3038  // Returns the offset for the given track and baseline sharing group, or the
   3039  // opposite group in the same track if unavailable.
   3040  // Returns `Nothing()` if neither is set.
   3041  Maybe<nscoord> GetBaseline(uint32_t aTrack,
   3042                             BaselineSharingGroup aBaselineSharingGroup) const {
   3043    if (aTrack >= mBaselines.Length()) {
   3044      return {};
   3045    }
   3046 
   3047    const auto& trackBaselines = mBaselines[aTrack];
   3048    if (auto b = trackBaselines[aBaselineSharingGroup]) {
   3049      return b;
   3050    }
   3051    if (auto b = trackBaselines[GetOppositeBaselineSharingGroup(
   3052            aBaselineSharingGroup)]) {
   3053      // First and last baseline offsets are from the start or end edges
   3054      // respectively and requesting the opposite baseline requires adjusting
   3055      // the offset to the opposite side.
   3056      return Some(mSizes[aTrack].mBase - *b);
   3057    }
   3058 
   3059    return {};
   3060  }
   3061 
   3062 #ifdef DEBUG
   3063  void Dump() const;
   3064 #endif
   3065 
   3066  TrackPlan mSizes;
   3067  nscoord mContentBoxSize;
   3068  nscoord mGridGap;
   3069 
   3070  // Stores per-track baselines for grid baseline calculations.
   3071  CopyableTArray<PerBaseline<Maybe<nscoord>>> mBaselines;
   3072 
   3073  // The union of the track min/max-sizing state bits in this axis.
   3074  TrackSize::StateBits mStateUnion;
   3075  LogicalAxis mAxis;
   3076  // Used for aligning a baseline-aligned subtree of items.  The only possible
   3077  // values are StyleAlignFlags::{START,END,CENTER,AUTO}.  AUTO means there are
   3078  // no baseline-aligned items in any track in that axis.
   3079  // There is one alignment value for each BaselineSharingGroup.
   3080  PerBaseline<StyleAlignFlags> mBaselineSubtreeAlign;
   3081  // True if track positions and sizes are final in this axis.
   3082  bool mCanResolveLineRangeSize;
   3083  // True if this axis has masonry layout.
   3084  bool mIsMasonry;
   3085 };
   3086 
   3087 #ifdef DEBUG
   3088 void nsGridContainerFrame::Tracks::Dump() const {
   3089  const size_t numTracks = mSizes.Length();
   3090  const char* trackName = mAxis == LogicalAxis::Inline ? "column" : "row";
   3091 
   3092  auto BaselineToStr = [](Maybe<nscoord> aBaseline) {
   3093    if (!aBaseline) {
   3094      return std::string("not set");
   3095    }
   3096 
   3097    return std::to_string(*aBaseline);
   3098  };
   3099 
   3100  auto CoordToStr = [](nscoord aCoord) {
   3101    return aCoord == NS_UNCONSTRAINEDSIZE ? std::string("unconstrained")
   3102                                          : std::to_string(aCoord);
   3103  };
   3104 
   3105  fmt::print(FMT_STRING("{} {} {}{}, track union bits: "), numTracks,
   3106             mIsMasonry ? "masonry" : "grid", trackName,
   3107             numTracks > 1 ? "s" : "");
   3108  TrackSize::DumpStateBits(mStateUnion);
   3109  printf("\n");
   3110 
   3111  for (uint32_t i = 0; i < numTracks; ++i) {
   3112    fmt::print(FMT_STRING("  {} {}: "), trackName, i);
   3113    mSizes[i].Dump();
   3114    printf("\n");
   3115  }
   3116 
   3117  fmt::println(FMT_STRING("  first baseline: {}, last baseline: {}"),
   3118               BaselineToStr(GetBaseline(0, BaselineSharingGroup::First)),
   3119               BaselineToStr(GetBaseline(mBaselines.Length() - 1,
   3120                                         BaselineSharingGroup::Last)));
   3121  fmt::println(FMT_STRING("  {} gap: {}, content-box {}-size: {}"), trackName,
   3122               CoordToStr(mGridGap),
   3123               mAxis == LogicalAxis::Inline ? "inline" : "block",
   3124               CoordToStr(mContentBoxSize));
   3125 }
   3126 #endif
   3127 
   3128 /**
   3129 * Grid data shared by all continuations, owned by the first-in-flow.
   3130 * The data is initialized from the first-in-flow's GridReflowInput at
   3131 * the end of its reflow.  Fragmentation will modify mRows.mSizes -
   3132 * the mPosition to remove the row gap at the break boundary, the mState
   3133 * by setting the eBreakBefore flag, and mBase is modified when we decide
   3134 * to grow a row.  mOriginalRowData is setup by the first-in-flow and
   3135 * not modified after that.  It's used for undoing the changes to mRows.
   3136 * mCols, mGridItems, mAbsPosItems are used for initializing the grid
   3137 * reflow input for continuations, see GridReflowInput::Initialize below.
   3138 */
   3139 struct nsGridContainerFrame::SharedGridData {
   3140  SharedGridData()
   3141      : mCols(LogicalAxis::Inline),
   3142        mRows(LogicalAxis::Block),
   3143        mGenerateComputedGridInfo(false) {}
   3144  Tracks mCols;
   3145  Tracks mRows;
   3146  struct RowData {
   3147    nscoord mBase;  // the original track size
   3148    nscoord mGap;   // the original gap before a track
   3149  };
   3150  nsTArray<RowData> mOriginalRowData;
   3151  nsTArray<GridItemInfo> mGridItems;
   3152  nsTArray<GridItemInfo> mAbsPosItems;
   3153  bool mGenerateComputedGridInfo;
   3154 
   3155  /**
   3156   * Only set on the first-in-flow.  Continuations will Initialize() their
   3157   * GridReflowInput from it.
   3158   */
   3159  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, SharedGridData)
   3160 };
   3161 
   3162 struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput {
   3163  GridReflowInput(nsGridContainerFrame* aFrame, const ReflowInput& aRI)
   3164      : GridReflowInput(aFrame, *aRI.mRenderingContext, &aRI,
   3165                        aRI.mStylePosition, aRI.GetWritingMode()) {}
   3166  GridReflowInput(nsGridContainerFrame* aFrame, gfxContext& aRC)
   3167      : GridReflowInput(aFrame, aRC, nullptr, aFrame->StylePosition(),
   3168                        aFrame->GetWritingMode()) {}
   3169 
   3170  /**
   3171   * Initialize our track sizes and grid item info using the shared
   3172   * state from aGridContainerFrame first-in-flow.
   3173   */
   3174  void InitializeForContinuation(nsGridContainerFrame* aGridContainerFrame,
   3175                                 nscoord aConsumedBSize) {
   3176    MOZ_ASSERT(aGridContainerFrame->GetPrevInFlow(),
   3177               "don't call this on the first-in-flow");
   3178    MOZ_ASSERT(mGridItems.IsEmpty() && mAbsPosItems.IsEmpty(),
   3179               "shouldn't have any item data yet");
   3180 
   3181    // Get the SharedGridData from the first-in-flow. Also calculate the number
   3182    // of fragments before this so that we can figure out our start row below.
   3183    uint32_t fragment = 0;
   3184    nsIFrame* firstInFlow = aGridContainerFrame;
   3185    for (auto pif = aGridContainerFrame->GetPrevInFlow(); pif;
   3186         pif = pif->GetPrevInFlow()) {
   3187      ++fragment;
   3188      firstInFlow = pif;
   3189    }
   3190    mSharedGridData = firstInFlow->GetProperty(SharedGridData::Prop());
   3191    MOZ_ASSERT(mSharedGridData, "first-in-flow must have SharedGridData");
   3192 
   3193    // Find the start row for this fragment and undo breaks after that row
   3194    // since the breaks might be different from the last reflow.
   3195    auto& rowSizes = mSharedGridData->mRows.mSizes;
   3196    const uint32_t numRows = rowSizes.Length();
   3197    mStartRow = numRows;
   3198    for (uint32_t row = 0, breakCount = 0; row < numRows; ++row) {
   3199      if (rowSizes[row].mState & TrackSize::eBreakBefore) {
   3200        if (fragment == ++breakCount) {
   3201          mStartRow = row;
   3202          mFragBStart = rowSizes[row].mPosition;
   3203          // Restore the original size for |row| and grid gaps / state after it.
   3204          const auto& origRowData = mSharedGridData->mOriginalRowData;
   3205          rowSizes[row].mBase = origRowData[row].mBase;
   3206          nscoord prevEndPos = rowSizes[row].mPosition + rowSizes[row].mBase;
   3207          while (++row < numRows) {
   3208            auto& sz = rowSizes[row];
   3209            const auto& orig = origRowData[row];
   3210            sz.mPosition = prevEndPos + orig.mGap;
   3211            sz.mBase = orig.mBase;
   3212            sz.mState &= ~TrackSize::eBreakBefore;
   3213            prevEndPos = sz.mPosition + sz.mBase;
   3214          }
   3215          break;
   3216        }
   3217      }
   3218    }
   3219    if (mStartRow == numRows || aGridContainerFrame->IsRowMasonry()) {
   3220      // All of the grid's rows fit inside of previous grid-container fragments,
   3221      // or it's a masonry axis.
   3222      mFragBStart = aConsumedBSize;
   3223    }
   3224 
   3225    // Copy the shared track state.
   3226    // XXX consider temporarily swapping the array elements instead and swapping
   3227    // XXX them back after we're done reflowing, for better performance.
   3228    // XXX (bug 1252002)
   3229    mCols = mSharedGridData->mCols;
   3230    mRows = mSharedGridData->mRows;
   3231 
   3232    if (firstInFlow->GetProperty(UsedTrackSizes::Prop())) {
   3233      auto* prop = aGridContainerFrame->GetProperty(UsedTrackSizes::Prop());
   3234      if (!prop) {
   3235        prop = new UsedTrackSizes();
   3236        aGridContainerFrame->SetProperty(UsedTrackSizes::Prop(), prop);
   3237      }
   3238      prop->mCanResolveLineRangeSize = {true, true};
   3239      prop->mTrackPlans[LogicalAxis::Inline].Assign(mCols.mSizes);
   3240      prop->mTrackPlans[LogicalAxis::Block].Assign(mRows.mSizes);
   3241    }
   3242 
   3243    // Copy item data from each child's first-in-flow data in mSharedGridData.
   3244    // XXX NOTE: This is O(n^2) in the number of items. (bug 1252186)
   3245    mIter.Reset();
   3246    for (; !mIter.AtEnd(); mIter.Next()) {
   3247      nsIFrame* child = *mIter;
   3248      nsIFrame* childFirstInFlow = child->FirstInFlow();
   3249      DebugOnly<size_t> len = mGridItems.Length();
   3250      for (auto& itemInfo : mSharedGridData->mGridItems) {
   3251        if (itemInfo.mFrame == childFirstInFlow) {
   3252          auto item =
   3253              mGridItems.AppendElement(GridItemInfo(child, itemInfo.mArea));
   3254          // Copy the item's baseline data so that the item's last fragment can
   3255          // do 'last baseline' alignment if necessary.
   3256          item->mState[LogicalAxis::Block] |=
   3257              itemInfo.mState[LogicalAxis::Block] & ItemState::eAllBaselineBits;
   3258          item->mState[LogicalAxis::Inline] |=
   3259              itemInfo.mState[LogicalAxis::Inline] &
   3260              ItemState::eAllBaselineBits;
   3261          item->mBaselineOffset[LogicalAxis::Block] =
   3262              itemInfo.mBaselineOffset[LogicalAxis::Block];
   3263          item->mBaselineOffset[LogicalAxis::Inline] =
   3264              itemInfo.mBaselineOffset[LogicalAxis::Inline];
   3265          item->mState[LogicalAxis::Block] |=
   3266              itemInfo.mState[LogicalAxis::Block] & ItemState::eAutoPlacement;
   3267          item->mState[LogicalAxis::Inline] |=
   3268              itemInfo.mState[LogicalAxis::Inline] & ItemState::eAutoPlacement;
   3269          break;
   3270        }
   3271      }
   3272      MOZ_ASSERT(mGridItems.Length() == len + 1, "can't find GridItemInfo");
   3273    }
   3274 
   3275    if (auto* absCB = aGridContainerFrame->GetAbsoluteContainingBlock()) {
   3276      // Prepare absolute frames before constructing GridItemInfo.
   3277      absCB->PrepareAbsoluteFrames(aGridContainerFrame);
   3278    }
   3279    // XXX NOTE: This is O(n^2) in the number of abs.pos. items. (bug 1252186)
   3280    const nsFrameList& absPosChildren = aGridContainerFrame->GetChildList(
   3281        aGridContainerFrame->GetAbsoluteListID());
   3282    for (auto f : absPosChildren) {
   3283      nsIFrame* childFirstInFlow = f->FirstInFlow();
   3284      DebugOnly<size_t> len = mAbsPosItems.Length();
   3285      for (auto& itemInfo : mSharedGridData->mAbsPosItems) {
   3286        if (itemInfo.mFrame == childFirstInFlow) {
   3287          mAbsPosItems.AppendElement(GridItemInfo(f, itemInfo.mArea));
   3288          break;
   3289        }
   3290      }
   3291      MOZ_ASSERT(mAbsPosItems.Length() == len + 1, "can't find GridItemInfo");
   3292    }
   3293 
   3294    // Copy in the computed grid info state bit
   3295    if (mSharedGridData->mGenerateComputedGridInfo) {
   3296      aGridContainerFrame->AddStateBits(NS_STATE_GRID_COMPUTED_INFO);
   3297    }
   3298  }
   3299 
   3300  /**
   3301   * Calculate our track sizes in the given axis.
   3302   */
   3303  void CalculateTrackSizesForAxis(LogicalAxis aAxis, const Grid& aGrid,
   3304                                  nscoord aCBSize,
   3305                                  SizingConstraint aConstraint);
   3306 
   3307  /**
   3308   * Invalidate track sizes for the given axis by clearing track sizing bits for
   3309   * all grid items and mark the track sizes and positions as needing recompute.
   3310   *
   3311   * This helper must be called before invoking CalculateTrackSizesForAxis()
   3312   * again in aAxis; otherwise, assertions will fire.
   3313   */
   3314  void InvalidateTrackSizesForAxis(LogicalAxis aAxis);
   3315 
   3316  /**
   3317   * Return the percentage basis for a grid item in its writing-mode based on
   3318   * track sizes and the grid area occupied by the grid item.
   3319   *
   3320   * @param aAxis the axis we're currently calculating track sizes for
   3321   */
   3322  LogicalSize PercentageBasisFor(LogicalAxis aAxis,
   3323                                 const GridItemInfo& aGridItem) const;
   3324 
   3325  /**
   3326   * Return the containing block for a grid item occupying aArea.
   3327   */
   3328  LogicalRect ContainingBlockFor(const GridArea& aArea) const;
   3329 
   3330  /**
   3331   * Return the containing block for an abs.pos. grid item occupying aArea.
   3332   * Any 'auto' lines in the grid area will be aligned with grid container
   3333   * containing block on that side.
   3334   * @param aGridOrigin the origin of the grid
   3335   * @param aGridCB the grid container containing block (its padding area)
   3336   */
   3337  LogicalRect ContainingBlockForAbsPos(const GridArea& aArea,
   3338                                       const LogicalPoint& aGridOrigin,
   3339                                       const LogicalRect& aGridCB) const;
   3340 
   3341  /**
   3342   * Apply `align/justify-content` alignment in our masonry axis.
   3343   * This aligns the "masonry box" within our content box size.
   3344   */
   3345  void AlignJustifyContentInMasonryAxis(nscoord aMasonryBoxSize,
   3346                                        nscoord aContentBoxSize);
   3347  /**
   3348   * Apply `align/justify-tracks` alignment in our masonry axis.
   3349   */
   3350  void AlignJustifyTracksInMasonryAxis(const LogicalSize& aContentSize,
   3351                                       const nsSize& aContainerSize);
   3352 
   3353  // Recursive helper for CollectSubgridItemsForAxis().
   3354  static void CollectSubgridItemsForAxisHelper(
   3355      LogicalAxis aAxis, WritingMode aContainerWM,
   3356      const LineRange& aRangeInAxis, const LineRange& aRangeInOppositeAxis,
   3357      const GridItemInfo& aItem, const nsTArray<GridItemInfo>& aItems,
   3358      nsTArray<GridItemInfo>& aResult) {
   3359    const auto oppositeAxis = GetOrthogonalAxis(aAxis);
   3360    bool itemIsSubgridInOppositeAxis = aItem.IsSubgrid(oppositeAxis);
   3361    auto subgridWM = aItem.mFrame->GetWritingMode();
   3362    bool isOrthogonal = subgridWM.IsOrthogonalTo(aContainerWM);
   3363    bool isSameDirInAxis =
   3364        subgridWM.ParallelAxisStartsOnSameSide(aAxis, aContainerWM);
   3365    bool isSameDirInOppositeAxis =
   3366        subgridWM.ParallelAxisStartsOnSameSide(oppositeAxis, aContainerWM);
   3367    if (isOrthogonal) {
   3368      // We'll Transpose the area below so these needs to be transposed as well.
   3369      std::swap(isSameDirInAxis, isSameDirInOppositeAxis);
   3370    }
   3371    uint32_t offsetInAxis = aRangeInAxis.mStart;
   3372    uint32_t gridEndInAxis = aRangeInAxis.Extent();
   3373    uint32_t offsetInOppositeAxis = aRangeInOppositeAxis.mStart;
   3374    uint32_t gridEndInOppositeAxis = aRangeInOppositeAxis.Extent();
   3375    for (const auto& subgridItem : aItems) {
   3376      auto newItem = aResult.AppendElement(
   3377          isOrthogonal ? subgridItem.Transpose() : subgridItem);
   3378      if (MOZ_UNLIKELY(!isSameDirInAxis)) {
   3379        newItem->ReverseDirection(aAxis, gridEndInAxis);
   3380      }
   3381      newItem->mArea.LineRangeForAxis(aAxis).Translate(offsetInAxis);
   3382      if (itemIsSubgridInOppositeAxis) {
   3383        if (MOZ_UNLIKELY(!isSameDirInOppositeAxis)) {
   3384          newItem->ReverseDirection(oppositeAxis, gridEndInOppositeAxis);
   3385        }
   3386        LineRange& range = newItem->mArea.LineRangeForAxis(oppositeAxis);
   3387        range.Translate(offsetInOppositeAxis);
   3388      }
   3389      if (newItem->IsSubgrid(aAxis)) {
   3390        auto* subgrid =
   3391            subgridItem.SubgridFrame()->GetProperty(Subgrid::Prop());
   3392        CollectSubgridItemsForAxisHelper(
   3393            aAxis, aContainerWM, newItem->mArea.LineRangeForAxis(aAxis),
   3394            newItem->mArea.LineRangeForAxis(oppositeAxis), *newItem,
   3395            subgrid->mGridItems, aResult);
   3396      }
   3397    }
   3398  }
   3399 
   3400  // Copy all descendant items from all our subgrid children that are subgridded
   3401  // in aAxis recursively into aResult.  All item grid area's and state are
   3402  // translated to our coordinates.
   3403  void CollectSubgridItemsForAxis(LogicalAxis aAxis,
   3404                                  nsTArray<GridItemInfo>& aResult) const {
   3405    for (const auto& item : mGridItems) {
   3406      if (item.IsSubgrid(aAxis)) {
   3407        const auto oppositeAxis = GetOrthogonalAxis(aAxis);
   3408        auto* subgrid = item.SubgridFrame()->GetProperty(Subgrid::Prop());
   3409        CollectSubgridItemsForAxisHelper(
   3410            aAxis, mWM, item.mArea.LineRangeForAxis(aAxis),
   3411            item.mArea.LineRangeForAxis(oppositeAxis), item,
   3412            subgrid->mGridItems, aResult);
   3413      }
   3414    }
   3415  }
   3416 
   3417  /**
   3418   * Recursive helper for CopyBaselineMetricsToSubgridItems().
   3419   *
   3420   * @param aAxis The LogicalAxis for the axis whose baseline metrics we're
   3421   *              copying here (with respect to the outermost parent grid's
   3422   *              writing mode).
   3423   * @param aContainerWM The writing mode of that outermost parent grid.
   3424   * @param aSubgridFrame The subgrid whose subgrid-items we're considering
   3425   *              in this recursive traversal (whose items we're copying over
   3426   *              baseline-alignment metrics for).
   3427   * @param aContainerGridItems The outermost parent grid's array of
   3428   *              GridItemInfo objects.  (The final portion of this array is
   3429   *              all for subgrid items, and that's the portion that we're
   3430   *              recursively iterating over.)
   3431   * @param aContainerGridItemsIdx [in/out] The index for the item that we're
   3432   *              currently considering in aContainerGridItemsIdx. When
   3433   *              this function returns, this will be the index just beyond the
   3434   *              last item that we handled here, i.e. the index of the next
   3435   *              item to be handled.
   3436   */
   3437  static void CopyBaselineMetricsToSubgridItemsHelper(
   3438      LogicalAxis aAxis, WritingMode aContainerWM, nsIFrame* aSubgridFrame,
   3439      const nsTArray<GridItemInfo>& aContainerGridItems,
   3440      size_t& aContainerGridItemsIdx) {
   3441    // Get the canonical GridItemInfo structs for the grid items that live
   3442    // inside of aSubgridFrame:
   3443    Subgrid* subgridProp = aSubgridFrame->GetProperty(Subgrid::Prop());
   3444    nsTArray<GridItemInfo>& subgridItems = subgridProp->mGridItems;
   3445 
   3446    // Use aSubgridFrame's writing-mode to determine subgridAxis.
   3447    // Grids & subgrids store various data on a per-LogicalAxis basis, with
   3448    // respect to their own WritingMode.  Here, subgridAxis is aSubgridFrame's
   3449    // axis that maps to the same physical axis that aAxis does for the
   3450    // outermost parent grid.
   3451    auto subgridWM = aSubgridFrame->GetWritingMode();
   3452    bool isOrthogonal = subgridWM.IsOrthogonalTo(aContainerWM);
   3453    LogicalAxis subgridAxis = isOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
   3454 
   3455    // Do a parallel walk through (1) subgridItems and (2) the portion of
   3456    // aContainerGridItems that starts at offset aContainerGridItems,
   3457    // descending to traverse child subgrids own items as we encounter them in
   3458    // subgridItems.  We expect to have an exact correspondence, because this
   3459    // is precisely how we built up this portion of aContainerGridItems in
   3460    // CollectSubgridItemsForAxis. (But if we happen to overstep the end of an
   3461    // array, or find a GridItemInfo for a frame that we don't expect, we
   3462    // gracefully bail out.)
   3463    for (auto& subgridItem : subgridItems) {
   3464      if (MOZ_UNLIKELY(aContainerGridItemsIdx >=
   3465                       aContainerGridItems.Length())) {
   3466        // We failed to make the same traversal as CollectSubgridItemsForAxis;
   3467        // whoops! This shouldn't happen; but if it does, we gracefully bail
   3468        // out, instead of crashing.
   3469        MOZ_ASSERT_UNREACHABLE("Out-of-bounds aContainerGridItemsIdx");
   3470        return;
   3471      }
   3472      const auto& itemFromContainer =
   3473          aContainerGridItems[aContainerGridItemsIdx];
   3474      aContainerGridItemsIdx++;
   3475 
   3476      if (MOZ_UNLIKELY(subgridItem.mFrame != itemFromContainer.mFrame)) {
   3477        // We failed to make the same traversal as CollectSubgridItemsForAxis;
   3478        // whoops! This shouldn't happen; but if it does, we gracefully bail
   3479        // out, instead of copying baseline-alignment data for the wrong frame.
   3480        MOZ_ASSERT_UNREACHABLE("Found unexpected frame during traversal");
   3481        return;
   3482      }
   3483 
   3484      // This pattern of bits will be truthy if the item is baseline-aligned in
   3485      // this axis (in which case the exact pattern of bits will have some
   3486      // additional significance that doesn't matter here, but we do need to
   3487      // copy it over).
   3488      const auto baselineStateBits =
   3489          itemFromContainer.mState[aAxis] & ItemState::eAllBaselineBits;
   3490 
   3491      if (subgridItem.IsSubgrid(subgridAxis)) {
   3492        // This item is in fact a nested subgrid. It shouldn't itself be
   3493        // baseline-aligned, but we need to make a recursive call to copy
   3494        // baseline metrics to its items.
   3495        MOZ_ASSERT(!baselineStateBits,
   3496                   "subgrids themselves can't be baseline-aligned "
   3497                   "(or self-aligned in any way) in their subgrid axis");
   3498        CopyBaselineMetricsToSubgridItemsHelper(
   3499            aAxis, aContainerWM, subgridItem.SubgridFrame(),
   3500            aContainerGridItems, aContainerGridItemsIdx);
   3501      } else if (baselineStateBits) {
   3502        // This item is a baseline-aligned grid item (in the subgrid that we're
   3503        // traversing). Copy over its baseline metrics.
   3504        subgridItem.mState[subgridAxis] |= baselineStateBits;
   3505        subgridItem.mBaselineOffset[subgridAxis] =
   3506            itemFromContainer.mBaselineOffset[aAxis];
   3507      }
   3508    }
   3509  }
   3510 
   3511  /**
   3512   * This function here is responsible for propagating baseline-alignment
   3513   * metrics for subgrid-items from mGridItems over to the "canonical"
   3514   * GridItemInfo structs for those grid items (which live on the subgrid that
   3515   * owns them). The outermost parent grid *computes* those metrics as part of
   3516   * doing track sizing, but it does this using *temporary* GridItemInfo
   3517   * objects for any grid items that live in subgrids (aka subgrid items). So
   3518   * that's why we need to rescue this baseline-alignment information before
   3519   * those temporary objects are discarded.
   3520   *
   3521   * (The temporary subgrid-items all live at the end of mGridItems; they were
   3522   * appended there by CollectSubgridItemsForAxis().  So, it's important that
   3523   * we perform the exact same traversal that CollectSubgridItemsForAxis() did,
   3524   * in order to properly match up the temporary & canonical GridItemInfo
   3525   * objects for these subgrid items.)
   3526   */
   3527  // traversal that CollectSubgridItemsForAxis (and its recursive helper) does.
   3528  void CopyBaselineMetricsToSubgridItems(LogicalAxis aAxis,
   3529                                         size_t aOriginalLength) {
   3530    MOZ_ASSERT(aOriginalLength <= mGridItems.Length(),
   3531               "aOriginalLength is the length that mGridItems had *before* we "
   3532               "appended temporary copies of subgrid items to it, so it's not "
   3533               "possible for it to be more than the current length");
   3534 
   3535    // This index 'subgridItemIdx' traverses the final portion of mGridItems,
   3536    // the portion that currently has temporary GridItemInfo structs that we
   3537    // built for the items that live in our subgrids.  (Our caller is about to
   3538    // discard this temporary portion of mGridItems, and we're trying to
   3539    // transfer some baseline-alignment data to the canonical GridItemInfo
   3540    // structs before that happens.)
   3541    //
   3542    // Our recursive helper updates subgridItemIdx internally. When this index
   3543    // reaches mGridItems.Length(), we can stop looping; that means we've
   3544    // finished copying out all the data from these temporary structs.
   3545    size_t subgridItemIdx = aOriginalLength;
   3546 
   3547    for (size_t i = 0;
   3548         (i < aOriginalLength && subgridItemIdx < mGridItems.Length()); i++) {
   3549      const auto& item = mGridItems[i];
   3550      if (item.IsSubgrid(aAxis)) {
   3551        CopyBaselineMetricsToSubgridItemsHelper(aAxis, mWM, item.SubgridFrame(),
   3552                                                mGridItems, subgridItemIdx);
   3553      }
   3554    }
   3555  }
   3556 
   3557  Tracks& TracksFor(LogicalAxis aAxis) {
   3558    return aAxis == LogicalAxis::Block ? mRows : mCols;
   3559  }
   3560  const Tracks& TracksFor(LogicalAxis aAxis) const {
   3561    return aAxis == LogicalAxis::Block ? mRows : mCols;
   3562  }
   3563 
   3564  CSSOrderAwareFrameIterator mIter;
   3565  const nsStylePosition* const mGridStyle;
   3566  Tracks mCols;
   3567  Tracks mRows;
   3568  TrackSizingFunctions mColFunctions;
   3569  TrackSizingFunctions mRowFunctions;
   3570  /**
   3571   * Info about each (normal flow) grid item.
   3572   */
   3573  nsTArray<GridItemInfo> mGridItems;
   3574  /**
   3575   * Info about each grid-aligned abs.pos. child.
   3576   */
   3577  nsTArray<GridItemInfo> mAbsPosItems;
   3578 
   3579  /**
   3580   * @note mReflowInput may be null when using the 2nd ctor above. In this case
   3581   * we'll construct a dummy parent reflow input if we need it to calculate
   3582   * min/max-content contributions when sizing tracks.
   3583   */
   3584  const ReflowInput* const mReflowInput;
   3585  gfxContext& mRenderingContext;
   3586  nsGridContainerFrame* const mFrame;
   3587  /** [weak] owned by mFrame's first-in-flow. */
   3588  SharedGridData* mSharedGridData = nullptr;
   3589  /** Computed border+padding with mSkipSides applied. */
   3590  LogicalMargin mBorderPadding;
   3591  /**
   3592   * BStart of this fragment in "grid space" (i.e. the concatenation of content
   3593   * areas of all fragments).  Equal to mRows.mSizes[mStartRow].mPosition,
   3594   * or, if this fragment starts after the last row, the ConsumedBSize().
   3595   */
   3596  nscoord mFragBStart = 0;
   3597  /** The start row for this fragment. */
   3598  uint32_t mStartRow = 0;
   3599  /**
   3600   * The start row for the next fragment, if any.  If mNextFragmentStartRow ==
   3601   * mStartRow then there are no rows in this fragment.
   3602   */
   3603  uint32_t mNextFragmentStartRow = 0;
   3604  /** Our tentative ApplySkipSides bits. */
   3605  LogicalSides mSkipSides;
   3606  const WritingMode mWM;
   3607  /** Initialized lazily, when we find the fragmentainer. */
   3608  bool mInFragmentainer = false;
   3609  /** Set when the grid itself is having its intrinsic size measured. */
   3610  bool mIsGridIntrinsicSizing = false;
   3611 
   3612 private:
   3613  GridReflowInput(nsGridContainerFrame* aFrame, gfxContext& aRenderingContext,
   3614                  const ReflowInput* aReflowInput,
   3615                  const nsStylePosition* aGridStyle, const WritingMode& aWM)
   3616      : mIter(aFrame, FrameChildListID::Principal),
   3617        mGridStyle(aGridStyle),
   3618        mCols(LogicalAxis::Inline),
   3619        mRows(LogicalAxis::Block),
   3620        mColFunctions(mGridStyle->mGridTemplateColumns,
   3621                      mGridStyle->mGridAutoColumns, aFrame->IsColSubgrid()),
   3622        mRowFunctions(mGridStyle->mGridTemplateRows, mGridStyle->mGridAutoRows,
   3623                      aFrame->IsRowSubgrid()),
   3624        mReflowInput(aReflowInput),
   3625        mRenderingContext(aRenderingContext),
   3626        mFrame(aFrame),
   3627        mBorderPadding(aWM),
   3628        mSkipSides(aFrame->GetWritingMode()),
   3629        mWM(aWM) {
   3630    MOZ_ASSERT(!aReflowInput || aReflowInput->mFrame == mFrame);
   3631    if (aReflowInput) {
   3632      mBorderPadding = aReflowInput->ComputedLogicalBorderPadding(mWM);
   3633      mSkipSides = aFrame->PreReflowBlockLevelLogicalSkipSides();
   3634      mBorderPadding.ApplySkipSides(mSkipSides);
   3635    }
   3636    mCols.mIsMasonry = aFrame->IsColMasonry();
   3637    mRows.mIsMasonry = aFrame->IsRowMasonry();
   3638    MOZ_ASSERT(!(mCols.mIsMasonry && mRows.mIsMasonry),
   3639               "can't have masonry layout in both axes");
   3640  }
   3641 };
   3642 
   3643 using GridReflowInput = nsGridContainerFrame::GridReflowInput;
   3644 
   3645 /**
   3646 * The Grid implements grid item placement and the state of the grid -
   3647 * the size of the explicit/implicit grid, which cells are occupied etc.
   3648 */
   3649 struct MOZ_STACK_CLASS nsGridContainerFrame::Grid {
   3650  explicit Grid(const Grid* aParentGrid = nullptr) : mParentGrid(aParentGrid) {}
   3651 
   3652  /**
   3653   * Place all child frames into the grid and expand the (implicit) grid as
   3654   * needed.  The allocated GridAreas are stored in the GridAreaProperty
   3655   * frame property on the child frame.
   3656   * @param aRepeatSizing the container's [min-|max-]*size - used to determine
   3657   *   the number of repeat(auto-fill/fit) tracks.
   3658   */
   3659  void PlaceGridItems(GridReflowInput& aGridRI,
   3660                      const RepeatTrackSizingInput& aRepeatSizing);
   3661 
   3662  void SubgridPlaceGridItems(GridReflowInput& aParentGridRI, Grid* aParentGrid,
   3663                             const GridItemInfo& aGridItem);
   3664 
   3665  /**
   3666   * As above but for an abs.pos. child.  Any 'auto' lines will be represented
   3667   * by kAutoLine in the LineRange result.
   3668   * @param aGridStart the first line in the final, but untranslated grid
   3669   * @param aGridEnd the last line in the final, but untranslated grid
   3670   */
   3671  LineRange ResolveAbsPosLineRange(const StyleGridLine& aStart,
   3672                                   const StyleGridLine& aEnd,
   3673                                   const LineNameMap& aNameMap,
   3674                                   LogicalAxis aAxis, uint32_t aExplicitGridEnd,
   3675                                   int32_t aGridStart, int32_t aGridEnd,
   3676                                   const nsStylePosition* aStyle);
   3677 
   3678  /**
   3679   * Return a GridArea for abs.pos. item with non-auto lines placed at
   3680   * a definite line (1-based) with placement errors resolved.  One or both
   3681   * positions may still be 'auto'.
   3682   * @param aChild the abs.pos. grid item to place
   3683   * @param aStyle the StylePosition() for the grid container
   3684   */
   3685  GridArea PlaceAbsPos(nsIFrame* aChild, const LineNameMap& aColLineNameMap,
   3686                       const LineNameMap& aRowLineNameMap,
   3687                       const nsStylePosition* aStyle);
   3688 
   3689  /**
   3690   * Find the first column in row aLockedRow starting at aStartCol where aArea
   3691   * could be placed without overlapping other items.  The returned column may
   3692   * cause aArea to overflow the current implicit grid bounds if placed there.
   3693   */
   3694  uint32_t FindAutoCol(uint32_t aStartCol, uint32_t aLockedRow,
   3695                       const GridArea* aArea) const;
   3696 
   3697  /**
   3698   * Place aArea in the first column (in row aArea->mRows.mStart) starting at
   3699   * aStartCol without overlapping other items.  The resulting aArea may
   3700   * overflow the current implicit grid bounds.
   3701   * @param aClampMaxColLine the maximum allowed column line number (zero-based)
   3702   * Pre-condition: aArea->mRows.IsDefinite() is true.
   3703   * Post-condition: aArea->IsDefinite() is true.
   3704   */
   3705  void PlaceAutoCol(uint32_t aStartCol, GridArea* aArea,
   3706                    uint32_t aClampMaxColLine) const;
   3707 
   3708  /**
   3709   * Find the first row in column aLockedCol starting at aStartRow where aArea
   3710   * could be placed without overlapping other items.  The returned row may
   3711   * cause aArea to overflow the current implicit grid bounds if placed there.
   3712   */
   3713  uint32_t FindAutoRow(uint32_t aLockedCol, uint32_t aStartRow,
   3714                       const GridArea* aArea) const;
   3715 
   3716  /**
   3717   * Place aArea in the first row (in column aArea->mCols.mStart) starting at
   3718   * aStartRow without overlapping other items. The resulting aArea may
   3719   * overflow the current implicit grid bounds.
   3720   * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
   3721   * Pre-condition: aArea->mCols.IsDefinite() is true.
   3722   * Post-condition: aArea->IsDefinite() is true.
   3723   */
   3724  void PlaceAutoRow(uint32_t aStartRow, GridArea* aArea,
   3725                    uint32_t aClampMaxRowLine) const;
   3726 
   3727  /**
   3728   * Place aArea in the first column starting at aStartCol,aStartRow without
   3729   * causing it to overlap other items or overflow mGridColEnd.
   3730   * If there's no such column in aStartRow, continue in position 1,aStartRow+1.
   3731   * @param aClampMaxColLine the maximum allowed column line number (zero-based)
   3732   * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
   3733   * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
   3734   * Post-condition: aArea->IsDefinite() is true.
   3735   */
   3736  void PlaceAutoAutoInRowOrder(uint32_t aStartCol, uint32_t aStartRow,
   3737                               GridArea* aArea, uint32_t aClampMaxColLine,
   3738                               uint32_t aClampMaxRowLine) const;
   3739 
   3740  /**
   3741   * Place aArea in the first row starting at aStartCol,aStartRow without
   3742   * causing it to overlap other items or overflow mGridRowEnd.
   3743   * If there's no such row in aStartCol, continue in position aStartCol+1,1.
   3744   * @param aClampMaxColLine the maximum allowed column line number (zero-based)
   3745   * @param aClampMaxRowLine the maximum allowed row line number (zero-based)
   3746   * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
   3747   * Post-condition: aArea->IsDefinite() is true.
   3748   */
   3749  void PlaceAutoAutoInColOrder(uint32_t aStartCol, uint32_t aStartRow,
   3750                               GridArea* aArea, uint32_t aClampMaxColLine,
   3751                               uint32_t aClampMaxRowLine) const;
   3752 
   3753  /**
   3754   * Return aLine if it's inside the aMin..aMax range (inclusive),
   3755   * otherwise return kAutoLine.
   3756   */
   3757  static int32_t AutoIfOutside(int32_t aLine, int32_t aMin, int32_t aMax) {
   3758    MOZ_ASSERT(aMin <= aMax);
   3759    if (aLine < aMin || aLine > aMax) {
   3760      return kAutoLine;
   3761    }
   3762    return aLine;
   3763  }
   3764 
   3765  /**
   3766   * Inflate the implicit grid to include aArea.
   3767   * @param aArea may be definite or auto
   3768   */
   3769  void InflateGridFor(const GridArea& aArea) {
   3770    mGridColEnd = std::max(mGridColEnd, aArea.mCols.HypotheticalEnd());
   3771    mGridRowEnd = std::max(mGridRowEnd, aArea.mRows.HypotheticalEnd());
   3772    MOZ_ASSERT(mGridColEnd <= kTranslatedMaxLine &&
   3773               mGridRowEnd <= kTranslatedMaxLine);
   3774  }
   3775 
   3776  /**
   3777   * Calculates the empty tracks in a repeat(auto-fit).
   3778   * @param aOutNumEmptyLines Outputs the number of tracks which are empty.
   3779   * @param aSizingFunctions Sizing functions for the relevant axis.
   3780   * @param aNumGridLines Number of grid lines for the relevant axis.
   3781   * @param aIsEmptyFunc Functor to check if a cell is empty. This should be
   3782   * mCellMap.IsColEmpty or mCellMap.IsRowEmpty, depending on the axis.
   3783   */
   3784  template <typename IsEmptyFuncT>
   3785  static Maybe<nsTArray<uint32_t>> CalculateAdjustForAutoFitElements(
   3786      uint32_t* aOutNumEmptyTracks, TrackSizingFunctions& aSizingFunctions,
   3787      uint32_t aNumGridLines, IsEmptyFuncT aIsEmptyFunc);
   3788 
   3789  /**
   3790   * Return a line number for (non-auto) aLine, per:
   3791   * https://drafts.csswg.org/css-grid-2/#line-placement
   3792   * @param aLine style data for the line (must be non-auto)
   3793   * @param aNth a number of lines to find from aFromIndex, negative if the
   3794   *             search should be in reverse order.  In the case aLine has
   3795   *             a specified line name, it's permitted to pass in zero which
   3796   *             will be treated as one.
   3797   * @param aFromIndex the zero-based index to start counting from
   3798   * @param aLineNameList the explicit named lines
   3799   * @param aSide the axis+edge we're resolving names for (e.g. if we're
   3800                  resolving a grid-row-start line, pass LogicalSide::BStart)
   3801   * @param aExplicitGridEnd the last line in the explicit grid
   3802   * @param aStyle the StylePosition() for the grid container
   3803   * @return a definite line (1-based), clamped to
   3804   *   the mClampMinLine..mClampMaxLine range
   3805   */
   3806  int32_t ResolveLine(const StyleGridLine& aLine, int32_t aNth,
   3807                      uint32_t aFromIndex, const LineNameMap& aNameMap,
   3808                      LogicalSide aSide, uint32_t aExplicitGridEnd,
   3809                      const nsStylePosition* aStyle);
   3810 
   3811  /**
   3812   * Helper method for ResolveLineRange.
   3813   * @see ResolveLineRange
   3814   * @return a pair (start,end) of lines
   3815   */
   3816  typedef std::pair<int32_t, int32_t> LinePair;
   3817  LinePair ResolveLineRangeHelper(const StyleGridLine& aStart,
   3818                                  const StyleGridLine& aEnd,
   3819                                  const LineNameMap& aNameMap,
   3820                                  LogicalAxis aAxis, uint32_t aExplicitGridEnd,
   3821                                  const nsStylePosition* aStyle);
   3822 
   3823  /**
   3824   * Return a LineRange based on the given style data. Non-auto lines
   3825   * are resolved to a definite line number (1-based) per:
   3826   * https://drafts.csswg.org/css-grid-2/#line-placement
   3827   * with placement errors corrected per:
   3828   * https://drafts.csswg.org/css-grid-2/#grid-placement-errors
   3829   * @param aStyle the StylePosition() for the grid container
   3830   * @param aStart style data for the start line
   3831   * @param aEnd style data for the end line
   3832   * @param aLineNameList the explicit named lines
   3833   * @param aAxis the axis we're resolving names in
   3834   * @param aExplicitGridEnd the last line in the explicit grid
   3835   * @param aStyle the StylePosition() for the grid container
   3836   */
   3837  LineRange ResolveLineRange(const StyleGridLine& aStart,
   3838                             const StyleGridLine& aEnd,
   3839                             const LineNameMap& aNameMap, LogicalAxis aAxis,
   3840                             uint32_t aExplicitGridEnd,
   3841                             const nsStylePosition* aStyle);
   3842 
   3843  /**
   3844   * Return a GridArea with non-auto lines placed at a definite line (1-based)
   3845   * with placement errors resolved.  One or both positions may still
   3846   * be 'auto'.
   3847   * @param aChild the grid item
   3848   * @param aStyle the StylePosition() for the grid container
   3849   */
   3850  GridArea PlaceDefinite(nsIFrame* aChild, const LineNameMap& aColLineNameMap,
   3851                         const LineNameMap& aRowLineNameMap,
   3852                         const nsStylePosition* aStyle);
   3853 
   3854  bool HasImplicitNamedArea(nsAtom* aName) const {
   3855    return mAreas && mAreas->has(aName);
   3856  }
   3857 
   3858  // Return true if aString ends in aSuffix and has at least one character
   3859  // before the suffix. Assign aIndex to where the suffix starts.
   3860  static bool IsNameWithSuffix(nsAtom* aString, const nsString& aSuffix,
   3861                               uint32_t* aIndex) {
   3862    if (StringEndsWith(nsDependentAtomString(aString), aSuffix)) {
   3863      *aIndex = aString->GetLength() - aSuffix.Length();
   3864      return *aIndex != 0;
   3865    }
   3866    return false;
   3867  }
   3868 
   3869  static bool IsNameWithEndSuffix(nsAtom* aString, uint32_t* aIndex) {
   3870    return IsNameWithSuffix(aString, u"-end"_ns, aIndex);
   3871  }
   3872 
   3873  static bool IsNameWithStartSuffix(nsAtom* aString, uint32_t* aIndex) {
   3874    return IsNameWithSuffix(aString, u"-start"_ns, aIndex);
   3875  }
   3876 
   3877  // Return the relevant parent LineNameMap for the given subgrid axis aAxis.
   3878  const LineNameMap* ParentLineMapForAxis(bool aIsOrthogonal,
   3879                                          LogicalAxis aAxis) const {
   3880    if (!mParentGrid) {
   3881      return nullptr;
   3882    }
   3883    bool isRows = aIsOrthogonal == (aAxis == LogicalAxis::Inline);
   3884    return isRows ? mParentGrid->mRowNameMap : mParentGrid->mColNameMap;
   3885  }
   3886 
   3887  void SetLineMaps(const LineNameMap* aColNameMap,
   3888                   const LineNameMap* aRowNameMap) {
   3889    mColNameMap = aColNameMap;
   3890    mRowNameMap = aRowNameMap;
   3891  }
   3892 
   3893  /**
   3894   * A CellMap holds state for each cell in the grid.
   3895   * It's row major.  It's sparse in the sense that it only has enough rows to
   3896   * cover the last row that has a grid item.  Each row only has enough entries
   3897   * to cover columns that are occupied *on that row*, i.e. it's not a full
   3898   * matrix covering the entire implicit grid.  An absent Cell means that it's
   3899   * unoccupied by any grid item.
   3900   */
   3901  struct CellMap {
   3902    struct Cell {
   3903      constexpr Cell() : mIsOccupied(false) {}
   3904      bool mIsOccupied : 1;
   3905    };
   3906 
   3907    void Fill(const GridArea& aGridArea) {
   3908      MOZ_ASSERT(aGridArea.IsDefinite());
   3909      MOZ_ASSERT(aGridArea.mRows.mStart < aGridArea.mRows.mEnd);
   3910      MOZ_ASSERT(aGridArea.mCols.mStart < aGridArea.mCols.mEnd);
   3911      const auto numRows = aGridArea.mRows.mEnd;
   3912      const auto numCols = aGridArea.mCols.mEnd;
   3913      mCells.EnsureLengthAtLeast(numRows);
   3914      for (auto i = aGridArea.mRows.mStart; i < numRows; ++i) {
   3915        nsTArray<Cell>& cellsInRow = mCells[i];
   3916        cellsInRow.EnsureLengthAtLeast(numCols);
   3917        for (auto j = aGridArea.mCols.mStart; j < numCols; ++j) {
   3918          cellsInRow[j].mIsOccupied = true;
   3919        }
   3920      }
   3921    }
   3922 
   3923    uint32_t IsEmptyCol(uint32_t aCol) const {
   3924      for (auto& row : mCells) {
   3925        if (aCol < row.Length() && row[aCol].mIsOccupied) {
   3926          return false;
   3927        }
   3928      }
   3929      return true;
   3930    }
   3931    uint32_t IsEmptyRow(uint32_t aRow) const {
   3932      if (aRow >= mCells.Length()) {
   3933        return true;
   3934      }
   3935      for (const Cell& cell : mCells[aRow]) {
   3936        if (cell.mIsOccupied) {
   3937          return false;
   3938        }
   3939      }
   3940      return true;
   3941    }
   3942 #ifdef DEBUG
   3943    void Dump() const {
   3944      const size_t numRows = mCells.Length();
   3945      for (size_t i = 0; i < numRows; ++i) {
   3946        const nsTArray<Cell>& cellsInRow = mCells[i];
   3947        const size_t numCols = cellsInRow.Length();
   3948        printf("%lu:\t", (unsigned long)i + 1);
   3949        for (size_t j = 0; j < numCols; ++j) {
   3950          printf(cellsInRow[j].mIsOccupied ? "X " : ". ");
   3951        }
   3952        printf("\n");
   3953      }
   3954    }
   3955 #endif
   3956 
   3957    nsTArray<nsTArray<Cell>> mCells;
   3958  };
   3959 
   3960  /**
   3961   * State for each cell in the grid.
   3962   */
   3963  CellMap mCellMap;
   3964  /**
   3965   * @see HasImplicitNamedArea.
   3966   */
   3967  ImplicitNamedAreas* mAreas;
   3968  /**
   3969   * The last column grid line (1-based) in the explicit grid.
   3970   * (i.e. the number of explicit columns + 1)
   3971   */
   3972  uint32_t mExplicitGridColEnd;
   3973  /**
   3974   * The last row grid line (1-based) in the explicit grid.
   3975   * (i.e. the number of explicit rows + 1)
   3976   */
   3977  uint32_t mExplicitGridRowEnd;
   3978  // Same for the implicit grid, except these become zero-based after
   3979  // resolving definite lines.
   3980  uint32_t mGridColEnd;
   3981  uint32_t mGridRowEnd;
   3982 
   3983  /**
   3984   * Offsets from the start of the implicit grid to the start of the translated
   3985   * explicit grid.  They are zero if there are no implicit lines before 1,1.
   3986   * e.g. "grid-column: span 3 / 1" makes mExplicitGridOffsetCol = 3 and the
   3987   * corresponding GridArea::mCols will be 0 / 3 in the zero-based translated
   3988   * grid.
   3989   */
   3990  uint32_t mExplicitGridOffsetCol;
   3991  uint32_t mExplicitGridOffsetRow;
   3992 
   3993  /**
   3994   * Our parent grid if any.
   3995   */
   3996  const Grid* mParentGrid;
   3997 
   3998  /**
   3999   * Our LineNameMaps.
   4000   */
   4001  const LineNameMap* mColNameMap;
   4002  const LineNameMap* mRowNameMap;
   4003 };
   4004 
   4005 /**
   4006 * Compute margin+border+padding for aGridItem.mFrame (a subgrid) and store it
   4007 * on its Subgrid property (and return that property).
   4008 * aPercentageBasis is in the grid item's writing-mode.
   4009 */
   4010 static Subgrid* SubgridComputeMarginBorderPadding(
   4011    const GridItemInfo& aGridItem, const LogicalSize& aPercentageBasis) {
   4012  auto* subgridFrame = aGridItem.SubgridFrame();
   4013  auto cbWM = aGridItem.mFrame->GetParent()->GetWritingMode();
   4014  auto* subgrid = subgridFrame->GetProperty(Subgrid::Prop());
   4015  auto wm = subgridFrame->GetWritingMode();
   4016  auto pmPercentageBasis = cbWM.IsOrthogonalTo(wm) ? aPercentageBasis.BSize(wm)
   4017                                                   : aPercentageBasis.ISize(wm);
   4018  SizeComputationInput sz(subgridFrame, nullptr, cbWM, pmPercentageBasis);
   4019  subgrid->mMarginBorderPadding =
   4020      sz.ComputedLogicalMargin(cbWM) + sz.ComputedLogicalBorderPadding(cbWM);
   4021  if (aGridItem.mFrame == subgridFrame) {
   4022    return subgrid;
   4023  }
   4024 
   4025  bool scroller = false;
   4026  nsIFrame* outerFrame = nullptr;
   4027  if (ScrollContainerFrame* scrollContainerFrame =
   4028          aGridItem.mFrame->GetScrollTargetFrame()) {
   4029    scroller = true;
   4030    outerFrame = scrollContainerFrame;
   4031  }
   4032 
   4033  if (outerFrame) {
   4034    MOZ_ASSERT(sz.ComputedLogicalMargin(cbWM) == LogicalMargin(cbWM) &&
   4035                   sz.ComputedLogicalBorder(cbWM) == LogicalMargin(cbWM),
   4036               "A scrolled inner frame / button content frame "
   4037               "should not have any margin or border / padding!");
   4038 
   4039    // Add the margin and border from the (outer) frame. Padding is factored-in
   4040    // for scrollers already (except for the scrollbar gutter), but not for
   4041    // button-content.
   4042    SizeComputationInput szOuterFrame(outerFrame, nullptr, cbWM,
   4043                                      pmPercentageBasis);
   4044    subgrid->mMarginBorderPadding += szOuterFrame.ComputedLogicalMargin(cbWM) +
   4045                                     szOuterFrame.ComputedLogicalBorder(cbWM);
   4046    if (scroller) {
   4047      nsMargin ssz = static_cast<ScrollContainerFrame*>(outerFrame)
   4048                         ->IntrinsicScrollbarGutterSize();
   4049      subgrid->mMarginBorderPadding += LogicalMargin(cbWM, ssz);
   4050    } else {
   4051      subgrid->mMarginBorderPadding +=
   4052          szOuterFrame.ComputedLogicalPadding(cbWM);
   4053    }
   4054  }
   4055 
   4056  if (nsFieldSetFrame* f = do_QueryFrame(aGridItem.mFrame)) {
   4057    const auto* inner = f->GetInner();
   4058    auto wm = inner->GetWritingMode();
   4059    LogicalPoint pos = inner->GetLogicalPosition(aGridItem.mFrame->GetSize());
   4060    // The legend is always on the BStart side and it inflates the fieldset's
   4061    // "border area" size.  The inner frame's b-start pos equals that size.
   4062    LogicalMargin offsets(wm, pos.B(wm), 0, 0, 0);
   4063    subgrid->mMarginBorderPadding += offsets.ConvertTo(cbWM, wm);
   4064  }
   4065 
   4066  return subgrid;
   4067 }
   4068 
   4069 static void CopyUsedTrackSizes(TrackPlan& aResult,
   4070                               const nsGridContainerFrame* aUsedTrackSizesFrame,
   4071                               const UsedTrackSizes* aUsedTrackSizes,
   4072                               const nsGridContainerFrame* aSubgridFrame,
   4073                               const Subgrid* aSubgrid,
   4074                               LogicalAxis aSubgridAxis) {
   4075  MOZ_ASSERT(aSubgridFrame->ParentGridContainerForSubgrid() ==
   4076             aUsedTrackSizesFrame);
   4077  aResult.SetLength(aSubgridAxis == LogicalAxis::Inline
   4078                        ? aSubgrid->mGridColEnd
   4079                        : aSubgrid->mGridRowEnd);
   4080  auto parentAxis =
   4081      aSubgrid->mIsOrthogonal ? GetOrthogonalAxis(aSubgridAxis) : aSubgridAxis;
   4082  const auto& parentSizes = aUsedTrackSizes->mTrackPlans[parentAxis];
   4083  MOZ_ASSERT(aUsedTrackSizes->mCanResolveLineRangeSize[parentAxis]);
   4084  if (parentSizes.IsEmpty()) {
   4085    return;
   4086  }
   4087  const auto& range = aSubgrid->mArea.LineRangeForAxis(parentAxis);
   4088  const auto cbwm = aUsedTrackSizesFrame->GetWritingMode();
   4089  const auto wm = aSubgridFrame->GetWritingMode();
   4090  // Recompute the MBP to resolve percentages against the resolved track sizes.
   4091  if (parentAxis == LogicalAxis::Inline) {
   4092    // Find the subgrid's grid item frame in its parent grid container.  This
   4093    // is usually the same as aSubgridFrame but it may also have a ScrollFrame,
   4094    // FieldSetFrame etc.  We just loop until we see the first ancestor
   4095    // GridContainerFrame and pick the last frame we saw before that.
   4096    // Note that all subgrids are inside a parent (sub)grid container.
   4097    const nsIFrame* outerGridItemFrame = aSubgridFrame;
   4098    for (nsIFrame* parent = aSubgridFrame->GetParent();
   4099         parent != aUsedTrackSizesFrame; parent = parent->GetParent()) {
   4100      MOZ_ASSERT(!parent->IsGridContainerFrame());
   4101      outerGridItemFrame = parent;
   4102    }
   4103    auto sizeInAxis = range.ToLength(aUsedTrackSizes->mTrackPlans[parentAxis]);
   4104    LogicalSize pmPercentageBasis =
   4105        aSubgrid->mIsOrthogonal ? LogicalSize(wm, nscoord(0), sizeInAxis)
   4106                                : LogicalSize(wm, sizeInAxis, nscoord(0));
   4107    GridItemInfo info(const_cast<nsIFrame*>(outerGridItemFrame),
   4108                      aSubgrid->mArea);
   4109    SubgridComputeMarginBorderPadding(info, pmPercentageBasis);
   4110  }
   4111  const LogicalMargin& mbp = aSubgrid->mMarginBorderPadding;
   4112  nscoord startMBP;
   4113  nscoord endMBP;
   4114  if (MOZ_LIKELY(cbwm.ParallelAxisStartsOnSameSide(parentAxis, wm))) {
   4115    startMBP = mbp.Start(parentAxis, cbwm);
   4116    endMBP = mbp.End(parentAxis, cbwm);
   4117    uint32_t i = range.mStart;
   4118    nscoord startPos = parentSizes[i].mPosition + startMBP;
   4119    for (auto& sz : aResult) {
   4120      sz = parentSizes[i++];
   4121      sz.mPosition -= startPos;
   4122    }
   4123  } else {
   4124    startMBP = mbp.End(parentAxis, cbwm);
   4125    endMBP = mbp.Start(parentAxis, cbwm);
   4126    uint32_t i = range.mEnd - 1;
   4127    const auto& parentEnd = parentSizes[i];
   4128    nscoord parentEndPos = parentEnd.mPosition + parentEnd.mBase - startMBP;
   4129    for (auto& sz : aResult) {
   4130      sz = parentSizes[i--];
   4131      sz.mPosition = parentEndPos - (sz.mPosition + sz.mBase);
   4132    }
   4133  }
   4134  auto& startTrack = aResult[0];
   4135  startTrack.mPosition = 0;
   4136  startTrack.mBase -= startMBP;
   4137  if (MOZ_UNLIKELY(startTrack.mBase < nscoord(0))) {
   4138    // Our MBP doesn't fit in the start track.  Adjust the track position
   4139    // to maintain track alignment with our parent.
   4140    startTrack.mPosition = startTrack.mBase;
   4141    startTrack.mBase = nscoord(0);
   4142  }
   4143  auto& endTrack = aResult.LastElement();
   4144  endTrack.mBase -= endMBP;
   4145  if (MOZ_UNLIKELY(endTrack.mBase < nscoord(0))) {
   4146    endTrack.mBase = nscoord(0);
   4147  }
   4148 }
   4149 
   4150 void nsGridContainerFrame::UsedTrackSizes::ResolveTrackSizesForAxis(
   4151    nsGridContainerFrame* aFrame, LogicalAxis aAxis, gfxContext& aRC) {
   4152  if (mCanResolveLineRangeSize[aAxis]) {
   4153    return;
   4154  }
   4155  if (!aFrame->IsSubgrid()) {
   4156    // We can't resolve sizes in this axis at this point. aFrame is the top grid
   4157    // container, which will store its final track sizes later once they're
   4158    // resolved in this axis (in GridReflowInput::CalculateTrackSizesForAxis).
   4159    // The single caller of this method only needs track sizes for
   4160    // calculating a CB size and it will treat it as indefinite when
   4161    // this happens.
   4162    return;
   4163  }
   4164  auto* parent = aFrame->ParentGridContainerForSubgrid();
   4165  auto* parentSizes = parent->GetUsedTrackSizes();
   4166  if (!parentSizes) {
   4167    parentSizes = new UsedTrackSizes();
   4168    parent->SetProperty(UsedTrackSizes::Prop(), parentSizes);
   4169  }
   4170  auto* subgrid = aFrame->GetProperty(Subgrid::Prop());
   4171  const auto parentAxis =
   4172      subgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
   4173  parentSizes->ResolveTrackSizesForAxis(parent, parentAxis, aRC);
   4174  if (!parentSizes->mCanResolveLineRangeSize[parentAxis]) {
   4175    if (aFrame->IsSubgrid(aAxis)) {
   4176      ResolveSubgridTrackSizesForAxis(aFrame, aAxis, subgrid, aRC,
   4177                                      NS_UNCONSTRAINEDSIZE);
   4178    }
   4179    return;
   4180  }
   4181  if (aFrame->IsSubgrid(aAxis)) {
   4182    CopyUsedTrackSizes(mTrackPlans[aAxis], parent, parentSizes, aFrame, subgrid,
   4183                       aAxis);
   4184    mCanResolveLineRangeSize[aAxis] = true;
   4185  } else {
   4186    const auto& range = subgrid->mArea.LineRangeForAxis(parentAxis);
   4187    nscoord contentBoxSize =
   4188        range.ToLength(parentSizes->mTrackPlans[parentAxis]);
   4189    auto parentWM = aFrame->GetParent()->GetWritingMode();
   4190    contentBoxSize -=
   4191        subgrid->mMarginBorderPadding.StartEnd(parentAxis, parentWM);
   4192    contentBoxSize = std::max(nscoord(0), contentBoxSize);
   4193    ResolveSubgridTrackSizesForAxis(aFrame, aAxis, subgrid, aRC,
   4194                                    contentBoxSize);
   4195  }
   4196 }
   4197 
   4198 void nsGridContainerFrame::UsedTrackSizes::ResolveSubgridTrackSizesForAxis(
   4199    nsGridContainerFrame* aFrame, LogicalAxis aAxis, Subgrid* aSubgrid,
   4200    gfxContext& aRC, nscoord aContentBoxSize) {
   4201  GridReflowInput gridRI(aFrame, aRC);
   4202  gridRI.mGridItems = aSubgrid->mGridItems.Clone();
   4203  Grid grid;
   4204  grid.mGridColEnd = aSubgrid->mGridColEnd;
   4205  grid.mGridRowEnd = aSubgrid->mGridRowEnd;
   4206  gridRI.CalculateTrackSizesForAxis(aAxis, grid, aContentBoxSize,
   4207                                    SizingConstraint::NoConstraint);
   4208  const auto& tracks = gridRI.TracksFor(aAxis);
   4209  mTrackPlans[aAxis].Assign(tracks.mSizes);
   4210  mCanResolveLineRangeSize[aAxis] = tracks.mCanResolveLineRangeSize;
   4211  MOZ_ASSERT(mCanResolveLineRangeSize[aAxis]);
   4212 }
   4213 
   4214 void nsGridContainerFrame::GridReflowInput::CalculateTrackSizesForAxis(
   4215    LogicalAxis aAxis, const Grid& aGrid, nscoord aContentBoxSize,
   4216    SizingConstraint aConstraint) {
   4217  auto& tracks = TracksFor(aAxis);
   4218  const auto& sizingFunctions =
   4219      aAxis == LogicalAxis::Inline ? mColFunctions : mRowFunctions;
   4220  const auto& gapStyle = aAxis == LogicalAxis::Inline ? mGridStyle->mColumnGap
   4221                                                      : mGridStyle->mRowGap;
   4222  if (tracks.mIsMasonry) {
   4223    // See comment on nsGridContainerFrame::MasonryLayout().
   4224    tracks.Initialize(sizingFunctions, gapStyle, 2, aContentBoxSize);
   4225    tracks.mCanResolveLineRangeSize = true;
   4226    return;
   4227  }
   4228  uint32_t gridEnd =
   4229      aAxis == LogicalAxis::Inline ? aGrid.mGridColEnd : aGrid.mGridRowEnd;
   4230  Maybe<TrackSizingFunctions> fallbackTrackSizing;
   4231 
   4232  bool useParentGaps = false;
   4233  const bool isSubgriddedAxis = mFrame->IsSubgrid(aAxis);
   4234  if (MOZ_LIKELY(!isSubgriddedAxis)) {
   4235    tracks.Initialize(sizingFunctions, gapStyle, gridEnd, aContentBoxSize);
   4236  } else {
   4237    tracks.mGridGap =
   4238        nsLayoutUtils::ResolveGapToLength(gapStyle, aContentBoxSize);
   4239    tracks.mContentBoxSize = aContentBoxSize;
   4240    const auto* subgrid = mFrame->GetProperty(Subgrid::Prop());
   4241    tracks.mSizes.SetLength(gridEnd);
   4242    auto* parent = mFrame->ParentGridContainerForSubgrid();
   4243    auto parentAxis = subgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
   4244    const auto* parentSizes = parent->GetUsedTrackSizes();
   4245    if (parentSizes && parentSizes->mCanResolveLineRangeSize[parentAxis]) {
   4246      CopyUsedTrackSizes(tracks.mSizes, parent, parentSizes, mFrame, subgrid,
   4247                         aAxis);
   4248      useParentGaps = gapStyle.IsNormal();
   4249    } else {
   4250      fallbackTrackSizing.emplace(TrackSizingFunctions::ForSubgridFallback(
   4251          mFrame, subgrid, parent, parentAxis));
   4252      tracks.Initialize(*fallbackTrackSizing, gapStyle, gridEnd,
   4253                        aContentBoxSize);
   4254    }
   4255  }
   4256 
   4257  // We run the Track Sizing Algorithm in non-subgridded axes, and in some
   4258  // cases in a subgridded axis when our parent track sizes aren't resolved yet.
   4259  if (MOZ_LIKELY(!isSubgriddedAxis) || fallbackTrackSizing.isSome()) {
   4260    const size_t origGridItemCount = mGridItems.Length();
   4261    const bool hasSubgridItems = mFrame->HasSubgridItems(aAxis);
   4262    if (hasSubgridItems) {
   4263      AutoTArray<GridItemInfo, 8> collectedItems;
   4264      CollectSubgridItemsForAxis(aAxis, collectedItems);
   4265      mGridItems.AppendElements(collectedItems);
   4266    }
   4267    tracks.CalculateSizes(
   4268        *this, mGridItems,
   4269        fallbackTrackSizing ? *fallbackTrackSizing : sizingFunctions,
   4270        aContentBoxSize,
   4271        aAxis == LogicalAxis::Inline ? &GridArea::mCols : &GridArea::mRows,
   4272        aConstraint);
   4273 
   4274    if (hasSubgridItems &&
   4275        StaticPrefs::layout_css_grid_subgrid_baselines_enabled()) {
   4276      // If any of the subgrid items are baseline-aligned, we've just recorded
   4277      // their baseline-alignment offsets in our own copy of their GridItemInfo
   4278      // structs. Before we get rid of those copies (via TruncateLength), we
   4279      // have to copy these offsets back to the subgrids' versions of the
   4280      // GridItemInfo structs.
   4281      //
   4282      // XXXdholbert This new behavior is behind a pref due to bug 1871719.
   4283      CopyBaselineMetricsToSubgridItems(aAxis, origGridItemCount);
   4284    }
   4285    mGridItems.TruncateLength(origGridItemCount);
   4286  }
   4287  if (isSubgriddedAxis) {
   4288    // XXXdholbert This is a bit hacky, but this is something that
   4289    // tracks.CalculateSizes does internally (unconditionally, if there are
   4290    // baseline-aligned items), and it seems like subgrids need to do it too,
   4291    // or else they hit the "unexpected baseline subtree alignment"
   4292    // fatal-assert when aligning their children with the baseline-alignment
   4293    // information that they received from the outer grid.
   4294    // (This might be entirely unnecessary? Aside from the default ::AUTO
   4295    // value, it looks like the ::First entry is always set to ::START and
   4296    // the ::Last entry is always set to ::END...)
   4297    tracks.mBaselineSubtreeAlign[BaselineSharingGroup::First] =
   4298        StyleAlignFlags::START;
   4299    tracks.mBaselineSubtreeAlign[BaselineSharingGroup::Last] =
   4300        StyleAlignFlags::END;
   4301  }
   4302 
   4303  if (aContentBoxSize != NS_UNCONSTRAINEDSIZE) {
   4304    auto alignment = mGridStyle->UsedContentAlignment(tracks.mAxis);
   4305    tracks.AlignJustifyContent(mGridStyle, alignment, mWM, aContentBoxSize,
   4306                               isSubgriddedAxis);
   4307  } else if (!useParentGaps) {
   4308    const nscoord gridGap = tracks.mGridGap;
   4309    nscoord pos = 0;
   4310    for (TrackSize& sz : tracks.mSizes) {
   4311      sz.mPosition = pos;
   4312      pos += sz.mBase + gridGap;
   4313    }
   4314  }
   4315 
   4316  if (aConstraint == SizingConstraint::NoConstraint &&
   4317      (mFrame->HasSubgridItems() || mFrame->IsSubgrid())) {
   4318    mFrame->StoreUsedTrackSizes(aAxis, tracks.mSizes);
   4319  }
   4320 
   4321  // positions and sizes are now final
   4322  tracks.mCanResolveLineRangeSize = true;
   4323 }
   4324 
   4325 void nsGridContainerFrame::GridReflowInput::InvalidateTrackSizesForAxis(
   4326    LogicalAxis aAxis) {
   4327  for (auto& item : mGridItems) {
   4328    item.ResetTrackSizingBits(aAxis);
   4329  }
   4330  TracksFor(aAxis).mCanResolveLineRangeSize = false;
   4331 }
   4332 
   4333 // Align an item's margin box in its aAxis inside aCBSize.
   4334 static void AlignJustifySelf(StyleAlignFlags aAlignment, LogicalAxis aAxis,
   4335                             AlignJustifyFlags aFlags, nscoord aBaselineAdjust,
   4336                             nscoord aCBSize, const ReflowInput& aRI,
   4337                             const LogicalSize& aChildSize,
   4338                             LogicalPoint* aPos) {
   4339  MOZ_ASSERT(aAlignment != StyleAlignFlags::AUTO,
   4340             "unexpected 'auto' "
   4341             "computed value for normal flow grid item");
   4342 
   4343  // NOTE: this is the resulting frame offset (border box).
   4344  nscoord offset = CSSAlignUtils::AlignJustifySelf(
   4345      aAlignment, aAxis, aFlags, aBaselineAdjust, aCBSize, aRI, aChildSize);
   4346 
   4347  // Set the position (aPos) for the requested alignment.
   4348  if (offset != 0) {
   4349    WritingMode wm = aRI.GetWritingMode();
   4350    nscoord& pos = aAxis == LogicalAxis::Block ? aPos->B(wm) : aPos->I(wm);
   4351    pos += MOZ_LIKELY(aFlags.contains(AlignJustifyFlag::SameSide)) ? offset
   4352                                                                   : -offset;
   4353  }
   4354 }
   4355 
   4356 static void AlignSelf(const nsGridContainerFrame::GridItemInfo& aGridItem,
   4357                      StyleAlignFlags aAlignSelf, nscoord aCBSize,
   4358                      const WritingMode aCBWM, const ReflowInput& aRI,
   4359                      const LogicalSize& aSize, AlignJustifyFlags aFlags,
   4360                      LogicalPoint* aPos) {
   4361  AlignJustifyFlags flags = aFlags;
   4362  if (aAlignSelf & StyleAlignFlags::SAFE) {
   4363    flags += AlignJustifyFlag::OverflowSafe;
   4364  }
   4365  aAlignSelf &= ~StyleAlignFlags::FLAG_BITS;
   4366 
   4367  WritingMode childWM = aRI.GetWritingMode();
   4368  if (aCBWM.ParallelAxisStartsOnSameSide(LogicalAxis::Block, childWM)) {
   4369    flags += AlignJustifyFlag::SameSide;
   4370  }
   4371 
   4372  if (aGridItem.mState[LogicalAxis::Block] &
   4373      GridItemInfo::eLastBaselineSharingGroup) {
   4374    flags += AlignJustifyFlag::LastBaselineSharingGroup;
   4375  }
   4376 
   4377  // Grid's 'align-self' axis is never parallel to the container's inline axis.
   4378  if (aAlignSelf == StyleAlignFlags::LEFT ||
   4379      aAlignSelf == StyleAlignFlags::RIGHT) {
   4380    aAlignSelf = StyleAlignFlags::START;
   4381  }
   4382  if (MOZ_LIKELY(aAlignSelf == StyleAlignFlags::NORMAL)) {
   4383    aAlignSelf = StyleAlignFlags::STRETCH;
   4384  }
   4385 
   4386  nscoord baselineAdjust = 0;
   4387  if (aAlignSelf == StyleAlignFlags::BASELINE ||
   4388      aAlignSelf == StyleAlignFlags::LAST_BASELINE) {
   4389    aAlignSelf = aGridItem.GetSelfBaseline(aAlignSelf, LogicalAxis::Block,
   4390                                           &baselineAdjust);
   4391  }
   4392 
   4393  const auto bAxisInChildWM = aCBWM.ConvertAxisTo(LogicalAxis::Block, childWM);
   4394  AlignJustifySelf(aAlignSelf, bAxisInChildWM, flags, baselineAdjust, aCBSize,
   4395                   aRI, aSize, aPos);
   4396 }
   4397 
   4398 static void JustifySelf(const nsGridContainerFrame::GridItemInfo& aGridItem,
   4399                        StyleAlignFlags aJustifySelf, nscoord aCBSize,
   4400                        const WritingMode aCBWM, const ReflowInput& aRI,
   4401                        const LogicalSize& aSize, AlignJustifyFlags aFlags,
   4402                        LogicalPoint* aPos) {
   4403  AlignJustifyFlags flags = aFlags;
   4404  if (aJustifySelf & StyleAlignFlags::SAFE) {
   4405    flags += AlignJustifyFlag::OverflowSafe;
   4406  }
   4407  aJustifySelf &= ~StyleAlignFlags::FLAG_BITS;
   4408 
   4409  WritingMode childWM = aRI.GetWritingMode();
   4410  if (aCBWM.ParallelAxisStartsOnSameSide(LogicalAxis::Inline, childWM)) {
   4411    flags += AlignJustifyFlag::SameSide;
   4412  }
   4413 
   4414  if (aGridItem.mState[LogicalAxis::Inline] &
   4415      GridItemInfo::eLastBaselineSharingGroup) {
   4416    flags += AlignJustifyFlag::LastBaselineSharingGroup;
   4417  }
   4418 
   4419  if (MOZ_LIKELY(aJustifySelf == StyleAlignFlags::NORMAL)) {
   4420    aJustifySelf = StyleAlignFlags::STRETCH;
   4421  }
   4422 
   4423  nscoord baselineAdjust = 0;
   4424  // Grid's 'justify-self' axis is always parallel to the container's inline
   4425  // axis, so justify-self:left|right always applies.
   4426  if (aJustifySelf == StyleAlignFlags::LEFT) {
   4427    aJustifySelf =
   4428        aCBWM.IsBidiLTR() ? StyleAlignFlags::START : StyleAlignFlags::END;
   4429  } else if (aJustifySelf == StyleAlignFlags::RIGHT) {
   4430    aJustifySelf =
   4431        aCBWM.IsBidiLTR() ? StyleAlignFlags::END : StyleAlignFlags::START;
   4432  } else if (aJustifySelf == StyleAlignFlags::BASELINE ||
   4433             aJustifySelf == StyleAlignFlags::LAST_BASELINE) {
   4434    aJustifySelf = aGridItem.GetSelfBaseline(aJustifySelf, LogicalAxis::Inline,
   4435                                             &baselineAdjust);
   4436  }
   4437 
   4438  const auto iAxisInChildWM = aCBWM.ConvertAxisTo(LogicalAxis::Inline, childWM);
   4439  AlignJustifySelf(aJustifySelf, iAxisInChildWM, flags, baselineAdjust, aCBSize,
   4440                   aRI, aSize, aPos);
   4441 }
   4442 
   4443 static StyleAlignFlags GetAlignJustifyValue(StyleAlignFlags aAlignment,
   4444                                            const WritingMode aWM,
   4445                                            const bool aIsAlign,
   4446                                            bool* aOverflowSafe) {
   4447  *aOverflowSafe = bool(aAlignment & StyleAlignFlags::SAFE);
   4448  aAlignment &= ~StyleAlignFlags::FLAG_BITS;
   4449 
   4450  // Map some alignment values to 'start' / 'end'.
   4451  if (aAlignment == StyleAlignFlags::LEFT ||
   4452      aAlignment == StyleAlignFlags::RIGHT) {
   4453    if (aIsAlign) {
   4454      // Grid's 'align-content' axis is never parallel to the inline axis.
   4455      return StyleAlignFlags::START;
   4456    }
   4457    bool isStart = aWM.IsBidiLTR() == (aAlignment == StyleAlignFlags::LEFT);
   4458    return isStart ? StyleAlignFlags::START : StyleAlignFlags::END;
   4459  }
   4460  if (aAlignment == StyleAlignFlags::FLEX_START) {
   4461    return StyleAlignFlags::START;  // same as 'start' for Grid
   4462  }
   4463  if (aAlignment == StyleAlignFlags::FLEX_END) {
   4464    return StyleAlignFlags::END;  // same as 'end' for Grid
   4465  }
   4466  return aAlignment;
   4467 }
   4468 
   4469 static Maybe<StyleAlignFlags> GetAlignJustifyDistributionFallback(
   4470    const StyleContentDistribution& aDistribution, bool* aOverflowSafe) {
   4471  // See "4.3. Distributed Alignment" for the default fallback alignment values:
   4472  // https://drafts.csswg.org/css-align-3/#distribution-values
   4473  //
   4474  // TODO: Extend this function to handle explicitly specified fallback
   4475  // alignment once the CSS Alignment Module introduces that syntax:
   4476  // https://github.com/w3c/csswg-drafts/issues/1002.
   4477  if (aDistribution.primary == StyleAlignFlags::SPACE_BETWEEN) {
   4478    *aOverflowSafe = true;
   4479    return Some(StyleAlignFlags::START);
   4480  }
   4481  if (aDistribution.primary == StyleAlignFlags::SPACE_AROUND ||
   4482      aDistribution.primary == StyleAlignFlags::SPACE_EVENLY) {
   4483    *aOverflowSafe = true;
   4484    return Some(StyleAlignFlags::CENTER);
   4485  }
   4486  if (aDistribution.primary == StyleAlignFlags::STRETCH) {
   4487    *aOverflowSafe = false;
   4488    return Some(StyleAlignFlags::START);
   4489  }
   4490  return Nothing();
   4491 }
   4492 
   4493 //----------------------------------------------------------------------
   4494 
   4495 // Frame class boilerplate
   4496 // =======================
   4497 
   4498 NS_QUERYFRAME_HEAD(nsGridContainerFrame)
   4499  NS_QUERYFRAME_ENTRY(nsGridContainerFrame)
   4500 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
   4501 
   4502 NS_IMPL_FRAMEARENA_HELPERS(nsGridContainerFrame)
   4503 
   4504 nsContainerFrame* NS_NewGridContainerFrame(PresShell* aPresShell,
   4505                                           ComputedStyle* aStyle) {
   4506  return new (aPresShell)
   4507      nsGridContainerFrame(aStyle, aPresShell->GetPresContext());
   4508 }
   4509 
   4510 //----------------------------------------------------------------------
   4511 
   4512 // nsGridContainerFrame Method Implementations
   4513 // ===========================================
   4514 
   4515 /*static*/ const nsRect& nsGridContainerFrame::GridItemCB(nsIFrame* aChild) {
   4516  MOZ_ASSERT(aChild->IsAbsolutelyPositioned());
   4517  nsRect* cb = aChild->GetProperty(GridItemContainingBlockRect());
   4518  MOZ_ASSERT(cb,
   4519             "this method must only be called on grid items, and the grid "
   4520             "container should've reflowed this item by now and set up cb");
   4521  return *cb;
   4522 }
   4523 
   4524 void nsGridContainerFrame::AddImplicitNamedAreasInternal(
   4525    LineNameList& aNameList,
   4526    nsGridContainerFrame::ImplicitNamedAreas*& aAreas) {
   4527  for (const auto& nameIdent : aNameList.AsSpan()) {
   4528    nsAtom* name = nameIdent.AsAtom();
   4529    uint32_t indexOfSuffix;
   4530    if (Grid::IsNameWithStartSuffix(name, &indexOfSuffix) ||
   4531        Grid::IsNameWithEndSuffix(name, &indexOfSuffix)) {
   4532      // Extract the name that was found earlier.
   4533      nsDependentSubstring areaName(nsDependentAtomString(name), 0,
   4534                                    indexOfSuffix);
   4535 
   4536      // Lazily create the ImplicitNamedAreas.
   4537      if (!aAreas) {
   4538        aAreas = new nsGridContainerFrame::ImplicitNamedAreas;
   4539        SetProperty(nsGridContainerFrame::ImplicitNamedAreasProperty(), aAreas);
   4540      }
   4541 
   4542      RefPtr<nsAtom> name = NS_Atomize(areaName);
   4543      auto addPtr = aAreas->lookupForAdd(name);
   4544      if (!addPtr) {
   4545        if (!aAreas->add(addPtr, name,
   4546                         nsGridContainerFrame::NamedArea{
   4547                             StyleAtom(do_AddRef(name)), {0, 0}, {0, 0}})) {
   4548          MOZ_CRASH("OOM while adding grid name lists");
   4549        }
   4550      }
   4551    }
   4552  }
   4553 }
   4554 
   4555 void nsGridContainerFrame::AddImplicitNamedAreas(
   4556    Span<LineNameList> aLineNameLists) {
   4557  // https://drafts.csswg.org/css-grid-2/#implicit-named-areas
   4558  // Note: recording these names for fast lookup later is just an optimization.
   4559  ImplicitNamedAreas* areas = GetImplicitNamedAreas();
   4560  const uint32_t len = std::min(aLineNameLists.Length(), size_t(kMaxLine));
   4561  for (uint32_t i = 0; i < len; ++i) {
   4562    AddImplicitNamedAreasInternal(aLineNameLists[i], areas);
   4563  }
   4564 }
   4565 
   4566 void nsGridContainerFrame::AddImplicitNamedAreas(
   4567    Span<StyleLineNameListValue> aLineNameList) {
   4568  // https://drafts.csswg.org/css-grid-2/#implicit-named-areas
   4569  // Note: recording these names for fast lookup later is just an optimization.
   4570  uint32_t count = 0;
   4571  ImplicitNamedAreas* areas = GetImplicitNamedAreas();
   4572  for (const auto& nameList : aLineNameList) {
   4573    if (nameList.IsRepeat()) {
   4574      for (const auto& repeatNameList :
   4575           nameList.AsRepeat().line_names.AsSpan()) {
   4576        AddImplicitNamedAreasInternal(repeatNameList, areas);
   4577        ++count;
   4578      }
   4579    } else {
   4580      MOZ_ASSERT(nameList.IsLineNames());
   4581      AddImplicitNamedAreasInternal(nameList.AsLineNames(), areas);
   4582      ++count;
   4583    }
   4584 
   4585    if (count >= size_t(kMaxLine)) {
   4586      break;
   4587    }
   4588  }
   4589 }
   4590 
   4591 void nsGridContainerFrame::InitImplicitNamedAreas(
   4592    const nsStylePosition* aStyle) {
   4593  ImplicitNamedAreas* areas = GetImplicitNamedAreas();
   4594  if (areas) {
   4595    // Clear it, but reuse the hashtable itself for now.  We'll remove it
   4596    // below if it isn't needed anymore.
   4597    areas->clear();
   4598  }
   4599  auto Add = [&](const GridTemplate& aTemplate, bool aIsSubgrid) {
   4600    AddImplicitNamedAreas(aTemplate.LineNameLists(aIsSubgrid));
   4601    for (auto& value : aTemplate.TrackListValues()) {
   4602      if (value.IsTrackRepeat()) {
   4603        AddImplicitNamedAreas(value.AsTrackRepeat().line_names.AsSpan());
   4604      }
   4605    }
   4606 
   4607    if (aIsSubgrid && aTemplate.IsSubgrid()) {
   4608      // For subgrid, |aTemplate.LineNameLists(aIsSubgrid)| returns an empty
   4609      // list so we have to manually add each item.
   4610      AddImplicitNamedAreas(aTemplate.AsSubgrid()->line_names.AsSpan());
   4611    }
   4612  };
   4613  Add(aStyle->mGridTemplateColumns, IsColSubgrid());
   4614  Add(aStyle->mGridTemplateRows, IsRowSubgrid());
   4615  if (areas && areas->count() == 0) {
   4616    RemoveProperty(ImplicitNamedAreasProperty());
   4617  }
   4618 }
   4619 
   4620 int32_t nsGridContainerFrame::Grid::ResolveLine(
   4621    const StyleGridLine& aLine, int32_t aNth, uint32_t aFromIndex,
   4622    const LineNameMap& aNameMap, LogicalSide aSide, uint32_t aExplicitGridEnd,
   4623    const nsStylePosition* aStyle) {
   4624  MOZ_ASSERT(!aLine.IsAuto());
   4625  int32_t line = 0;
   4626  if (aLine.LineName()->IsEmpty()) {
   4627    MOZ_ASSERT(aNth != 0, "css-grid 9.2: <integer> must not be zero.");
   4628    line = int32_t(aFromIndex) + aNth;
   4629  } else {
   4630    if (aNth == 0) {
   4631      // <integer> was omitted; treat it as 1.
   4632      aNth = 1;
   4633    }
   4634    bool isNameOnly = !aLine.is_span && aLine.line_num == 0;
   4635    if (isNameOnly) {
   4636      AutoTArray<uint32_t, 16> implicitLines;
   4637      aNameMap.FindNamedAreas(aLine.ident.AsAtom(), aSide, implicitLines);
   4638      if (!implicitLines.IsEmpty() ||
   4639          aNameMap.HasImplicitNamedArea(aLine.LineName())) {
   4640        // aName is a named area - look for explicit lines named
   4641        // <name>-start/-end depending on which side we're resolving.
   4642        // https://drafts.csswg.org/css-grid-2/#grid-placement-slot
   4643        nsAutoString lineName(nsDependentAtomString(aLine.LineName()));
   4644        if (IsStart(aSide)) {
   4645          lineName.AppendLiteral("-start");
   4646        } else {
   4647          lineName.AppendLiteral("-end");
   4648        }
   4649        RefPtr<nsAtom> name = NS_Atomize(lineName);
   4650        line = aNameMap.FindNamedLine(name, &aNth, aFromIndex, implicitLines);
   4651      }
   4652    }
   4653 
   4654    if (line == 0) {
   4655      // If LineName() ends in -start/-end, try the prefix as a named area.
   4656      AutoTArray<uint32_t, 16> implicitLines;
   4657      uint32_t index;
   4658      bool useStart = IsNameWithStartSuffix(aLine.LineName(), &index);
   4659      if (useStart || IsNameWithEndSuffix(aLine.LineName(), &index)) {
   4660        auto side = MakeLogicalSide(
   4661            GetAxis(aSide), useStart ? LogicalEdge::Start : LogicalEdge::End);
   4662        RefPtr<nsAtom> name = NS_Atomize(nsDependentSubstring(
   4663            nsDependentAtomString(aLine.LineName()), 0, index));
   4664        aNameMap.FindNamedAreas(name, side, implicitLines);
   4665      }
   4666      line = aNameMap.FindNamedLine(aLine.LineName(), &aNth, aFromIndex,
   4667                                    implicitLines);
   4668    }
   4669 
   4670    if (line == 0) {
   4671      MOZ_ASSERT(aNth != 0, "we found all N named lines but 'line' is zero!");
   4672      int32_t edgeLine;
   4673      if (aLine.is_span) {
   4674        // https://drafts.csswg.org/css-grid-2/#grid-placement-span-int
   4675        // 'span <custom-ident> N'
   4676        edgeLine = IsStart(aSide) ? 1 : aExplicitGridEnd;
   4677      } else {
   4678        // https://drafts.csswg.org/css-grid-2/#grid-placement-int
   4679        // '<custom-ident> N'
   4680        edgeLine = aNth < 0 ? 1 : aExplicitGridEnd;
   4681      }
   4682      // "If not enough lines with that name exist, all lines in the implicit
   4683      // grid are assumed to have that name..."
   4684      line = edgeLine + aNth;
   4685    }
   4686  }
   4687  // Note: at this point, 'line' might be outside of aNameMap's allowed range,
   4688  // [mClampMinLin, mClampMaxLine]. This is fine; we'll clamp once we've
   4689  // resolved *both* the start and end line -- in particular, we clamp in
   4690  // ResolveLineRange(). If we clamped here, it'd be premature -- if one line
   4691  // is definite and the other is specified as a span to some named line
   4692  // (i.e. we need to perform a name-search that starts from the definite
   4693  // line), then it matters whether we clamp the definite line before or after
   4694  // that search. See https://bugzilla.mozilla.org/show_bug.cgi?id=1800566#c6
   4695  // for more.
   4696  return line;
   4697 }
   4698 
   4699 nsGridContainerFrame::Grid::LinePair
   4700 nsGridContainerFrame::Grid::ResolveLineRangeHelper(
   4701    const StyleGridLine& aStart, const StyleGridLine& aEnd,
   4702    const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
   4703    const nsStylePosition* aStyle) {
   4704  MOZ_ASSERT(int32_t(kAutoLine) > kMaxLine);
   4705 
   4706  if (aStart.is_span) {
   4707    if (aEnd.is_span || aEnd.IsAuto()) {
   4708      // https://drafts.csswg.org/css-grid-2/#grid-placement-errors
   4709      if (aStart.LineName()->IsEmpty()) {
   4710        // span <integer> / span *
   4711        // span <integer> / auto
   4712        return LinePair(kAutoLine, aStart.line_num);
   4713      }
   4714      // span <custom-ident> / span *
   4715      // span <custom-ident> / auto
   4716      return LinePair(kAutoLine, 1);  // XXX subgrid explicit size instead of 1?
   4717    }
   4718 
   4719    uint32_t from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0;
   4720    auto end = ResolveLine(aEnd, aEnd.line_num, from, aNameMap,
   4721                           MakeLogicalSide(aAxis, LogicalEdge::End),
   4722                           aExplicitGridEnd, aStyle);
   4723    int32_t span = aStart.line_num == 0 ? 1 : aStart.line_num;
   4724    if (end <= 1) {
   4725      // The end is at or before the first explicit line, thus all lines before
   4726      // it match <custom-ident> since they're implicit.
   4727      int32_t start = std::max(end - span, aNameMap.mClampMinLine);
   4728      return LinePair(start, end);
   4729    }
   4730    auto start = ResolveLine(aStart, -span, end, aNameMap,
   4731                             MakeLogicalSide(aAxis, LogicalEdge::Start),
   4732                             aExplicitGridEnd, aStyle);
   4733    return LinePair(start, end);
   4734  }
   4735 
   4736  int32_t start = kAutoLine;
   4737  if (aStart.IsAuto()) {
   4738    if (aEnd.IsAuto()) {
   4739      // auto / auto
   4740      return LinePair(start, 1);  // XXX subgrid explicit size instead of 1?
   4741    }
   4742    if (aEnd.is_span) {
   4743      if (aEnd.LineName()->IsEmpty()) {
   4744        // auto / span <integer>
   4745        MOZ_ASSERT(aEnd.line_num != 0);
   4746        return LinePair(start, aEnd.line_num);
   4747      }
   4748      // https://drafts.csswg.org/css-grid-2/#grid-placement-errors
   4749      // auto / span <custom-ident>
   4750      return LinePair(start, 1);  // XXX subgrid explicit size instead of 1?
   4751    }
   4752  } else {
   4753    uint32_t from = aStart.line_num < 0 ? aExplicitGridEnd + 1 : 0;
   4754    start = ResolveLine(aStart, aStart.line_num, from, aNameMap,
   4755                        MakeLogicalSide(aAxis, LogicalEdge::Start),
   4756                        aExplicitGridEnd, aStyle);
   4757    if (aEnd.IsAuto()) {
   4758      // A "definite line / auto" should resolve the auto to 'span 1'.
   4759      // The error handling in ResolveLineRange will make that happen and also
   4760      // clamp the end line correctly if we return "start / start".
   4761      return LinePair(start, start);
   4762    }
   4763  }
   4764 
   4765  uint32_t from;
   4766  int32_t nth = aEnd.line_num == 0 ? 1 : aEnd.line_num;
   4767  if (aEnd.is_span) {
   4768    if (MOZ_UNLIKELY(start < 0)) {
   4769      if (aEnd.LineName()->IsEmpty()) {
   4770        return LinePair(start, start + nth);
   4771      }
   4772      from = 0;
   4773    } else {
   4774      if (start >= int32_t(aExplicitGridEnd)) {
   4775        // The start is at or after the last explicit line, thus all lines
   4776        // after it match <custom-ident> since they're implicit.
   4777        return LinePair(start, std::min(start + nth, aNameMap.mClampMaxLine));
   4778      }
   4779      from = start;
   4780    }
   4781  } else {
   4782    from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0;
   4783  }
   4784  auto end = ResolveLine(aEnd, nth, from, aNameMap,
   4785                         MakeLogicalSide(aAxis, LogicalEdge::End),
   4786                         aExplicitGridEnd, aStyle);
   4787  if (start == int32_t(kAutoLine)) {
   4788    // auto / definite line
   4789    start = std::max(aNameMap.mClampMinLine, end - 1);
   4790  }
   4791  return LinePair(start, end);
   4792 }
   4793 
   4794 nsGridContainerFrame::LineRange nsGridContainerFrame::Grid::ResolveLineRange(
   4795    const StyleGridLine& aStart, const StyleGridLine& aEnd,
   4796    const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
   4797    const nsStylePosition* aStyle) {
   4798  LinePair r = ResolveLineRangeHelper(aStart, aEnd, aNameMap, aAxis,
   4799                                      aExplicitGridEnd, aStyle);
   4800  MOZ_ASSERT(r.second != int32_t(kAutoLine));
   4801 
   4802  if (r.first == int32_t(kAutoLine)) {
   4803    // r.second is a span, clamp it to aNameMap.mClampMaxLine - 1 so that
   4804    // the returned range has a HypotheticalEnd <= aNameMap.mClampMaxLine.
   4805    // https://drafts.csswg.org/css-grid-2/#overlarge-grids
   4806    r.second = std::min(r.second, aNameMap.mClampMaxLine - 1);
   4807  } else {
   4808    // Clamp the lines to be within our limits, per
   4809    // https://drafts.csswg.org/css-grid-2/#overlarge-grids
   4810    // Note that our limits here might come from the [kMinLine, kMaxLine]
   4811    // extremes; or, they might just be the bounds of a subgrid's explicit
   4812    // grid. We use the same clamping approach either way, per
   4813    // https://drafts.csswg.org/css-grid-2/#subgrid-implicit ("using the same
   4814    // procedure as for clamping placement in an overly-large grid").
   4815    //
   4816    // Note that these two clamped assignments might collapse our range to
   4817    // have both edges pointing at the same line (spanning 0 tracks); this
   4818    // might happen here if e.g. r.first were mClampMaxLine, and r.second gets
   4819    // clamped from some higher number down to mClampMaxLine. We'll handle this
   4820    // by shifting the inner line (r.first in this hypothetical) inwards by 1,
   4821    // in the #grid-placement-errors section; that achieves the outcome of
   4822    // the #overlarge-grids clamping spec text that says "its span must be
   4823    // truncated to 1" when clamping an item that was completely outside the
   4824    // limits.
   4825    r.first =
   4826        std::clamp(r.first, aNameMap.mClampMinLine, aNameMap.mClampMaxLine);
   4827    r.second =
   4828        std::clamp(r.second, aNameMap.mClampMinLine, aNameMap.mClampMaxLine);
   4829 
   4830    // Handle grid placement errors.
   4831    // https://drafts.csswg.org/css-grid-2/#grid-placement-errors
   4832    if (r.first > r.second) {
   4833      std::swap(r.first, r.second);
   4834    } else if (r.first == r.second) {
   4835      // (This is #grid-placement-errors fixup, but it's also where we ensure
   4836      // that any #overlarge-grids fixup that we did above will end up
   4837      // truncating the range to a span of 1 rather than 0 -- i.e. sliding
   4838      // inwards if needed.)
   4839      if (MOZ_UNLIKELY(r.first == aNameMap.mClampMaxLine)) {
   4840        r.first = aNameMap.mClampMaxLine - 1;
   4841      }
   4842      r.second = r.first + 1;
   4843    }
   4844  }
   4845  return LineRange(r.first, r.second);
   4846 }
   4847 
   4848 nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceDefinite(
   4849    nsIFrame* aChild, const LineNameMap& aColLineNameMap,
   4850    const LineNameMap& aRowLineNameMap, const nsStylePosition* aStyle) {
   4851  const nsStylePosition* itemStyle = aChild->StylePosition();
   4852  return GridArea(
   4853      ResolveLineRange(itemStyle->mGridColumnStart, itemStyle->mGridColumnEnd,
   4854                       aColLineNameMap, LogicalAxis::Inline,
   4855                       mExplicitGridColEnd, aStyle),
   4856      ResolveLineRange(itemStyle->mGridRowStart, itemStyle->mGridRowEnd,
   4857                       aRowLineNameMap, LogicalAxis::Block, mExplicitGridRowEnd,
   4858                       aStyle));
   4859 }
   4860 
   4861 nsGridContainerFrame::LineRange
   4862 nsGridContainerFrame::Grid::ResolveAbsPosLineRange(
   4863    const StyleGridLine& aStart, const StyleGridLine& aEnd,
   4864    const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
   4865    int32_t aGridStart, int32_t aGridEnd, const nsStylePosition* aStyle) {
   4866  if (aStart.IsAuto()) {
   4867    if (aEnd.IsAuto()) {
   4868      return LineRange(kAutoLine, kAutoLine);
   4869    }
   4870    uint32_t from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0;
   4871    int32_t end = ResolveLine(aEnd, aEnd.line_num, from, aNameMap,
   4872                              MakeLogicalSide(aAxis, LogicalEdge::End),
   4873                              aExplicitGridEnd, aStyle);
   4874    if (aEnd.is_span) {
   4875      ++end;
   4876    }
   4877    // A line outside the existing grid is treated as 'auto' for abs.pos (10.1).
   4878    end = AutoIfOutside(end, aGridStart, aGridEnd);
   4879    return LineRange(kAutoLine, end);
   4880  }
   4881 
   4882  if (aEnd.IsAuto()) {
   4883    uint32_t from = aStart.line_num < 0 ? aExplicitGridEnd + 1 : 0;
   4884    int32_t start = ResolveLine(aStart, aStart.line_num, from, aNameMap,
   4885                                MakeLogicalSide(aAxis, LogicalEdge::Start),
   4886                                aExplicitGridEnd, aStyle);
   4887    if (aStart.is_span) {
   4888      start = std::max(aGridEnd - start, aGridStart);
   4889    }
   4890    start = AutoIfOutside(start, aGridStart, aGridEnd);
   4891    return LineRange(start, kAutoLine);
   4892  }
   4893 
   4894  LineRange r =
   4895      ResolveLineRange(aStart, aEnd, aNameMap, aAxis, aExplicitGridEnd, aStyle);
   4896  if (r.IsAuto()) {
   4897    MOZ_ASSERT(aStart.is_span && aEnd.is_span,
   4898               "span / span is the only case "
   4899               "leading to IsAuto here -- we dealt with the other cases above");
   4900    // The second span was ignored per 9.2.1.  For abs.pos., 10.1 says that this
   4901    // case should result in "auto / auto" unlike normal flow grid items.
   4902    return LineRange(kAutoLine, kAutoLine);
   4903  }
   4904 
   4905  return LineRange(AutoIfOutside(r.mUntranslatedStart, aGridStart, aGridEnd),
   4906                   AutoIfOutside(r.mUntranslatedEnd, aGridStart, aGridEnd));
   4907 }
   4908 
   4909 nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceAbsPos(
   4910    nsIFrame* aChild, const LineNameMap& aColLineNameMap,
   4911    const LineNameMap& aRowLineNameMap, const nsStylePosition* aStyle) {
   4912  const nsStylePosition* itemStyle = aChild->StylePosition();
   4913  int32_t gridColStart = 1 - mExplicitGridOffsetCol;
   4914  int32_t gridRowStart = 1 - mExplicitGridOffsetRow;
   4915  return GridArea(ResolveAbsPosLineRange(
   4916                      itemStyle->mGridColumnStart, itemStyle->mGridColumnEnd,
   4917                      aColLineNameMap, LogicalAxis::Inline, mExplicitGridColEnd,
   4918                      gridColStart, mGridColEnd, aStyle),
   4919                  ResolveAbsPosLineRange(
   4920                      itemStyle->mGridRowStart, itemStyle->mGridRowEnd,
   4921                      aRowLineNameMap, LogicalAxis::Block, mExplicitGridRowEnd,
   4922                      gridRowStart, mGridRowEnd, aStyle));
   4923 }
   4924 
   4925 uint32_t nsGridContainerFrame::Grid::FindAutoCol(uint32_t aStartCol,
   4926                                                 uint32_t aLockedRow,
   4927                                                 const GridArea* aArea) const {
   4928  const uint32_t extent = aArea->mCols.Extent();
   4929  const uint32_t iStart = aLockedRow;
   4930  const uint32_t iEnd = iStart + aArea->mRows.Extent();
   4931  uint32_t candidate = aStartCol;
   4932  for (uint32_t i = iStart; i < iEnd;) {
   4933    if (i >= mCellMap.mCells.Length()) {
   4934      break;
   4935    }
   4936    const nsTArray<CellMap::Cell>& cellsInRow = mCellMap.mCells[i];
   4937    const uint32_t len = cellsInRow.Length();
   4938    const uint32_t lastCandidate = candidate;
   4939    // Find the first gap in the current row that's at least 'extent' wide.
   4940    // ('gap' tracks how wide the current column gap is.)
   4941    for (uint32_t j = candidate, gap = 0; j < len && gap < extent; ++j) {
   4942      if (!cellsInRow[j].mIsOccupied) {
   4943        ++gap;
   4944        continue;
   4945      }
   4946      candidate = j + 1;
   4947      gap = 0;
   4948    }
   4949    if (lastCandidate < candidate && i != iStart) {
   4950      // Couldn't fit 'extent' tracks at 'lastCandidate' here so we must
   4951      // restart from the beginning with the new 'candidate'.
   4952      i = iStart;
   4953    } else {
   4954      ++i;
   4955    }
   4956  }
   4957  return candidate;
   4958 }
   4959 
   4960 void nsGridContainerFrame::Grid::PlaceAutoCol(uint32_t aStartCol,
   4961                                              GridArea* aArea,
   4962                                              uint32_t aClampMaxColLine) const {
   4963  MOZ_ASSERT(aArea->mRows.IsDefinite() && aArea->mCols.IsAuto());
   4964  uint32_t col = FindAutoCol(aStartCol, aArea->mRows.mStart, aArea);
   4965  aArea->mCols.ResolveAutoPosition(col, aClampMaxColLine);
   4966  MOZ_ASSERT(aArea->IsDefinite());
   4967 }
   4968 
   4969 uint32_t nsGridContainerFrame::Grid::FindAutoRow(uint32_t aLockedCol,
   4970                                                 uint32_t aStartRow,
   4971                                                 const GridArea* aArea) const {
   4972  const uint32_t extent = aArea->mRows.Extent();
   4973  const uint32_t jStart = aLockedCol;
   4974  const uint32_t jEnd = jStart + aArea->mCols.Extent();
   4975  const uint32_t iEnd = mCellMap.mCells.Length();
   4976  uint32_t candidate = aStartRow;
   4977  // Find the first gap in the rows that's at least 'extent' tall.
   4978  // ('gap' tracks how tall the current row gap is.)
   4979  for (uint32_t i = candidate, gap = 0; i < iEnd && gap < extent; ++i) {
   4980    ++gap;  // tentative, but we may reset it below if a column is occupied
   4981    const nsTArray<CellMap::Cell>& cellsInRow = mCellMap.mCells[i];
   4982    const uint32_t clampedJEnd = std::min<uint32_t>(jEnd, cellsInRow.Length());
   4983    // Check if the current row is unoccupied from jStart to jEnd.
   4984    for (uint32_t j = jStart; j < clampedJEnd; ++j) {
   4985      if (cellsInRow[j].mIsOccupied) {
   4986        // Couldn't fit 'extent' rows at 'candidate' here; we hit something
   4987        // at row 'i'.  So, try the row after 'i' as our next candidate.
   4988        candidate = i + 1;
   4989        gap = 0;
   4990        break;
   4991      }
   4992    }
   4993  }
   4994  return candidate;
   4995 }
   4996 
   4997 void nsGridContainerFrame::Grid::PlaceAutoRow(uint32_t aStartRow,
   4998                                              GridArea* aArea,
   4999                                              uint32_t aClampMaxRowLine) const {
   5000  MOZ_ASSERT(aArea->mCols.IsDefinite() && aArea->mRows.IsAuto());
   5001  uint32_t row = FindAutoRow(aArea->mCols.mStart, aStartRow, aArea);
   5002  aArea->mRows.ResolveAutoPosition(row, aClampMaxRowLine);
   5003  MOZ_ASSERT(aArea->IsDefinite());
   5004 }
   5005 
   5006 void nsGridContainerFrame::Grid::PlaceAutoAutoInRowOrder(
   5007    uint32_t aStartCol, uint32_t aStartRow, GridArea* aArea,
   5008    uint32_t aClampMaxColLine, uint32_t aClampMaxRowLine) const {
   5009  MOZ_ASSERT(aArea->mCols.IsAuto() && aArea->mRows.IsAuto());
   5010  const uint32_t colExtent = aArea->mCols.Extent();
   5011  const uint32_t gridRowEnd = mGridRowEnd;
   5012  const uint32_t gridColEnd = mGridColEnd;
   5013  uint32_t col = aStartCol;
   5014  uint32_t row = aStartRow;
   5015  for (; row < gridRowEnd; ++row) {
   5016    col = FindAutoCol(col, row, aArea);
   5017    if (col + colExtent <= gridColEnd) {
   5018      break;
   5019    }
   5020    col = 0;
   5021  }
   5022  MOZ_ASSERT(row < gridRowEnd || col == 0,
   5023             "expected column 0 for placing in a new row");
   5024  aArea->mCols.ResolveAutoPosition(col, aClampMaxColLine);
   5025  aArea->mRows.ResolveAutoPosition(row, aClampMaxRowLine);
   5026  MOZ_ASSERT(aArea->IsDefinite());
   5027 }
   5028 
   5029 void nsGridContainerFrame::Grid::PlaceAutoAutoInColOrder(
   5030    uint32_t aStartCol, uint32_t aStartRow, GridArea* aArea,
   5031    uint32_t aClampMaxColLine, uint32_t aClampMaxRowLine) const {
   5032  MOZ_ASSERT(aArea->mCols.IsAuto() && aArea->mRows.IsAuto());
   5033  const uint32_t rowExtent = aArea->mRows.Extent();
   5034  const uint32_t gridRowEnd = mGridRowEnd;
   5035  const uint32_t gridColEnd = mGridColEnd;
   5036  uint32_t col = aStartCol;
   5037  uint32_t row = aStartRow;
   5038  for (; col < gridColEnd; ++col) {
   5039    row = FindAutoRow(col, row, aArea);
   5040    if (row + rowExtent <= gridRowEnd) {
   5041      break;
   5042    }
   5043    row = 0;
   5044  }
   5045  MOZ_ASSERT(col < gridColEnd || row == 0,
   5046             "expected row 0 for placing in a new column");
   5047  aArea->mCols.ResolveAutoPosition(col, aClampMaxColLine);
   5048  aArea->mRows.ResolveAutoPosition(row, aClampMaxRowLine);
   5049  MOZ_ASSERT(aArea->IsDefinite());
   5050 }
   5051 
   5052 template <typename IsEmptyFuncT>
   5053 Maybe<nsTArray<uint32_t>>
   5054 nsGridContainerFrame::Grid::CalculateAdjustForAutoFitElements(
   5055    uint32_t* const aOutNumEmptyLines, TrackSizingFunctions& aSizingFunctions,
   5056    uint32_t aNumGridLines, IsEmptyFuncT aIsEmptyFunc) {
   5057  Maybe<nsTArray<uint32_t>> trackAdjust;
   5058  uint32_t& numEmptyLines = *aOutNumEmptyLines;
   5059  numEmptyLines = 0;
   5060  if (aSizingFunctions.NumRepeatTracks() > 0) {
   5061    MOZ_ASSERT(aSizingFunctions.mHasRepeatAuto);
   5062    // Since this loop is concerned with just the repeat tracks, we
   5063    // iterate from 0..NumRepeatTracks() which is the natural range of
   5064    // mRemoveRepeatTracks. This means we have to add
   5065    // (mExplicitGridOffset + mRepeatAutoStart) to get a zero-based
   5066    // index for arrays like mCellMap/aIsEmptyFunc and trackAdjust. We'll then
   5067    // fill out the trackAdjust array for all the remaining lines.
   5068    const uint32_t repeatStart = (aSizingFunctions.mExplicitGridOffset +
   5069                                  aSizingFunctions.mRepeatAutoStart);
   5070    const uint32_t numRepeats = aSizingFunctions.NumRepeatTracks();
   5071    for (uint32_t i = 0; i < numRepeats; ++i) {
   5072      if (numEmptyLines) {
   5073        MOZ_ASSERT(trackAdjust.isSome());
   5074        (*trackAdjust)[repeatStart + i] = numEmptyLines;
   5075      }
   5076      if (aIsEmptyFunc(repeatStart + i)) {
   5077        ++numEmptyLines;
   5078        if (trackAdjust.isNothing()) {
   5079          trackAdjust.emplace(aNumGridLines);
   5080          trackAdjust->SetLength(aNumGridLines);
   5081          PodZero(trackAdjust->Elements(), trackAdjust->Length());
   5082        }
   5083 
   5084        aSizingFunctions.mRemovedRepeatTracks[i] = true;
   5085      }
   5086    }
   5087    // Fill out the trackAdjust array for all the tracks after the repeats.
   5088    if (numEmptyLines) {
   5089      for (uint32_t line = repeatStart + numRepeats; line < aNumGridLines;
   5090           ++line) {
   5091        (*trackAdjust)[line] = numEmptyLines;
   5092      }
   5093    }
   5094  }
   5095 
   5096  return trackAdjust;
   5097 }
   5098 
   5099 void nsGridContainerFrame::Grid::SubgridPlaceGridItems(
   5100    GridReflowInput& aParentGridRI, Grid* aParentGrid,
   5101    const GridItemInfo& aGridItem) {
   5102  MOZ_ASSERT(aGridItem.mArea.IsDefinite() ||
   5103                 aGridItem.mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
   5104             "the subgrid's lines should be resolved by now");
   5105  if (aGridItem.IsSubgrid(LogicalAxis::Inline)) {
   5106    aParentGridRI.mFrame->AddStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM);
   5107  }
   5108  if (aGridItem.IsSubgrid(LogicalAxis::Block)) {
   5109    aParentGridRI.mFrame->AddStateBits(NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
   5110  }
   5111  auto* childGrid = aGridItem.SubgridFrame();
   5112  const auto* pos = childGrid->StylePosition();
   5113  childGrid->NormalizeChildLists();
   5114  GridReflowInput gridRI(childGrid, aParentGridRI.mRenderingContext);
   5115  childGrid->InitImplicitNamedAreas(pos);
   5116 
   5117  const bool isOrthogonal = aParentGridRI.mWM.IsOrthogonalTo(gridRI.mWM);
   5118  // Record the subgrid's GridArea in a frame property.
   5119  auto* subgrid = childGrid->GetProperty(Subgrid::Prop());
   5120  if (!subgrid) {
   5121    subgrid = new Subgrid(aGridItem.mArea, isOrthogonal, aParentGridRI.mWM);
   5122    childGrid->SetProperty(Subgrid::Prop(), subgrid);
   5123  } else {
   5124    subgrid->mArea = aGridItem.mArea;
   5125    subgrid->mIsOrthogonal = isOrthogonal;
   5126    subgrid->mGridItems.Clear();
   5127    subgrid->mAbsPosItems.Clear();
   5128  }
   5129 
   5130  // Abs.pos. subgrids may have kAutoLine in their area.  Map those to the edge
   5131  // line in the parent's grid (zero-based line numbers).
   5132  if (MOZ_UNLIKELY(subgrid->mArea.mCols.mStart == kAutoLine)) {
   5133    subgrid->mArea.mCols.mStart = 0;
   5134  }
   5135  if (MOZ_UNLIKELY(subgrid->mArea.mCols.mEnd == kAutoLine)) {
   5136    subgrid->mArea.mCols.mEnd = aParentGrid->mGridColEnd - 1;
   5137  }
   5138  if (MOZ_UNLIKELY(subgrid->mArea.mRows.mStart == kAutoLine)) {
   5139    subgrid->mArea.mRows.mStart = 0;
   5140  }
   5141  if (MOZ_UNLIKELY(subgrid->mArea.mRows.mEnd == kAutoLine)) {
   5142    subgrid->mArea.mRows.mEnd = aParentGrid->mGridRowEnd - 1;
   5143  }
   5144 
   5145  MOZ_ASSERT((subgrid->mArea.mCols.Extent() > 0 &&
   5146              subgrid->mArea.mRows.Extent() > 0) ||
   5147                 gridRI.mGridItems.IsEmpty(),
   5148             "subgrid needs at least one track for its items");
   5149 
   5150  // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
   5151  // https://drafts.csswg.org/css-grid-2/#auto-repeat
   5152  // They're only used for auto-repeat in a non-subgridded axis so we skip
   5153  // computing them otherwise.
   5154  RepeatTrackSizingInput repeatSizing(gridRI.mWM);
   5155  if (!childGrid->IsColSubgrid() && gridRI.mColFunctions.mHasRepeatAuto) {
   5156    // FIXME: Bug 1918794. Figure out if it is fine to pass Nothing() here. It
   5157    // seems we use a different way to calculate the size if the container is a
   5158    // subgrid. Otherwise, we may have to know the area size that this grid item
   5159    // is placed, and pass the area size as the containing block size to this
   5160    // function.
   5161    repeatSizing.InitFromStyle(LogicalAxis::Inline, gridRI.mWM, gridRI.mFrame,
   5162                               gridRI.mFrame->Style(),
   5163                               gridRI.mFrame->GetAspectRatio(), Nothing());
   5164  }
   5165  if (!childGrid->IsRowSubgrid() && gridRI.mRowFunctions.mHasRepeatAuto) {
   5166    // FIXME: Bug 1918794. Same as above.
   5167    repeatSizing.InitFromStyle(LogicalAxis::Block, gridRI.mWM, gridRI.mFrame,
   5168                               gridRI.mFrame->Style(),
   5169                               gridRI.mFrame->GetAspectRatio(), Nothing());
   5170  }
   5171 
   5172  PlaceGridItems(gridRI, repeatSizing);
   5173 
   5174  subgrid->mGridItems = std::move(gridRI.mGridItems);
   5175  subgrid->mAbsPosItems = std::move(gridRI.mAbsPosItems);
   5176  subgrid->mGridColEnd = mGridColEnd;
   5177  subgrid->mGridRowEnd = mGridRowEnd;
   5178 }
   5179 
   5180 void nsGridContainerFrame::Grid::PlaceGridItems(
   5181    GridReflowInput& aGridRI, const RepeatTrackSizingInput& aSizes) {
   5182  MOZ_ASSERT(mCellMap.mCells.IsEmpty(), "unexpected entries in cell map");
   5183 
   5184  mAreas = aGridRI.mFrame->GetImplicitNamedAreas();
   5185 
   5186  if (aGridRI.mFrame->HasSubgridItems() || aGridRI.mFrame->IsSubgrid()) {
   5187    if (auto* uts = aGridRI.mFrame->GetUsedTrackSizes()) {
   5188      uts->mCanResolveLineRangeSize = {false, false};
   5189      uts->mTrackPlans[LogicalAxis::Inline].ClearAndRetainStorage();
   5190      uts->mTrackPlans[LogicalAxis::Block].ClearAndRetainStorage();
   5191    }
   5192  }
   5193 
   5194  // SubgridPlaceGridItems will set these if we find any subgrid items.
   5195  aGridRI.mFrame->RemoveStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM |
   5196                                  NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
   5197 
   5198  // https://drafts.csswg.org/css-grid-2/#grid-definition
   5199  // Initialize the end lines of the Explicit Grid (mExplicitGridCol[Row]End).
   5200  // This is determined by the larger of the number of rows/columns defined
   5201  // by 'grid-template-areas' and the 'grid-template-rows'/'-columns', plus one.
   5202  // Also initialize the Implicit Grid (mGridCol[Row]End) to the same values.
   5203  // Note that this is for a grid with a 1,1 origin.  We'll change that
   5204  // to a 0,0 based grid after placing definite lines.
   5205  const nsStylePosition* const gridStyle = aGridRI.mGridStyle;
   5206  const auto* areas = gridStyle->mGridTemplateAreas.IsNone()
   5207                          ? nullptr
   5208                          : &*gridStyle->mGridTemplateAreas.AsAreas();
   5209  const LineNameMap* parentLineNameMap = nullptr;
   5210  const LineRange* subgridRange = nullptr;
   5211  bool subgridAxisIsSameDirection = true;
   5212  if (!aGridRI.mFrame->IsColSubgrid()) {
   5213    aGridRI.mColFunctions.InitRepeatTracks(
   5214        gridStyle->mColumnGap, aSizes.mMin.ISize(aGridRI.mWM),
   5215        aSizes.mSize.ISize(aGridRI.mWM), aSizes.mMax.ISize(aGridRI.mWM));
   5216    uint32_t areaCols = areas ? areas->width + 1 : 1;
   5217    mExplicitGridColEnd =
   5218        aGridRI.mColFunctions.ComputeExplicitGridEnd(areaCols);
   5219  } else {
   5220    const auto* subgrid = aGridRI.mFrame->GetProperty(Subgrid::Prop());
   5221    subgridRange = &subgrid->SubgridCols();
   5222    uint32_t extent = subgridRange->Extent();
   5223    mExplicitGridColEnd = extent + 1;  // the grid is 1-based at this point
   5224    parentLineNameMap =
   5225        ParentLineMapForAxis(subgrid->mIsOrthogonal, LogicalAxis::Inline);
   5226    auto parentWM =
   5227        aGridRI.mFrame->ParentGridContainerForSubgrid()->GetWritingMode();
   5228    subgridAxisIsSameDirection =
   5229        aGridRI.mWM.ParallelAxisStartsOnSameSide(LogicalAxis::Inline, parentWM);
   5230  }
   5231  mGridColEnd = mExplicitGridColEnd;
   5232  LineNameMap colLineNameMap(gridStyle, mAreas, aGridRI.mColFunctions,
   5233                             parentLineNameMap, subgridRange,
   5234                             subgridAxisIsSameDirection);
   5235 
   5236  if (!aGridRI.mFrame->IsRowSubgrid()) {
   5237    const Maybe<nscoord> containBSize = aGridRI.mFrame->ContainIntrinsicBSize();
   5238    const nscoord repeatTrackSizingBSize = [&] {
   5239      // This clamping only applies to auto sizes.
   5240      if (containBSize &&
   5241          aSizes.mSize.BSize(aGridRI.mWM) == NS_UNCONSTRAINEDSIZE) {
   5242        return CSSMinMax(*containBSize, aSizes.mMin.BSize(aGridRI.mWM),
   5243                         aSizes.mMax.BSize(aGridRI.mWM));
   5244      }
   5245      return aSizes.mSize.BSize(aGridRI.mWM);
   5246    }();
   5247    aGridRI.mRowFunctions.InitRepeatTracks(
   5248        gridStyle->mRowGap, aSizes.mMin.BSize(aGridRI.mWM),
   5249        repeatTrackSizingBSize, aSizes.mMax.BSize(aGridRI.mWM));
   5250    uint32_t areaRows = areas ? areas->strings.Length() + 1 : 1;
   5251    mExplicitGridRowEnd =
   5252        aGridRI.mRowFunctions.ComputeExplicitGridEnd(areaRows);
   5253    parentLineNameMap = nullptr;
   5254    subgridRange = nullptr;
   5255  } else {
   5256    const auto* subgrid = aGridRI.mFrame->GetProperty(Subgrid::Prop());
   5257    subgridRange = &subgrid->SubgridRows();
   5258    uint32_t extent = subgridRange->Extent();
   5259    mExplicitGridRowEnd = extent + 1;  // the grid is 1-based at this point
   5260    parentLineNameMap =
   5261        ParentLineMapForAxis(subgrid->mIsOrthogonal, LogicalAxis::Block);
   5262    auto parentWM =
   5263        aGridRI.mFrame->ParentGridContainerForSubgrid()->GetWritingMode();
   5264    subgridAxisIsSameDirection =
   5265        aGridRI.mWM.ParallelAxisStartsOnSameSide(LogicalAxis::Block, parentWM);
   5266  }
   5267  mGridRowEnd = mExplicitGridRowEnd;
   5268  LineNameMap rowLineNameMap(gridStyle, mAreas, aGridRI.mRowFunctions,
   5269                             parentLineNameMap, subgridRange,
   5270                             subgridAxisIsSameDirection);
   5271 
   5272  const bool isSubgridOrItemInSubgrid =
   5273      aGridRI.mFrame->IsSubgrid() || !!mParentGrid;
   5274  auto SetSubgridChildEdgeBits =
   5275      [this, isSubgridOrItemInSubgrid](GridItemInfo& aItem) -> void {
   5276    if (isSubgridOrItemInSubgrid) {
   5277      const auto& area = aItem.mArea;
   5278      if (area.mCols.mStart == 0) {
   5279        aItem.mState[LogicalAxis::Inline] |= ItemState::eStartEdge;
   5280      }
   5281      if (area.mCols.mEnd == mGridColEnd) {
   5282        aItem.mState[LogicalAxis::Inline] |= ItemState::eEndEdge;
   5283      }
   5284      if (area.mRows.mStart == 0) {
   5285        aItem.mState[LogicalAxis::Block] |= ItemState::eStartEdge;
   5286      }
   5287      if (area.mRows.mEnd == mGridRowEnd) {
   5288        aItem.mState[LogicalAxis::Block] |= ItemState::eEndEdge;
   5289      }
   5290    }
   5291  };
   5292 
   5293  SetLineMaps(&colLineNameMap, &rowLineNameMap);
   5294 
   5295  // https://drafts.csswg.org/css-grid-2/#line-placement
   5296  // Resolve definite positions per spec chapter 8.3.
   5297  int32_t minCol = 1;
   5298  int32_t minRow = 1;
   5299  aGridRI.mGridItems.ClearAndRetainStorage();
   5300  aGridRI.mIter.Reset();
   5301  for (; !aGridRI.mIter.AtEnd(); aGridRI.mIter.Next()) {
   5302    nsIFrame* child = *aGridRI.mIter;
   5303    GridItemInfo* info = aGridRI.mGridItems.AppendElement(GridItemInfo(
   5304        child,
   5305        PlaceDefinite(child, colLineNameMap, rowLineNameMap, gridStyle)));
   5306    MOZ_ASSERT(aGridRI.mIter.ItemIndex() == aGridRI.mGridItems.Length() - 1,
   5307               "ItemIndex() is broken");
   5308    GridArea& area = info->mArea;
   5309    if (area.mCols.IsDefinite()) {
   5310      minCol = std::min(minCol, area.mCols.mUntranslatedStart);
   5311    }
   5312    if (area.mRows.IsDefinite()) {
   5313      minRow = std::min(minRow, area.mRows.mUntranslatedStart);
   5314    }
   5315  }
   5316 
   5317  // Translate the whole grid so that the top-/left-most area is at 0,0.
   5318  mExplicitGridOffsetCol = 1 - minCol;  // minCol/Row is always <= 1, see above
   5319  mExplicitGridOffsetRow = 1 - minRow;
   5320  aGridRI.mColFunctions.mExplicitGridOffset = mExplicitGridOffsetCol;
   5321  aGridRI.mRowFunctions.mExplicitGridOffset = mExplicitGridOffsetRow;
   5322  const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
   5323  const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
   5324  const bool isRowMasonry = aGridRI.mFrame->IsRowMasonry();
   5325  const bool isColMasonry = aGridRI.mFrame->IsColMasonry();
   5326  const bool isMasonry = isColMasonry || isRowMasonry;
   5327  mGridColEnd += offsetToColZero;
   5328  mGridRowEnd += offsetToRowZero;
   5329  const uint32_t gridAxisTrackCount = isRowMasonry ? mGridColEnd : mGridRowEnd;
   5330  aGridRI.mIter.Reset();
   5331  for (; !aGridRI.mIter.AtEnd(); aGridRI.mIter.Next()) {
   5332    auto& item = aGridRI.mGridItems[aGridRI.mIter.ItemIndex()];
   5333    GridArea& area = item.mArea;
   5334    if (area.mCols.IsDefinite()) {
   5335      area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
   5336      area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
   5337    }
   5338    if (area.mRows.IsDefinite()) {
   5339      area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
   5340      area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
   5341    }
   5342    if (area.IsDefinite()) {
   5343      if (isMasonry) {
   5344        item.MaybeInhibitSubgridInMasonry(aGridRI.mFrame, gridAxisTrackCount);
   5345      }
   5346      if (item.IsSubgrid()) {
   5347        Grid grid(this);
   5348        grid.SubgridPlaceGridItems(aGridRI, this, item);
   5349      }
   5350      mCellMap.Fill(area);
   5351      InflateGridFor(area);
   5352      SetSubgridChildEdgeBits(item);
   5353    }
   5354  }
   5355 
   5356  // https://drafts.csswg.org/css-grid-2/#auto-placement-algo
   5357  // Step 1, place 'auto' items that have one definite position -
   5358  // definite row (column) for grid-auto-flow:row (column).
   5359  auto flowStyle = gridStyle->mGridAutoFlow;
   5360  const bool isRowOrder =
   5361      isMasonry ? isRowMasonry : !!(flowStyle & StyleGridAutoFlow::ROW);
   5362  const bool isSparse = !(flowStyle & StyleGridAutoFlow::DENSE);
   5363  uint32_t clampMaxColLine = colLineNameMap.mClampMaxLine + offsetToColZero;
   5364  uint32_t clampMaxRowLine = rowLineNameMap.mClampMaxLine + offsetToRowZero;
   5365  // We need 1 cursor per row (or column) if placement is sparse.
   5366  {
   5367    Maybe<nsTHashMap<nsUint32HashKey, uint32_t>> cursors;
   5368    if (isSparse) {
   5369      cursors.emplace();
   5370    }
   5371    auto placeAutoMinorFunc =
   5372        isRowOrder ? &Grid::PlaceAutoCol : &Grid::PlaceAutoRow;
   5373    uint32_t clampMaxLine = isRowOrder ? clampMaxColLine : clampMaxRowLine;
   5374    aGridRI.mIter.Reset();
   5375    for (; !aGridRI.mIter.AtEnd(); aGridRI.mIter.Next()) {
   5376      auto& item = aGridRI.mGridItems[aGridRI.mIter.ItemIndex()];
   5377      GridArea& area = item.mArea;
   5378      LineRange& major = isRowOrder ? area.mRows : area.mCols;
   5379      LineRange& minor = isRowOrder ? area.mCols : area.mRows;
   5380      if (major.IsDefinite() && minor.IsAuto()) {
   5381        // Items with 'auto' in the minor dimension only.
   5382        const uint32_t cursor = isSparse ? cursors->Get(major.mStart) : 0;
   5383        (this->*placeAutoMinorFunc)(cursor, &area, clampMaxLine);
   5384        if (isMasonry) {
   5385          item.MaybeInhibitSubgridInMasonry(aGridRI.mFrame, gridAxisTrackCount);
   5386        }
   5387        if (item.IsSubgrid()) {
   5388          Grid grid(this);
   5389          grid.SubgridPlaceGridItems(aGridRI, this, item);
   5390        }
   5391        mCellMap.Fill(area);
   5392        SetSubgridChildEdgeBits(item);
   5393        if (isSparse) {
   5394          cursors->InsertOrUpdate(major.mStart, minor.mEnd);
   5395        }
   5396      }
   5397      InflateGridFor(area);  // Step 2, inflating for auto items too
   5398    }
   5399  }
   5400 
   5401  // XXX NOTE possible spec issue.
   5402  // XXX It's unclear if the remaining major-dimension auto and
   5403  // XXX auto in both dimensions should use the same cursor or not,
   5404  // XXX https://www.w3.org/Bugs/Public/show_bug.cgi?id=16044
   5405  // XXX seems to indicate it shouldn't.
   5406  // XXX https://drafts.csswg.org/css-grid-2/#auto-placement-algo
   5407  // XXX now says it should (but didn't in earlier versions)
   5408 
   5409  // Step 3, place the remaining grid items
   5410  uint32_t cursorMajor = 0;  // for 'dense' these two cursors will stay at 0,0
   5411  uint32_t cursorMinor = 0;
   5412  auto placeAutoMajorFunc =
   5413      isRowOrder ? &Grid::PlaceAutoRow : &Grid::PlaceAutoCol;
   5414  uint32_t clampMaxMajorLine = isRowOrder ? clampMaxRowLine : clampMaxColLine;
   5415  aGridRI.mIter.Reset();
   5416  for (; !aGridRI.mIter.AtEnd(); aGridRI.mIter.Next()) {
   5417    auto& item = aGridRI.mGridItems[aGridRI.mIter.ItemIndex()];
   5418    GridArea& area = item.mArea;
   5419    MOZ_ASSERT(*aGridRI.mIter == item.mFrame,
   5420               "iterator out of sync with aState.mGridItems");
   5421    LineRange& major = isRowOrder ? area.mRows : area.mCols;
   5422    LineRange& minor = isRowOrder ? area.mCols : area.mRows;
   5423    if (major.IsAuto()) {
   5424      if (minor.IsDefinite()) {
   5425        // Items with 'auto' in the major dimension only.
   5426        if (isSparse) {
   5427          if (minor.mStart < cursorMinor) {
   5428            ++cursorMajor;
   5429          }
   5430          cursorMinor = minor.mStart;
   5431        }
   5432        (this->*placeAutoMajorFunc)(cursorMajor, &area, clampMaxMajorLine);
   5433        if (isSparse) {
   5434          cursorMajor = major.mStart;
   5435        }
   5436      } else {
   5437        // Items with 'auto' in both dimensions.
   5438        if (isRowOrder) {
   5439          PlaceAutoAutoInRowOrder(cursorMinor, cursorMajor, &area,
   5440                                  clampMaxColLine, clampMaxRowLine);
   5441        } else {
   5442          PlaceAutoAutoInColOrder(cursorMajor, cursorMinor, &area,
   5443                                  clampMaxColLine, clampMaxRowLine);
   5444        }
   5445        if (isSparse) {
   5446          cursorMajor = major.mStart;
   5447          cursorMinor = minor.mEnd;
   5448 #ifdef DEBUG
   5449          uint32_t gridMajorEnd = isRowOrder ? mGridRowEnd : mGridColEnd;
   5450          uint32_t gridMinorEnd = isRowOrder ? mGridColEnd : mGridRowEnd;
   5451          MOZ_ASSERT(cursorMajor <= gridMajorEnd,
   5452                     "we shouldn't need to place items further than 1 track "
   5453                     "past the current end of the grid, in major dimension");
   5454          MOZ_ASSERT(cursorMinor <= gridMinorEnd,
   5455                     "we shouldn't add implicit minor tracks for auto/auto");
   5456 #endif
   5457        }
   5458      }
   5459      if (isMasonry) {
   5460        item.MaybeInhibitSubgridInMasonry(aGridRI.mFrame, gridAxisTrackCount);
   5461      }
   5462      if (item.IsSubgrid()) {
   5463        Grid grid(this);
   5464        grid.SubgridPlaceGridItems(aGridRI, this, item);
   5465      }
   5466      mCellMap.Fill(area);
   5467      InflateGridFor(area);
   5468      SetSubgridChildEdgeBits(item);
   5469      // XXXmats it might be possible to optimize this a bit for masonry layout
   5470      // if this item was placed in the 2nd row && !isSparse, or the 1st row
   5471      // is full.  Still gotta inflate the grid for all items though to make
   5472      // the grid large enough...
   5473    }
   5474  }
   5475 
   5476  // Force all items into the 1st/2nd track and have span 1 in the masonry axis.
   5477  // (See comment on nsGridContainerFrame::MasonryLayout().)
   5478  if (isMasonry) {
   5479    auto masonryAxis = isRowMasonry ? LogicalAxis::Block : LogicalAxis::Inline;
   5480    aGridRI.mIter.Reset();
   5481    for (; !aGridRI.mIter.AtEnd(); aGridRI.mIter.Next()) {
   5482      auto& item = aGridRI.mGridItems[aGridRI.mIter.ItemIndex()];
   5483      auto& masonryRange = item.mArea.LineRangeForAxis(masonryAxis);
   5484      masonryRange.mStart = std::min(masonryRange.mStart, 1U);
   5485      masonryRange.mEnd = masonryRange.mStart + 1U;
   5486    }
   5487  }
   5488 
   5489  if (auto* absCB = aGridRI.mFrame->GetAbsoluteContainingBlock();
   5490      absCB && absCB->PrepareAbsoluteFrames(aGridRI.mFrame)) {
   5491    // 10.1. With a Grid Container as Containing Block
   5492    // https://drafts.csswg.org/css-grid-2/#abspos-items
   5493    // We only resolve definite lines here; we'll align auto positions to the
   5494    // grid container later during reflow.
   5495    const nsFrameList& children = absCB->GetChildList();
   5496    const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
   5497    const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
   5498    // Untranslate the grid again temporarily while resolving abs.pos. lines.
   5499    AutoRestore<uint32_t> zeroOffsetGridColEnd(mGridColEnd);
   5500    AutoRestore<uint32_t> zeroOffsetGridRowEnd(mGridRowEnd);
   5501    mGridColEnd -= offsetToColZero;
   5502    mGridRowEnd -= offsetToRowZero;
   5503    aGridRI.mAbsPosItems.ClearAndRetainStorage();
   5504    for (nsIFrame* child : children) {
   5505      GridItemInfo* info = aGridRI.mAbsPosItems.AppendElement(GridItemInfo(
   5506          child,
   5507          PlaceAbsPos(child, colLineNameMap, rowLineNameMap, gridStyle)));
   5508      GridArea& area = info->mArea;
   5509      if (area.mCols.mUntranslatedStart != int32_t(kAutoLine)) {
   5510        area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
   5511        if (isColMasonry) {
   5512          // XXXmats clamp any non-auto line to 0 or 1. This is intended to
   5513          // allow authors to address the start/end of the masonry box.
   5514          // This is experimental at this point though and needs author feedback
   5515          // and spec work to sort out what is desired and how it should work.
   5516          // See https://github.com/w3c/csswg-drafts/issues/4650
   5517          area.mCols.mStart = std::min(area.mCols.mStart, 1U);
   5518        }
   5519      }
   5520      if (area.mCols.mUntranslatedEnd != int32_t(kAutoLine)) {
   5521        area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
   5522        if (isColMasonry) {
   5523          // ditto
   5524          area.mCols.mEnd = std::min(area.mCols.mEnd, 1U);
   5525        }
   5526      }
   5527      if (area.mRows.mUntranslatedStart != int32_t(kAutoLine)) {
   5528        area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
   5529        if (isRowMasonry) {
   5530          // ditto
   5531          area.mRows.mStart = std::min(area.mRows.mStart, 1U);
   5532        }
   5533      }
   5534      if (area.mRows.mUntranslatedEnd != int32_t(kAutoLine)) {
   5535        area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
   5536        if (isRowMasonry) {
   5537          // ditto
   5538          area.mRows.mEnd = std::min(area.mRows.mEnd, 1U);
   5539        }
   5540      }
   5541      if (isMasonry) {
   5542        info->MaybeInhibitSubgridInMasonry(aGridRI.mFrame, gridAxisTrackCount);
   5543      }
   5544 
   5545      // An abs.pos. subgrid with placement auto/1 or -1/auto technically
   5546      // doesn't span any parent tracks.  Inhibit subgridding in this case.
   5547      if (info->IsSubgrid(LogicalAxis::Inline)) {
   5548        if (info->mArea.mCols.mStart == zeroOffsetGridColEnd.SavedValue() ||
   5549            info->mArea.mCols.mEnd == 0) {
   5550          info->InhibitSubgrid(aGridRI.mFrame, LogicalAxis::Inline);
   5551        }
   5552      }
   5553      if (info->IsSubgrid(LogicalAxis::Block)) {
   5554        if (info->mArea.mRows.mStart == zeroOffsetGridRowEnd.SavedValue() ||
   5555            info->mArea.mRows.mEnd == 0) {
   5556          info->InhibitSubgrid(aGridRI.mFrame, LogicalAxis::Block);
   5557        }
   5558      }
   5559 
   5560      if (info->IsSubgrid()) {
   5561        Grid grid(this);
   5562        grid.SubgridPlaceGridItems(aGridRI, this, *info);
   5563      }
   5564    }
   5565  }
   5566 
   5567  // Count empty 'auto-fit' tracks in the repeat() range.
   5568  // |colAdjust| will have a count for each line in the grid of how many
   5569  // tracks were empty between the start of the grid and that line.
   5570 
   5571  Maybe<nsTArray<uint32_t>> colAdjust;
   5572  uint32_t numEmptyCols = 0;
   5573  if (aGridRI.mColFunctions.mHasRepeatAuto &&
   5574      gridStyle->mGridTemplateColumns.GetRepeatAutoValue()->count.IsAutoFit()) {
   5575    const auto& cellMap = mCellMap;
   5576    colAdjust = CalculateAdjustForAutoFitElements(
   5577        &numEmptyCols, aGridRI.mColFunctions, mGridColEnd + 1,
   5578        [&cellMap](uint32_t i) -> bool { return cellMap.IsEmptyCol(i); });
   5579  }
   5580 
   5581  // Do similar work for the row tracks, with the same logic.
   5582  Maybe<nsTArray<uint32_t>> rowAdjust;
   5583  uint32_t numEmptyRows = 0;
   5584  if (aGridRI.mRowFunctions.mHasRepeatAuto &&
   5585      gridStyle->mGridTemplateRows.GetRepeatAutoValue()->count.IsAutoFit()) {
   5586    const auto& cellMap = mCellMap;
   5587    rowAdjust = CalculateAdjustForAutoFitElements(
   5588        &numEmptyRows, aGridRI.mRowFunctions, mGridRowEnd + 1,
   5589        [&cellMap](uint32_t i) -> bool { return cellMap.IsEmptyRow(i); });
   5590  }
   5591  MOZ_ASSERT((numEmptyCols > 0) == colAdjust.isSome());
   5592  MOZ_ASSERT((numEmptyRows > 0) == rowAdjust.isSome());
   5593  // Remove the empty 'auto-fit' tracks we found above, if any.
   5594  if (numEmptyCols || numEmptyRows) {
   5595    // Adjust the line numbers in the grid areas.
   5596    for (auto& item : aGridRI.mGridItems) {
   5597      if (numEmptyCols) {
   5598        item.AdjustForRemovedTracks(LogicalAxis::Inline, *colAdjust);
   5599      }
   5600      if (numEmptyRows) {
   5601        item.AdjustForRemovedTracks(LogicalAxis::Block, *rowAdjust);
   5602      }
   5603    }
   5604    for (auto& item : aGridRI.mAbsPosItems) {
   5605      if (numEmptyCols) {
   5606        item.AdjustForRemovedTracks(LogicalAxis::Inline, *colAdjust);
   5607      }
   5608      if (numEmptyRows) {
   5609        item.AdjustForRemovedTracks(LogicalAxis::Block, *rowAdjust);
   5610      }
   5611    }
   5612    // Adjust the grid size.
   5613    mGridColEnd -= numEmptyCols;
   5614    mExplicitGridColEnd -= numEmptyCols;
   5615    mGridRowEnd -= numEmptyRows;
   5616    mExplicitGridRowEnd -= numEmptyRows;
   5617    // Adjust the track mapping to unmap the removed tracks.
   5618    auto colRepeatCount = aGridRI.mColFunctions.NumRepeatTracks();
   5619    aGridRI.mColFunctions.SetNumRepeatTracks(colRepeatCount - numEmptyCols);
   5620    auto rowRepeatCount = aGridRI.mRowFunctions.NumRepeatTracks();
   5621    aGridRI.mRowFunctions.SetNumRepeatTracks(rowRepeatCount - numEmptyRows);
   5622  }
   5623 
   5624  // Update the line boundaries of the implicit grid areas, if needed.
   5625  if (mAreas && aGridRI.mFrame->HasAnyStateBits(NS_STATE_GRID_COMPUTED_INFO)) {
   5626    for (auto iter = mAreas->iter(); !iter.done(); iter.next()) {
   5627      auto& areaInfo = iter.get().value();
   5628 
   5629      // Resolve the lines for the area. We use the name of the area as the
   5630      // name of the lines, knowing that the line placement algorithm will
   5631      // add the -start and -end suffixes as appropriate for layout.
   5632      StyleGridLine lineStartAndEnd;
   5633      lineStartAndEnd.ident._0 = areaInfo.name;
   5634 
   5635      LineRange columnLines =
   5636          ResolveLineRange(lineStartAndEnd, lineStartAndEnd, colLineNameMap,
   5637                           LogicalAxis::Inline, mExplicitGridColEnd, gridStyle);
   5638 
   5639      LineRange rowLines =
   5640          ResolveLineRange(lineStartAndEnd, lineStartAndEnd, rowLineNameMap,
   5641                           LogicalAxis::Block, mExplicitGridRowEnd, gridStyle);
   5642 
   5643      // Put the resolved line indices back into the area structure.
   5644      areaInfo.columns.start = columnLines.mStart + mExplicitGridOffsetCol;
   5645      areaInfo.columns.end = columnLines.mEnd + mExplicitGridOffsetCol;
   5646      areaInfo.rows.start = rowLines.mStart + mExplicitGridOffsetRow;
   5647      areaInfo.rows.end = rowLines.mEnd + mExplicitGridOffsetRow;
   5648    }
   5649  }
   5650 }
   5651 
   5652 void nsGridContainerFrame::Tracks::Initialize(
   5653    const TrackSizingFunctions& aFunctions,
   5654    const NonNegativeLengthPercentageOrNormal& aGridGap, uint32_t aNumTracks,
   5655    nscoord aContentBoxSize) {
   5656  mSizes.SetLength(aNumTracks);
   5657  mSizes.ZeroInitialize();
   5658  for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
   5659    auto& sz = mSizes[i];
   5660    mStateUnion |= sz.Initialize(aContentBoxSize, aFunctions.SizingFor(i));
   5661    if (mIsMasonry) {
   5662      sz.mBase = aContentBoxSize;
   5663      sz.mLimit = aContentBoxSize;
   5664    }
   5665  }
   5666  mGridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aContentBoxSize);
   5667  mContentBoxSize = aContentBoxSize;
   5668 }
   5669 
   5670 /**
   5671 * Reflow aChild in the given aAvailableSize.
   5672 */
   5673 static nscoord MeasuringReflow(nsIFrame* aChild,
   5674                               const ReflowInput* aReflowInput, gfxContext* aRC,
   5675                               const LogicalSize& aAvailableSize,
   5676                               const LogicalSize& aCBSize,
   5677                               nscoord aIMinSizeClamp = NS_MAXSIZE,
   5678                               nscoord aBMinSizeClamp = NS_MAXSIZE) {
   5679  MOZ_ASSERT(aChild->IsGridItem(), "aChild should be a grid item!");
   5680  auto* parent = static_cast<nsGridContainerFrame*>(aChild->GetParent());
   5681  nsPresContext* pc = aChild->PresContext();
   5682  Maybe<ReflowInput> dummyParentState;
   5683  const ReflowInput* rs = aReflowInput;
   5684  if (!aReflowInput) {
   5685    MOZ_ASSERT(!parent->HasAnyStateBits(NS_FRAME_IN_REFLOW));
   5686    dummyParentState.emplace(
   5687        pc, parent, aRC,
   5688        LogicalSize(parent->GetWritingMode(), 0, NS_UNCONSTRAINEDSIZE),
   5689        ReflowInput::InitFlag::DummyParentReflowInput);
   5690    rs = dummyParentState.ptr();
   5691  }
   5692 #ifdef DEBUG
   5693  // This will suppress various ABSURD_SIZE warnings for this reflow.
   5694  parent->SetProperty(nsContainerFrame::DebugReflowingWithInfiniteISize(),
   5695                      true);
   5696 #endif
   5697  auto wm = aChild->GetWritingMode();
   5698  ComputeSizeFlags csFlags = ComputeSizeFlag::IsGridMeasuringReflow;
   5699  // Shrink-wrap grid items that will be aligned (rather than stretched) in
   5700  // their own inline axis.
   5701  if (!parent->GridItemShouldStretch(aChild, LogicalAxis::Inline)) {
   5702    csFlags += ComputeSizeFlag::ShrinkWrap;
   5703  }
   5704  if (aAvailableSize.ISize(wm) == INFINITE_ISIZE_COORD) {
   5705    csFlags += ComputeSizeFlag::ShrinkWrap;
   5706  }
   5707  if (aIMinSizeClamp != NS_MAXSIZE) {
   5708    csFlags += ComputeSizeFlag::IClampMarginBoxMinSize;
   5709  }
   5710  if (aBMinSizeClamp != NS_MAXSIZE) {
   5711    csFlags += ComputeSizeFlag::BClampMarginBoxMinSize;
   5712    aChild->SetProperty(nsIFrame::BClampMarginBoxMinSizeProperty(),
   5713                        aBMinSizeClamp);
   5714  } else {
   5715    aChild->RemoveProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
   5716  }
   5717  ReflowInput childRI(pc, *rs, aChild, aAvailableSize, Some(aCBSize), {}, {},
   5718                      csFlags);
   5719 
   5720  // FIXME (perf): It would be faster to do this only if the previous reflow of
   5721  // the child was not a measuring reflow, and only if the child does some of
   5722  // the things that are affected by ComputeSizeFlag::IsGridMeasuringReflow.
   5723  childRI.SetBResize(true);
   5724  // Not 100% sure this is needed, but be conservative for now:
   5725  childRI.SetBResizeForPercentages(true);
   5726 
   5727  ReflowOutput childSize(childRI);
   5728  nsReflowStatus childStatus;
   5729  const nsIFrame::ReflowChildFlags flags =
   5730      nsIFrame::ReflowChildFlags::NoMoveFrame |
   5731      nsIFrame::ReflowChildFlags::NoDeleteNextInFlowChild;
   5732 
   5733  // Reflowing the child might invalidate the cache, so we declare the variable
   5734  // inside the if-statement to ensure it isn't accessed after it may have
   5735  // become invalid.
   5736  if (const GridItemCachedBAxisMeasurement* cachedMeasurement =
   5737          aChild->GetProperty(GridItemCachedBAxisMeasurement::Prop());
   5738      cachedMeasurement && cachedMeasurement->IsValidFor(aChild, aCBSize)) {
   5739    childSize.BSize(wm) = cachedMeasurement->BSize();
   5740    childSize.ISize(wm) = aChild->ISize(wm);
   5741    nsContainerFrame::FinishReflowChild(aChild, pc, childSize, &childRI, wm,
   5742                                        LogicalPoint(wm), nsSize(), flags);
   5743    GRID_LOG(
   5744        "[perf] MeasuringReflow accepted cached value=%d, child=%p, "
   5745        "aCBSize.ISize=%d",
   5746        cachedMeasurement->BSize(), aChild, aCBSize.ISize(wm));
   5747    return cachedMeasurement->BSize();
   5748  }
   5749 
   5750  parent->ReflowChild(aChild, pc, childSize, childRI, wm, LogicalPoint(wm),
   5751                      nsSize(), flags, childStatus);
   5752  nsContainerFrame::FinishReflowChild(aChild, pc, childSize, &childRI, wm,
   5753                                      LogicalPoint(wm), nsSize(), flags);
   5754 #ifdef DEBUG
   5755  parent->RemoveProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
   5756 #endif
   5757 
   5758  if (GridItemCachedBAxisMeasurement* cachedMeasurement =
   5759          aChild->GetProperty(GridItemCachedBAxisMeasurement::Prop())) {
   5760    cachedMeasurement->Update(aChild, aCBSize, childSize.BSize(wm));
   5761    GRID_LOG(
   5762        "[perf] MeasuringReflow rejected but updated cached value=%d, "
   5763        "child=%p, aCBSize.ISize=%d",
   5764        cachedMeasurement->BSize(), aChild, aCBSize.ISize(wm));
   5765  } else {
   5766    cachedMeasurement = new GridItemCachedBAxisMeasurement(aChild, aCBSize,
   5767                                                           childSize.BSize(wm));
   5768    aChild->SetProperty(GridItemCachedBAxisMeasurement::Prop(),
   5769                        cachedMeasurement);
   5770    GRID_LOG(
   5771        "[perf] MeasuringReflow created new cached value=%d, child=%p, "
   5772        "aCBSize.ISize=%d",
   5773        cachedMeasurement->BSize(), aChild, aCBSize.ISize(wm));
   5774  }
   5775 
   5776  return childSize.BSize(wm);
   5777 }
   5778 
   5779 /**
   5780 * Return the accumulated margin+border+padding in aAxis for aFrame (a subgrid)
   5781 * and its ancestor subgrids.
   5782 */
   5783 static LogicalMargin SubgridAccumulatedMarginBorderPadding(
   5784    nsIFrame* aFrame, const Subgrid* aSubgrid, WritingMode aResultWM,
   5785    LogicalAxis aAxis) {
   5786  MOZ_ASSERT(aFrame->IsGridContainerFrame());
   5787  auto* subgridFrame = static_cast<nsGridContainerFrame*>(aFrame);
   5788  LogicalMargin result(aSubgrid->mMarginBorderPadding);
   5789  auto* parent = subgridFrame->ParentGridContainerForSubgrid();
   5790  auto subgridCBWM = parent->GetWritingMode();
   5791  auto childRange = aSubgrid->mArea.LineRangeForAxis(aAxis);
   5792  bool skipStartSide = false;
   5793  bool skipEndSide = false;
   5794  auto axis = aSubgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
   5795  // If aFrame's parent is also a subgrid, then add its MBP on the edges that
   5796  // are adjacent (i.e. start or end in the same track), recursively.
   5797  // ("parent" refers to the grid-frame we're currently adding MBP for,
   5798  // and "grandParent" its parent, as we walk up the chain.)
   5799  while (parent->IsSubgrid(axis)) {
   5800    auto* parentSubgrid = parent->GetProperty(Subgrid::Prop());
   5801    auto* grandParent = parent->ParentGridContainerForSubgrid();
   5802    auto parentCBWM = grandParent->GetWritingMode();
   5803    if (parentCBWM.IsOrthogonalTo(subgridCBWM)) {
   5804      axis = GetOrthogonalAxis(axis);
   5805    }
   5806    const auto& parentRange = parentSubgrid->mArea.LineRangeForAxis(axis);
   5807    bool sameDir = parentCBWM.ParallelAxisStartsOnSameSide(axis, subgridCBWM);
   5808    if (sameDir) {
   5809      skipStartSide |= childRange.mStart != 0;
   5810      skipEndSide |= childRange.mEnd != parentRange.Extent();
   5811    } else {
   5812      skipEndSide |= childRange.mStart != 0;
   5813      skipStartSide |= childRange.mEnd != parentRange.Extent();
   5814    }
   5815    if (skipStartSide && skipEndSide) {
   5816      break;
   5817    }
   5818    auto mbp =
   5819        parentSubgrid->mMarginBorderPadding.ConvertTo(subgridCBWM, parentCBWM);
   5820    if (skipStartSide) {
   5821      mbp.Start(aAxis, subgridCBWM) = nscoord(0);
   5822    }
   5823    if (skipEndSide) {
   5824      mbp.End(aAxis, subgridCBWM) = nscoord(0);
   5825    }
   5826    result += mbp;
   5827    parent = grandParent;
   5828    childRange = parentRange;
   5829  }
   5830  return result.ConvertTo(aResultWM, subgridCBWM);
   5831 }
   5832 
   5833 /**
   5834 * Return the [min|max]-content contribution of aChild to its parent (i.e.
   5835 * the child's margin-box) in aAxis.
   5836 */
   5837 static nscoord ContentContribution(const GridItemInfo& aGridItem,
   5838                                   const GridReflowInput& aGridRI,
   5839                                   LogicalAxis aAxis,
   5840                                   const LogicalSize& aPercentageBasis,
   5841                                   IntrinsicISizeType aConstraint,
   5842                                   nscoord aMinSizeClamp = NS_MAXSIZE,
   5843                                   const StyleSizeOverrides& aOverrides = {}) {
   5844  nsIFrame* child = aGridItem.mFrame;
   5845 
   5846  const WritingMode gridWM = aGridRI.mWM;
   5847  nscoord extraMargin = 0;
   5848  nsGridContainerFrame::Subgrid* subgrid = nullptr;
   5849  if (child->GetParent() != aGridRI.mFrame) {
   5850    // |child| is a subgrid descendant, so it contributes its subgrids'
   5851    // margin+border+padding for any edge tracks that it spans.
   5852    auto* subgridFrame = child->GetParent();
   5853    subgrid = subgridFrame->GetProperty(Subgrid::Prop());
   5854    const auto itemEdgeBits = aGridItem.mState[aAxis] & ItemState::eEdgeBits;
   5855    if (itemEdgeBits) {
   5856      LogicalMargin mbp = SubgridAccumulatedMarginBorderPadding(
   5857          subgridFrame, subgrid, gridWM, aAxis);
   5858      if (itemEdgeBits & ItemState::eStartEdge) {
   5859        extraMargin += mbp.Start(aAxis, gridWM);
   5860      }
   5861      if (itemEdgeBits & ItemState::eEndEdge) {
   5862        extraMargin += mbp.End(aAxis, gridWM);
   5863      }
   5864    }
   5865    // It also contributes (half of) the subgrid's gap on its edges (if any)
   5866    // subtracted by the non-subgrid ancestor grid container's gap.
   5867    // Note that this can also be negative since it's considered a margin.
   5868    if (itemEdgeBits != ItemState::eEdgeBits) {
   5869      const auto subgridAxis =
   5870          gridWM.ConvertAxisTo(aAxis, subgridFrame->GetWritingMode());
   5871      auto& gapStyle = subgridAxis == LogicalAxis::Block
   5872                           ? subgridFrame->StylePosition()->mRowGap
   5873                           : subgridFrame->StylePosition()->mColumnGap;
   5874      if (!gapStyle.IsNormal()) {
   5875        auto subgridExtent = subgridAxis == LogicalAxis::Block
   5876                                 ? subgrid->mGridRowEnd
   5877                                 : subgrid->mGridColEnd;
   5878        if (subgridExtent > 1) {
   5879          nscoord subgridGap =
   5880              nsLayoutUtils::ResolveGapToLength(gapStyle, NS_UNCONSTRAINEDSIZE);
   5881          const auto& tracks = aGridRI.TracksFor(aAxis);
   5882          auto gapDelta = subgridGap - tracks.mGridGap;
   5883          if (!itemEdgeBits) {
   5884            extraMargin += gapDelta;
   5885          } else {
   5886            extraMargin += gapDelta / 2;
   5887          }
   5888        }
   5889      }
   5890    }
   5891  }
   5892 
   5893  gfxContext* rc = &aGridRI.mRenderingContext;
   5894  PhysicalAxis axis = gridWM.PhysicalAxis(aAxis);
   5895  nscoord size = nsLayoutUtils::IntrinsicForAxis(
   5896      axis, rc, child, aConstraint, Some(aPercentageBasis),
   5897      nsLayoutUtils::BAIL_IF_REFLOW_NEEDED, aMinSizeClamp, aOverrides);
   5898  auto childWM = child->GetWritingMode();
   5899  const bool isOrthogonal = childWM.IsOrthogonalTo(gridWM);
   5900  auto childAxis = isOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
   5901  if (size == NS_INTRINSIC_ISIZE_UNKNOWN && childAxis == LogicalAxis::Block) {
   5902    if (aGridRI.mIsGridIntrinsicSizing && aAxis == LogicalAxis::Block) {
   5903      // We may reach here while computing the grid container's min-content
   5904      // contribution in ComputeIntrinsicISize(), potentially during row size
   5905      // resolution. In this context, the main reason for computing row sizes is
   5906      // to transfer the child's block-size to the inline-axis via aspect-ratio,
   5907      // contributing to the grid container's intrinsic inline-size in a later
   5908      // column size resolution. Since an indefinite block-size cannot be
   5909      // transferred in this way, we can safely skip MeasuringReflow() and
   5910      // simply use zero as a dummy value because the value does not affect the
   5911      // result.
   5912      size = 0;
   5913    } else {
   5914      // We need to reflow the child to find its BSize contribution.
   5915      nscoord availISize = INFINITE_ISIZE_COORD;
   5916      nscoord availBSize = NS_UNCONSTRAINEDSIZE;
   5917      // The next two variables are MinSizeClamp values in the child's axes.
   5918      nscoord iMinSizeClamp = NS_MAXSIZE;
   5919      nscoord bMinSizeClamp = NS_MAXSIZE;
   5920      LogicalSize cbSize = aPercentageBasis;
   5921      // Below, we try to resolve the child's grid-area size in its inline-axis
   5922      // to use as the CB/Available size in the MeasuringReflow that follows.
   5923      if (child->GetParent() != aGridRI.mFrame) {
   5924        // This item is a child of a subgrid descendant.
   5925        auto* subgridFrame =
   5926            static_cast<nsGridContainerFrame*>(child->GetParent());
   5927        MOZ_ASSERT(subgridFrame->IsGridContainerFrame());
   5928        auto* uts = subgridFrame->GetProperty(UsedTrackSizes::Prop());
   5929        if (!uts) {
   5930          uts = new UsedTrackSizes();
   5931          subgridFrame->SetProperty(UsedTrackSizes::Prop(), uts);
   5932        }
   5933        // The grid-item's inline-axis as expressed in the subgrid's WM.
   5934        const auto subgridAxis = childWM.ConvertAxisTo(
   5935            LogicalAxis::Inline, subgridFrame->GetWritingMode());
   5936        uts->ResolveTrackSizesForAxis(subgridFrame, subgridAxis, *rc);
   5937        if (uts->mCanResolveLineRangeSize[subgridAxis]) {
   5938          auto* subgrid =
   5939              subgridFrame->GetProperty(nsGridContainerFrame::Subgrid::Prop());
   5940          const GridItemInfo* originalItem = nullptr;
   5941          for (const auto& item : subgrid->mGridItems) {
   5942            if (item.mFrame == child) {
   5943              originalItem = &item;
   5944              break;
   5945            }
   5946          }
   5947          MOZ_ASSERT(originalItem, "huh?");
   5948          const auto& range = originalItem->mArea.LineRangeForAxis(subgridAxis);
   5949          const nscoord sz = range.ToLength(uts->mTrackPlans[subgridAxis]);
   5950          if (childWM.IsOrthogonalTo(subgridFrame->GetWritingMode())) {
   5951            availBSize = sz;
   5952            cbSize.BSize(childWM) = sz;
   5953            if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
   5954              bMinSizeClamp = sz;
   5955            }
   5956          } else {
   5957            availISize = sz;
   5958            cbSize.ISize(childWM) = sz;
   5959            if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
   5960              iMinSizeClamp = sz;
   5961            }
   5962          }
   5963        }
   5964      } else {
   5965        const LogicalAxis inlineAxisInChildWM =
   5966            isOrthogonal ? LogicalAxis::Block : LogicalAxis::Inline;
   5967        const nscoord colSize = cbSize.Size(inlineAxisInChildWM, childWM);
   5968        if (colSize != NS_UNCONSTRAINEDSIZE) {
   5969          MOZ_ASSERT(aGridRI.mCols.mCanResolveLineRangeSize,
   5970                     "Grid column sizes should be resolvable!");
   5971          if (isOrthogonal) {
   5972            availBSize = colSize;
   5973            if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
   5974              bMinSizeClamp = colSize;
   5975            }
   5976          } else {
   5977            availISize = colSize;
   5978            if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
   5979              iMinSizeClamp = colSize;
   5980            }
   5981          }
   5982        }
   5983      }
   5984      if (isOrthogonal == (aAxis == LogicalAxis::Inline)) {
   5985        bMinSizeClamp = aMinSizeClamp;
   5986      } else {
   5987        iMinSizeClamp = aMinSizeClamp;
   5988      }
   5989      LogicalSize availableSize(childWM, availISize, availBSize);
   5990      size = ::MeasuringReflow(child, aGridRI.mReflowInput, rc, availableSize,
   5991                               cbSize, iMinSizeClamp, bMinSizeClamp);
   5992    }
   5993    size += child->GetLogicalUsedMargin(childWM).BStartEnd(childWM);
   5994    nscoord overflow = size - aMinSizeClamp;
   5995    if (MOZ_UNLIKELY(overflow > 0)) {
   5996      nscoord contentSize = child->ContentBSize(childWM);
   5997      nscoord newContentSize = std::max(nscoord(0), contentSize - overflow);
   5998      // XXXmats deal with percentages better, see bug 1300369 comment 27.
   5999      size -= contentSize - newContentSize;
   6000    }
   6001  }
   6002  MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
   6003             "baseline offset should be non-negative at this point");
   6004  MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
   6005                 aGridItem.mBaselineOffset[aAxis] == nscoord(0),
   6006             "baseline offset should be zero when not baseline-aligned");
   6007  size += aGridItem.mBaselineOffset[aAxis];
   6008  size += extraMargin;
   6009  return std::max(size, 0);
   6010 }
   6011 
   6012 struct CachedIntrinsicSizes {
   6013  CachedIntrinsicSizes() = delete;
   6014  CachedIntrinsicSizes(const GridItemInfo& aGridItem,
   6015                       const GridReflowInput& aGridRI, const LogicalAxis aAxis)
   6016      : mPercentageBasis(aGridRI.PercentageBasisFor(aAxis, aGridItem)) {}
   6017 
   6018  void EnsureContributions(EnumSet<GridIntrinsicSizeType> aTypes,
   6019                           const GridItemInfo& aGridItem,
   6020                           const GridReflowInput& aGridRI, LogicalAxis aAxis) {
   6021    // max-content and min-content should behave as initial value in block axis.
   6022    // XXXalaskanemily: The specifics might have changed in the spec?
   6023    // https://drafts.csswg.org/css-sizing-3/#valdef-width-min-content
   6024    // https://drafts.csswg.org/css-sizing-3/#valdef-width-max-content
   6025 
   6026    // If we need to calculate GridIntrinsicSizeType::MinContribution, we might
   6027    // need to substitute GridIntrinsicSizeType::MinContentContribution instead.
   6028    // Per https://drafts.csswg.org/css-grid-2/#algo-single-span-items
   6029    // Section "For auto minimums":
   6030    //  * "if the item's computed preferred size behaves as auto or depends on
   6031    //    the size of its containing block in the relevant axis," then we do in
   6032    //    fact need the used minimum size."
   6033    //  * "...else the item's minimum contribution is its min-content
   6034    //    contribution" in which case we make a recursive call to compute
   6035    //    GridIntrinsicSizeType::MinContentContribution instead, and do a fixup
   6036    //    to place that value in the MinContentContribution slot.
   6037    // Note that we use BehavesLikeInitialValue and HasPercent to implement
   6038    // the spec check for "behaves as auto or depends on the size of its
   6039    // containing block".
   6040    // We make a similar check in MinContributionDependsOnAutoMinSize as
   6041    // an earlier test for whether we need the used minimum size.
   6042    if (aTypes.contains(GridIntrinsicSizeType::MinContribution)) {
   6043      nsIFrame* const child = aGridItem.mFrame;
   6044      const nsStylePosition* const stylePos = child->StylePosition();
   6045      const auto anchorResolutionParams =
   6046          AnchorPosResolutionParams::From(child);
   6047      const WritingMode cbwm = aGridRI.mWM;
   6048      auto styleSize = stylePos->Size(aAxis, cbwm, anchorResolutionParams);
   6049      const LogicalAxis axisInItemWM =
   6050          cbwm.ConvertAxisTo(aAxis, child->GetWritingMode());
   6051      // FIXME: Bug 567039: moz-fit-content and -moz-available are not
   6052      // supported for block size dimension on sizing properties (e.g. height),
   6053      // so we treat it as `auto`.
   6054      if (!styleSize->BehavesLikeInitialValue(axisInItemWM) &&
   6055          !styleSize->HasPercent()) {
   6056        // Calculate without MinSize, but ensuring MinContentContribution.
   6057        aTypes -= GridIntrinsicSizeType::MinContribution;
   6058        aTypes += GridIntrinsicSizeType::MinContentContribution;
   6059        EnsureContributions(aTypes, aGridItem, aGridRI, aAxis);
   6060        // Copy the MinSize from the MinContentContribution.
   6061        mSizes[GridIntrinsicSizeType::MinContribution] =
   6062            mSizes[GridIntrinsicSizeType::MinContentContribution];
   6063        return;
   6064      }
   6065    }
   6066 
   6067    for (const GridIntrinsicSizeType type : aTypes) {
   6068      if (mSizes[type].isNothing()) {
   6069        mSizes[type].emplace(ComputeContribution(
   6070            type, aGridItem, aGridRI, aAxis, mPercentageBasis, mMinSizeClamp));
   6071      }
   6072    }
   6073  }
   6074 
   6075 private:
   6076  // Computes the MinSize, MinContentContribution, or MaxContentContribution of
   6077  // an item in the given axis.
   6078  // This helps to implement EnsureContributions. It's here to prevent other
   6079  // places from using it, as it is not general purpose and requires that the
   6080  // caller has made checks for when we will use the MinContentContribution as
   6081  // the MinSize, as EnsureContributions does.
   6082  static nscoord ComputeContribution(GridIntrinsicSizeType aType,
   6083                                     const GridItemInfo& aGridItem,
   6084                                     const GridReflowInput& aGridRI,
   6085                                     LogicalAxis aAxis,
   6086                                     const LogicalSize& aPercentageBasis,
   6087                                     nscoord aMinSizeClamp) {
   6088    const WritingMode containerWM = aGridRI.mWM;
   6089    gfxContext* const rc = &aGridRI.mRenderingContext;
   6090    switch (aType) {
   6091      case GridIntrinsicSizeType::MinContentContribution:
   6092        return ContentContribution(aGridItem, aGridRI, aAxis, aPercentageBasis,
   6093                                   IntrinsicISizeType::MinISize, aMinSizeClamp);
   6094      case GridIntrinsicSizeType::MaxContentContribution:
   6095        return ContentContribution(aGridItem, aGridRI, aAxis, aPercentageBasis,
   6096                                   IntrinsicISizeType::PrefISize,
   6097                                   aMinSizeClamp);
   6098      case GridIntrinsicSizeType::MinContribution: {
   6099        // Compute the min-size contribution for a grid item, as defined at
   6100        // https://drafts.csswg.org/css-grid-2/#min-size-contribution
   6101        nsIFrame* const child = aGridItem.mFrame;
   6102        const nsStylePosition* const stylePos = child->StylePosition();
   6103        const auto anchorResolutionParams =
   6104            AnchorPosResolutionParams::From(child);
   6105        const LogicalAxis axisInItemWM =
   6106            containerWM.ConvertAxisTo(aAxis, child->GetWritingMode());
   6107 #ifdef DEBUG
   6108        // The caller must handle this case separately.
   6109        // See EnsureContributions.
   6110        {
   6111          const auto styleSize =
   6112              stylePos->Size(aAxis, containerWM, anchorResolutionParams);
   6113          MOZ_ASSERT(styleSize->BehavesLikeInitialValue(axisInItemWM) ||
   6114                         styleSize->HasPercent(),
   6115                     "Should have been caught in EnsureContributions");
   6116        }
   6117 #endif
   6118        // https://drafts.csswg.org/css-grid-2/#min-size-auto
   6119        // This calculates the min-content contribution from either a definite
   6120        // min-width (or min-height depending on aAxis), or the
   6121        // "specified / transferred size" for min-width:auto if
   6122        // overflow == visible (as min-width:0 otherwise), or
   6123        // NS_UNCONSTRAINEDSIZE for other min-width intrinsic values
   6124        // (which results in always taking the "content size" part below).
   6125        MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
   6126                   "baseline offset should be non-negative at this point");
   6127        MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
   6128                       aGridItem.mBaselineOffset[aAxis] == (nscoord)0,
   6129                   "baseline offset should be zero when not baseline-aligned");
   6130        const auto styleMinSize =
   6131            stylePos->MinSize(aAxis, containerWM, anchorResolutionParams);
   6132 
   6133        // max-content and min-content should behave as initial value in block
   6134        // axis.
   6135        // FIXME: Bug 567039: moz-fit-content and -moz-available are not
   6136        // supported for block size dimension on sizing properties
   6137        // (e.g. height), so we treat it as `auto`.
   6138        const bool isAuto = styleMinSize->BehavesLikeInitialValue(axisInItemWM);
   6139        nscoord s = aGridItem.mBaselineOffset[aAxis];
   6140 
   6141        // Check if the min-size style of the grid item is auto and the
   6142        // minimum contribution is content-based.
   6143        // While the eContentBasedAutoMinSize flag is not synonymous with
   6144        // an item having content-based automatic minimum contribution,
   6145        // the previous checks should catch the other cases in which the
   6146        // automatic minimum contribution is zero instead.
   6147        //
   6148        // See bug 1951821 for this discrepency between the flag's usage
   6149        // and the specification:
   6150        // https://drafts.csswg.org/css-grid-2/#min-size-auto
   6151        if (!isAuto ||
   6152            (aGridItem.mState[aAxis] & ItemState::eContentBasedAutoMinSize)) {
   6153          nscoord contrib = nsLayoutUtils::MinSizeContributionForAxis(
   6154              containerWM.PhysicalAxis(aAxis), rc, child,
   6155              IntrinsicISizeType::MinISize, aPercentageBasis);
   6156          if (contrib == NS_UNCONSTRAINEDSIZE) {
   6157            s = contrib;
   6158          } else {
   6159            s += contrib;
   6160          }
   6161 
   6162          if ((axisInItemWM == LogicalAxis::Inline &&
   6163               nsIFrame::ToExtremumLength(*styleMinSize)) ||
   6164              (isAuto && !child->StyleDisplay()->IsScrollableOverflow())) {
   6165            // "if the item's computed preferred size behaves as auto or
   6166            // depends on the size of its containing block in the relevant
   6167            // axis, its minimum contribution is the outer size that would
   6168            // result from assuming the item's used minimum size as its
   6169            // preferred size"
   6170            //
   6171            // The "auto or depends on the size of its containing block" is
   6172            // checked above with ItemState::eContentBasedAutoMinSize.
   6173            //
   6174            // https://drafts.csswg.org/css-grid-2/#minimum-contribution
   6175            StyleSizeOverrides overrides;
   6176            if (axisInItemWM == LogicalAxis::Inline) {
   6177              overrides.mStyleISize.emplace(*styleMinSize.get());
   6178            } else {
   6179              overrides.mStyleBSize.emplace(*styleMinSize.get());
   6180            }
   6181            // Now calculate the "content size" part and return whichever is
   6182            // smaller.
   6183            MOZ_ASSERT(isAuto || s == NS_UNCONSTRAINEDSIZE);
   6184            s = std::min(s, ContentContribution(aGridItem, aGridRI, aAxis,
   6185                                                aPercentageBasis,
   6186                                                IntrinsicISizeType::MinISize,
   6187                                                aMinSizeClamp, overrides));
   6188          }
   6189        }
   6190        return s;
   6191      }
   6192    }
   6193    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected contribution type");
   6194  }
   6195 
   6196 public:
   6197  EnumeratedArray<GridIntrinsicSizeType, nscoord> SizesOrDefault() const {
   6198    EnumeratedArray<GridIntrinsicSizeType, nscoord> sizes;
   6199    for (GridIntrinsicSizeType type : kAllGridIntrinsicSizeTypes) {
   6200      sizes[type] = mSizes[type].valueOr(0);
   6201    }
   6202    return sizes;
   6203  }
   6204 
   6205  EnumeratedArray<GridIntrinsicSizeType, Maybe<nscoord>> mSizes;
   6206 
   6207  // The item's percentage basis for intrinsic sizing purposes.
   6208  const LogicalSize mPercentageBasis;
   6209 
   6210  // "if the grid item spans only grid tracks that have a fixed max track
   6211  // sizing function, its automatic minimum size in that dimension is
   6212  // further clamped to less than or equal to the size necessary to fit its
   6213  // margin box within the resulting grid area (flooring at zero)"
   6214  // https://drafts.csswg.org/css-grid-2/#min-size-auto
   6215  // This is the clamp value to use for that:
   6216  nscoord mMinSizeClamp = NS_MAXSIZE;
   6217 };
   6218 
   6219 void nsGridContainerFrame::Tracks::CalculateSizes(
   6220    GridReflowInput& aGridRI, nsTArray<GridItemInfo>& aGridItems,
   6221    const TrackSizingFunctions& aFunctions, nscoord aContentBoxSize,
   6222    LineRange GridArea::* aRange, SizingConstraint aConstraint) {
   6223  // Implement the intrinsic sizing algorithm, step 12.5 as described in:
   6224  // https://drafts.csswg.org/css-grid-2/#algo-content
   6225  nscoord percentageBasis = aContentBoxSize;
   6226  if (percentageBasis == NS_UNCONSTRAINEDSIZE) {
   6227    percentageBasis = 0;
   6228  }
   6229  // 12.5 step 1: Shim baseline-aligned items so their intrinsic size
   6230  // contributions reflect their baseline alignment
   6231  // https://drafts.csswg.org/css-grid-2/#algo-baseline-shims
   6232  InitializeItemBaselines(aGridRI, aGridItems);
   6233 
   6234  // 12.5 steps 2-5
   6235  ResolveIntrinsicSize(aGridRI, aGridItems, aFunctions, aRange, percentageBasis,
   6236                       aConstraint);
   6237 
   6238  // Neither 12.6 nor 12.7 will occur under min content constraint.
   6239  if (aConstraint != SizingConstraint::MinContent) {
   6240    nscoord freeSpace = aContentBoxSize;
   6241    if (freeSpace != NS_UNCONSTRAINEDSIZE) {
   6242      freeSpace -= SumOfGridGaps();
   6243    }
   6244    // 12.6 maximize tracks by distributing free space.
   6245    // https://drafts.csswg.org/css-grid-2/#algo-grow-tracks
   6246    DistributeFreeSpace(freeSpace);
   6247 
   6248    // 12.7 Expand flexible tracks.
   6249    // https://drafts.csswg.org/css-grid-2/#algo-flex-tracks
   6250    StretchFlexibleTracks(aGridRI, aGridItems, aFunctions, freeSpace);
   6251  }
   6252 }
   6253 
   6254 TrackSize::StateBits nsGridContainerFrame::Tracks::StateBitsForRange(
   6255    const LineRange& aRange) const {
   6256  MOZ_ASSERT(!aRange.IsAuto(), "must have a definite range");
   6257  TrackSize::StateBits state = TrackSize::StateBits{0};
   6258  for (auto i : aRange.Range()) {
   6259    state |= mSizes[i].mState;
   6260  }
   6261  return state;
   6262 }
   6263 
   6264 static void AddSubgridContribution(TrackSize& aSize,
   6265                                   nscoord aMarginBorderPadding) {
   6266  if (aSize.mState & TrackSize::eIntrinsicMinSizing) {
   6267    aSize.mBase = std::max(aSize.mBase, aMarginBorderPadding);
   6268    aSize.mLimit = std::max(aSize.mLimit, aSize.mBase);
   6269  }
   6270  // XXX maybe eFlexMaxSizing too?
   6271  // (once we implement https://github.com/w3c/csswg-drafts/issues/2177)
   6272  if (aSize.mState &
   6273      (TrackSize::eIntrinsicMaxSizing | TrackSize::eApplyFitContentClamping)) {
   6274    aSize.mLimit = std::max(aSize.mLimit, aMarginBorderPadding);
   6275  }
   6276 }
   6277 
   6278 Maybe<nscoord> nsGridContainerFrame::Tracks::ComputeMinSizeClamp(
   6279    const TrackSizingFunctions& aFunctions, nscoord aPercentageBasis,
   6280    const LineRange& aLineRange, const TrackSize::StateBits aState) const {
   6281  if (!TrackSize::IsDefiniteMaxSizing(aState)) {
   6282    return Nothing();
   6283  }
   6284  nscoord minSizeClamp = 0;
   6285  for (auto i : aLineRange.Range()) {
   6286    minSizeClamp +=
   6287        aFunctions.MaxSizingFor(i).AsBreadth().Resolve(aPercentageBasis);
   6288  }
   6289  minSizeClamp += mGridGap * (aLineRange.Extent() - 1);
   6290  return Some(minSizeClamp);
   6291 }
   6292 
   6293 void nsGridContainerFrame::Tracks::ResolveIntrinsicSizeForNonSpanningItems(
   6294    GridReflowInput& aGridRI, const TrackSizingFunctions& aFunctions,
   6295    nscoord aPercentageBasis, SizingConstraint aConstraint,
   6296    const LineRange& aRange, const GridItemInfo& aGridItem) {
   6297  // Calculate track sizes for fit non-spanning items.
   6298  // https://drafts.csswg.org/css-grid-2/#algo-single-span-items
   6299  CachedIntrinsicSizes cache{aGridItem, aGridRI, mAxis};
   6300  TrackSize& sz = mSizes[aRange.mStart];
   6301 
   6302  // Contribution type to use as the base size.
   6303  // This is a Maybe as we might not need to calculate a contribution at all,
   6304  // for instance if the base sizing function is a definite length.
   6305  Maybe<GridIntrinsicSizeType> baseSizeType;
   6306  if (sz.mState & TrackSize::eAutoMinSizing) {
   6307    // "For auto minimums:"
   6308    // "If the track has an 'auto' min track sizing function and the grid
   6309    // container is being sized under a min-/max-content constraint, set
   6310    // the track's base size to the maximum of its items' limited
   6311    // min-content contributions"
   6312    if (aGridItem.MinContributionDependsOnAutoMinSize(aGridRI.mWM, mAxis)) {
   6313      // Clamp it if it's spanning a definite track max-sizing function.
   6314      if (const Maybe<nscoord> minSizeClamp =
   6315              ComputeMinSizeClamp(aFunctions, aPercentageBasis, aRange)) {
   6316        cache.mMinSizeClamp = *minSizeClamp;
   6317        aGridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
   6318      }
   6319      // Use the content-based contribution.
   6320      baseSizeType.emplace((aConstraint == SizingConstraint::MaxContent)
   6321                               ? GridIntrinsicSizeType::MaxContentContribution
   6322                               : GridIntrinsicSizeType::MinContentContribution);
   6323    } else {
   6324      // Use the minimum contribution.
   6325      // Note that this could still become MinContentContribution in practice.
   6326      // MinContributionDependsOnAutoMinSize can return false when the item's
   6327      // size depends on the size of its containing block. In that case, using
   6328      // EnsureContributions to compute MinSize will instead compute
   6329      // MinContentContribution, which will then be placed in the MinSize
   6330      // slot on the cache.
   6331      baseSizeType.emplace(GridIntrinsicSizeType::MinContribution);
   6332    }
   6333  } else if (sz.mState & TrackSize::eMinContentMinSizing) {
   6334    // "For min-content minimums:"
   6335    // "If the track has a 'min-content' min track sizing function, set its
   6336    // base size to the maximum of the items' min-content contributions"
   6337    baseSizeType.emplace(GridIntrinsicSizeType::MinContentContribution);
   6338  } else if (sz.mState & TrackSize::eMaxContentMinSizing) {
   6339    // "For max-content minimums:"
   6340    // "If the track has a 'max-content' min track sizing function, set its
   6341    // base size to the maximum of the items' max-content contributions"
   6342    baseSizeType.emplace(GridIntrinsicSizeType::MaxContentContribution);
   6343  }
   6344 
   6345  // Size of fit-content maximum, if any.
   6346  Maybe<nscoord> fitContentClamp;
   6347  // Contribution type to use as the growth limit.
   6348  // This is a Maybe as we might not need to calculate a contribution at all,
   6349  // for instance if the growth limit sizing function is a definite length.
   6350  Maybe<GridIntrinsicSizeType> limitType;
   6351  if (sz.mState & TrackSize::eMinContentMaxSizing) {
   6352    // "For min-content maximums:"
   6353    // "If the track has a 'min-content' max track sizing function, set its
   6354    // growth limit to the maximum of the items' min-content contributions"
   6355    limitType.emplace(GridIntrinsicSizeType::MinContentContribution);
   6356  } else if (sz.mState &
   6357             (TrackSize::eAutoMaxSizing | TrackSize::eMaxContentMaxSizing)) {
   6358    // "For max-content maximums:"
   6359    // "If the track has a 'max-content' max track sizing function, set its
   6360    // growth limit to the maximum of the items' max-content contributions"
   6361    limitType.emplace(GridIntrinsicSizeType::MaxContentContribution);
   6362    if (MOZ_UNLIKELY(sz.mState & TrackSize::eApplyFitContentClamping)) {
   6363      // "For fit-content() maximums, furthermore clamp this growth limit by
   6364      // the fit-content() argument."
   6365      fitContentClamp.emplace(aFunctions.SizingFor(aRange.mStart)
   6366                                  .AsFitContent()
   6367                                  .AsBreadth()
   6368                                  .Resolve(aPercentageBasis));
   6369    }
   6370  }
   6371 
   6372  // Even if it was possible to use the minimum contribution as the limit in
   6373  // the spec, this could get trashed by the checks for whether the item's auto
   6374  // minimum size depends on the size implemented in
   6375  // GridItemInfo::MinContributionDependsOnAutoMinSize and
   6376  // CachedIntrinsicSizes::EnsureContributions.
   6377  MOZ_ASSERT(
   6378      limitType != Some(GridIntrinsicSizeType::MinContribution),
   6379      "We should never be using the minimum contribution as the limit size.");
   6380 
   6381  // Accumulate the required size types and compute the contributions.
   6382  {
   6383    EnumSet<GridIntrinsicSizeType> sizeTypesToCalculate;
   6384    for (const auto& maybeType : {baseSizeType, limitType}) {
   6385      if (maybeType) {
   6386        sizeTypesToCalculate += *maybeType;
   6387      }
   6388    }
   6389    cache.EnsureContributions(sizeTypesToCalculate, aGridItem, aGridRI, mAxis);
   6390  }
   6391 
   6392  if (baseSizeType) {
   6393    sz.mBase = std::max(sz.mBase, *cache.mSizes[*baseSizeType]);
   6394  }
   6395 
   6396  // Limit based on max size type.
   6397  if (limitType) {
   6398    if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
   6399      sz.mLimit = 0;  // Use only the contribution instead.
   6400    }
   6401    sz.mLimit = std::max(sz.mLimit, *cache.mSizes[*limitType]);
   6402    if (fitContentClamp) {
   6403      // "furthermore clamp this growth limit by the fit-content() argument."
   6404      sz.mLimit = std::min(sz.mLimit, *fitContentClamp);
   6405    }
   6406  }
   6407 
   6408  // "In all cases, if a track's growth limit is now less than its base size,
   6409  // increase the growth limit to match the base size."
   6410  sz.mLimit = std::max(sz.mLimit, sz.mBase);
   6411 }
   6412 
   6413 void nsGridContainerFrame::Tracks::CalculateItemBaselines(
   6414    nsTArray<ItemBaselineData>& aBaselineItems,
   6415    BaselineSharingGroup aBaselineGroup) {
   6416  if (aBaselineItems.IsEmpty()) {
   6417    return;
   6418  }
   6419 
   6420  // Sort the collected items on their baseline track.
   6421  std::sort(aBaselineItems.begin(), aBaselineItems.end(),
   6422            ItemBaselineData::IsBaselineTrackLessThan);
   6423 
   6424  MOZ_ASSERT(mSizes.Length() > 0, "having an item implies at least one track");
   6425 
   6426  // Make sure we have enough space to store the baselines. Use the highest
   6427  // track number (+1 to account for the 0 based indexing).
   6428  auto baselineCount = aBaselineItems.LastElement().mBaselineTrack + 1;
   6429  mBaselines.EnsureLengthAtLeast(baselineCount);
   6430 
   6431  nscoord maxBaseline = 0;
   6432  nscoord maxDescent = 0;
   6433  uint32_t currentTrack = kAutoLine;  // guaranteed to not match any item
   6434  uint32_t trackStartIndex = 0;
   6435  for (uint32_t i = 0, len = aBaselineItems.Length(); true; ++i) {
   6436    // Find the maximum baseline and descent in the current track.
   6437    if (i != len) {
   6438      const ItemBaselineData& item = aBaselineItems[i];
   6439      if (currentTrack == item.mBaselineTrack) {
   6440        maxBaseline = std::max(maxBaseline, item.mBaseline);
   6441        maxDescent = std::max(maxDescent, item.mSize - item.mBaseline);
   6442        continue;
   6443      }
   6444    }
   6445    // Iterate the current track again and update the baseline offsets making
   6446    // all items baseline-aligned within this group in this track.
   6447    for (uint32_t j = trackStartIndex; j < i; ++j) {
   6448      const ItemBaselineData& item = aBaselineItems[j];
   6449      item.mGridItem->mBaselineOffset[mAxis] = maxBaseline - item.mBaseline;
   6450      MOZ_ASSERT(item.mGridItem->mBaselineOffset[mAxis] >= 0);
   6451    }
   6452    if (i != 0) {
   6453      // Store the size of this baseline-aligned subtree.
   6454      mSizes[currentTrack].mBaselineSubtreeSize[aBaselineGroup] =
   6455          maxBaseline + maxDescent;
   6456 
   6457      // Record the baseline for the current track.
   6458      mBaselines[currentTrack][aBaselineGroup] = Some(maxBaseline);
   6459    }
   6460    if (i == len) {
   6461      break;
   6462    }
   6463    // Initialize data for the next track with baseline-aligned items.
   6464    const ItemBaselineData& item = aBaselineItems[i];
   6465    currentTrack = item.mBaselineTrack;
   6466    trackStartIndex = i;
   6467    maxBaseline = item.mBaseline;
   6468    maxDescent = item.mSize - item.mBaseline;
   6469  }
   6470 }
   6471 
   6472 void nsGridContainerFrame::Tracks::InitializeItemBaselines(
   6473    GridReflowInput& aGridRI, nsTArray<GridItemInfo>& aGridItems) {
   6474  MOZ_ASSERT(!mIsMasonry);
   6475  if (aGridRI.mFrame->IsSubgrid(mAxis)) {
   6476    // A grid container's subgridded axis doesn't have a baseline.
   6477    return;
   6478  }
   6479 
   6480  nsTArray<ItemBaselineData> firstBaselineItems;
   6481  nsTArray<ItemBaselineData> lastBaselineItems;
   6482  const WritingMode containerWM = aGridRI.mWM;
   6483  ComputedStyle* containerStyle = aGridRI.mFrame->Style();
   6484 
   6485  for (GridItemInfo& gridItem : aGridItems) {
   6486    if (gridItem.IsSubgrid(mAxis)) {
   6487      // A subgrid itself is never baseline-aligned.
   6488      continue;
   6489    }
   6490 
   6491    nsIFrame* child = gridItem.mFrame;
   6492    uint32_t baselineTrack = kAutoLine;
   6493    auto state = ItemState(0);
   6494    const auto childWM = child->GetWritingMode();
   6495 
   6496    const bool isOrthogonal = containerWM.IsOrthogonalTo(childWM);
   6497    const bool isInlineAxis = mAxis == LogicalAxis::Inline;  // i.e. columns
   6498    const bool itemHasBaselineParallelToTrack = isInlineAxis == isOrthogonal;
   6499 
   6500    // [align|justify]-self:[last ]baseline.
   6501    auto selfAlignment =
   6502        child->StylePosition()->UsedSelfAlignment(mAxis, containerStyle);
   6503    selfAlignment &= ~StyleAlignFlags::FLAG_BITS;
   6504    if (selfAlignment == StyleAlignFlags::BASELINE) {
   6505      state |= ItemState::eFirstBaseline | ItemState::eSelfBaseline;
   6506    } else if (selfAlignment == StyleAlignFlags::LAST_BASELINE) {
   6507      state |= ItemState::eLastBaseline | ItemState::eSelfBaseline;
   6508    }
   6509 
   6510    // https://drafts.csswg.org/css-align-3/#baseline-align-content
   6511    // Baseline content-alignment can only apply if the align-content axis is
   6512    // parallel with the box’s block axis; otherwise the fallback alignment is
   6513    // used.
   6514    if (!isInlineAxis) {
   6515      // Handle align-content:[last ]baseline (if present)
   6516      auto alignContent = child->StylePosition()->mAlignContent.primary;
   6517      alignContent &= ~StyleAlignFlags::FLAG_BITS;
   6518      if (alignContent == StyleAlignFlags::BASELINE ||
   6519          alignContent == StyleAlignFlags::LAST_BASELINE) {
   6520        const auto selfAlignEdge = alignContent == StyleAlignFlags::BASELINE
   6521                                       ? StyleAlignFlags::SELF_START
   6522                                       : StyleAlignFlags::SELF_END;
   6523        bool validCombo = selfAlignment == StyleAlignFlags::NORMAL ||
   6524                          selfAlignment == StyleAlignFlags::STRETCH ||
   6525                          selfAlignment == selfAlignEdge;
   6526        if (!validCombo) {
   6527          // We're doing alignment in the axis that's orthogonal to mAxis here.
   6528          LogicalAxis alignAxis = GetOrthogonalAxis(mAxis);
   6529          // |sameSide| is true if the container's start side in this axis is
   6530          // the same as the child's start side, in the child's parallel axis.
   6531          bool sameSide =
   6532              containerWM.ParallelAxisStartsOnSameSide(alignAxis, childWM);
   6533          if (selfAlignment == StyleAlignFlags::LEFT) {
   6534            selfAlignment = containerWM.IsBidiLTR() ? StyleAlignFlags::START
   6535                                                    : StyleAlignFlags::END;
   6536          } else if (selfAlignment == StyleAlignFlags::RIGHT) {
   6537            selfAlignment = StyleAlignFlags::START;
   6538          }
   6539 
   6540          if (selfAlignment == StyleAlignFlags::START ||
   6541              selfAlignment == StyleAlignFlags::FLEX_START) {
   6542            validCombo =
   6543                sameSide == (alignContent == StyleAlignFlags::BASELINE);
   6544          } else if (selfAlignment == StyleAlignFlags::END ||
   6545                     selfAlignment == StyleAlignFlags::FLEX_END) {
   6546            validCombo =
   6547                sameSide == (alignContent == StyleAlignFlags::LAST_BASELINE);
   6548          }
   6549        }
   6550        if (validCombo) {
   6551          const GridArea& area = gridItem.mArea;
   6552          if (alignContent == StyleAlignFlags::BASELINE) {
   6553            state |= ItemState::eFirstBaseline | ItemState::eContentBaseline;
   6554            baselineTrack = area.mRows.mStart;
   6555          } else if (alignContent == StyleAlignFlags::LAST_BASELINE) {
   6556            state |= ItemState::eLastBaseline | ItemState::eContentBaseline;
   6557            baselineTrack = area.mRows.mEnd - 1;
   6558          }
   6559        }
   6560      }
   6561    }
   6562 
   6563    if (state & ItemState::eIsBaselineAligned) {
   6564      // The item is baseline aligned, so calculate the baseline sharing group.
   6565      // <https://drafts.csswg.org/css-align-3/#baseline-terms>
   6566      bool isFirstBaseline = (state & ItemState::eFirstBaseline) != 0;
   6567      BaselineSharingGroup baselineAlignment = isFirstBaseline
   6568                                                   ? BaselineSharingGroup::First
   6569                                                   : BaselineSharingGroup::Last;
   6570      // Baseline alignment occurs along `mAxis`, but baselines are defined in
   6571      // the orthogonal axis (the axis of the baseline context that defines the
   6572      // baseline sharing group).
   6573      auto baselineWM = WritingMode::DetermineWritingModeForBaselineSynthesis(
   6574          containerWM, childWM, GetOrthogonalAxis(mAxis));
   6575 
   6576      auto sameSideInBaselineWM =
   6577          containerWM.ParallelAxisStartsOnSameSide(mAxis, baselineWM);
   6578      auto baselineSharingGroup = BaselineSharingGroup::First;
   6579      if (sameSideInBaselineWM != isFirstBaseline) {
   6580        baselineSharingGroup = BaselineSharingGroup::Last;
   6581        state |= ItemState::eLastBaselineSharingGroup;
   6582 
   6583        baselineTrack = (isInlineAxis ? gridItem.mArea.mCols.mEnd
   6584                                      : gridItem.mArea.mRows.mEnd) -
   6585                        1;
   6586      } else {
   6587        baselineTrack = isInlineAxis ? gridItem.mArea.mCols.mStart
   6588                                     : gridItem.mArea.mRows.mStart;
   6589      }
   6590 
   6591      // XXXmats if |child| is a descendant of a subgrid then the metrics
   6592      // below needs to account for the accumulated MPB somehow...
   6593 
   6594      auto* rc = &aGridRI.mRenderingContext;
   6595      // XXX figure out if we can avoid/merge this reflow with the main reflow.
   6596      // XXX (after bug 1174569 is sorted out)
   6597      //
   6598      // XXX How should we handle percentage padding here? (bug 1330866)
   6599      // XXX (see ::ContentContribution and how it deals with percentages)
   6600      // XXX What if the true baseline after line-breaking differs from this
   6601      // XXX hypothetical baseline based on an infinite inline size?
   6602      // XXX Maybe we should just call ::ContentContribution here instead?
   6603      const LogicalSize cbSize = aGridRI.PercentageBasisFor(mAxis, gridItem);
   6604      LogicalSize avail(childWM, INFINITE_ISIZE_COORD, NS_UNCONSTRAINEDSIZE);
   6605      const LogicalAxis inlineAxisInChildWM =
   6606          isOrthogonal ? LogicalAxis::Block : LogicalAxis::Inline;
   6607      const nscoord colSize = cbSize.Size(inlineAxisInChildWM, childWM);
   6608      if (colSize != NS_UNCONSTRAINEDSIZE) {
   6609        avail.Size(inlineAxisInChildWM, childWM) = colSize;
   6610      }
   6611      ::MeasuringReflow(child, aGridRI.mReflowInput, rc, avail, cbSize);
   6612 
   6613      nsGridContainerFrame* grid = do_QueryFrame(child);
   6614      auto frameSize =
   6615          isInlineAxis ? child->ISize(containerWM) : child->BSize(containerWM);
   6616      auto margin = child->GetLogicalUsedMargin(containerWM);
   6617      auto alignSize =
   6618          frameSize + (isInlineAxis ? margin.IStartEnd(containerWM)
   6619                                    : margin.BStartEnd(containerWM));
   6620 
   6621      Maybe<nscoord> baseline;
   6622      if (grid) {
   6623        baseline.emplace((isOrthogonal == isInlineAxis)
   6624                             ? grid->GetBBaseline(baselineAlignment)
   6625                             : grid->GetIBaseline(baselineAlignment));
   6626      } else {
   6627        if (itemHasBaselineParallelToTrack) {
   6628          baseline = child->GetNaturalBaselineBOffset(
   6629              childWM, baselineAlignment, BaselineExportContext::Other);
   6630        }
   6631 
   6632        if (!baseline) {
   6633          // If baseline alignment is specified on a grid item whose size in
   6634          // that axis depends on the size of an intrinsically-sized track, that
   6635          // item does not participate in baseline alignment, and instead uses
   6636          // its fallback alignment as if that were originally specified.
   6637          // https://drafts.csswg.org/css-grid-2/#row-align
   6638 
   6639          // Check if the item crosses any tracks that are intrinsically sized.
   6640          auto range = gridItem.mArea.LineRangeForAxis(mAxis).Range();
   6641          auto isTrackAutoSize =
   6642              std::find_if(range.begin(), range.end(), [&](auto track) {
   6643                constexpr auto intrinsicSizeFlags =
   6644                    TrackSize::eIntrinsicMinSizing |
   6645                    TrackSize::eIntrinsicMaxSizing |
   6646                    TrackSize::eApplyFitContentClamping |
   6647                    TrackSize::eFlexMaxSizing;
   6648                return (mSizes[track].mState & intrinsicSizeFlags) != 0;
   6649              }) != range.end();
   6650 
   6651          // If either the track or the item is not auto sized, then the item
   6652          // participates in baseline alignment.
   6653          if (!isTrackAutoSize ||
   6654              !gridItem.IsBSizeDependentOnContainerSize(containerWM)) {
   6655            // We're synthesizing the baseline from the child's border-box
   6656            // (frameSize is the size of the border-box). See:
   6657            // https://drafts.csswg.org/css-align-3/#baseline-export.
   6658 
   6659            if (containerWM.IsCentralBaseline()) {
   6660              // We want to use the exact same central position within the
   6661              // frame, regardless of which side we're measuring from. To
   6662              // achieve that, we round *up* if we're in the first baseline
   6663              // sharing group, and *down* if we're in the last baseline sharing
   6664              // group.
   6665              const bool isFirstBaselineSharingGroup =
   6666                  baselineSharingGroup == BaselineSharingGroup::First;
   6667              baseline.emplace(frameSize / 2 + (isFirstBaselineSharingGroup
   6668                                                    ? 0
   6669                                                    : frameSize % 2));
   6670            } else {
   6671              // The baseline offset is measured from the block-{start,end} edge
   6672              // of the container, using the block axis of 'baselineWM' (which
   6673              // may differ from the child or container’s writing mode).
   6674              //
   6675              // If we're synthesizing a baseline from the edge nearest to the
   6676              // container's reference side (start for the first baseline group,
   6677              // end for the last), the offset is `0`. Otherwise, it's from the
   6678              // opposite edge, so we use `frameSize`.
   6679              //
   6680              // This logic depends on whether we're in the first or last
   6681              // baseline-sharing group, and whether the line is inverted (e.g.,
   6682              // in vertical-rl mode), which affects which edge is considered
   6683              // the "start" or "end".
   6684              baseline.emplace((isFirstBaseline == baselineWM.IsLineInverted())
   6685                                   ? 0
   6686                                   : frameSize);
   6687            }
   6688          }
   6689        }
   6690      }
   6691 
   6692      if (baseline) {
   6693        nscoord finalBaseline = *baseline;
   6694        NS_ASSERTION(finalBaseline != NS_INTRINSIC_ISIZE_UNKNOWN,
   6695                     "about to use an unknown baseline");
   6696 
   6697        nscoord marginAdjust = 0;
   6698        if (baselineSharingGroup == BaselineSharingGroup::First) {
   6699          marginAdjust = isInlineAxis ? margin.IStart(containerWM)
   6700                                      : margin.BStart(containerWM);
   6701        } else {
   6702          marginAdjust = isInlineAxis ? margin.IEnd(containerWM)
   6703                                      : margin.BEnd(containerWM);
   6704 
   6705          // This flag is used in ::AlignSelf(...) to check whether the item is
   6706          // last baseline aligned, but this flag should go away.
   6707          state |= GridItemInfo::eEndSideBaseline;
   6708        }
   6709        finalBaseline += marginAdjust;
   6710 
   6711        auto& baselineItems =
   6712            (baselineSharingGroup == BaselineSharingGroup::First)
   6713                ? firstBaselineItems
   6714                : lastBaselineItems;
   6715        baselineItems.AppendElement(ItemBaselineData{
   6716            baselineTrack, finalBaseline, alignSize, &gridItem});
   6717      } else {
   6718        state &= ~ItemState::eAllBaselineBits;
   6719      }
   6720    }
   6721 
   6722    MOZ_ASSERT(
   6723        (state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) !=
   6724            (ItemState::eFirstBaseline | ItemState::eLastBaseline),
   6725        "first/last baseline bits are mutually exclusive");
   6726    MOZ_ASSERT(
   6727        (state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)) !=
   6728            (ItemState::eSelfBaseline | ItemState::eContentBaseline),
   6729        "*-self and *-content baseline bits are mutually exclusive");
   6730    MOZ_ASSERT(
   6731        !(state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) ==
   6732            !(state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)),
   6733        "first/last bit requires self/content bit and vice versa");
   6734 
   6735    gridItem.mState[mAxis] |= state;
   6736    gridItem.mBaselineOffset[mAxis] = nscoord(0);
   6737  }
   6738 
   6739  if (firstBaselineItems.IsEmpty() && lastBaselineItems.IsEmpty()) {
   6740    return;
   6741  }
   6742 
   6743  // TODO: CSS Align spec issue - how to align a baseline subtree in a track?
   6744  // https://lists.w3.org/Archives/Public/www-style/2016May/0141.html
   6745  mBaselineSubtreeAlign[BaselineSharingGroup::First] = StyleAlignFlags::START;
   6746  mBaselineSubtreeAlign[BaselineSharingGroup::Last] = StyleAlignFlags::END;
   6747 
   6748  CalculateItemBaselines(firstBaselineItems, BaselineSharingGroup::First);
   6749  CalculateItemBaselines(lastBaselineItems, BaselineSharingGroup::Last);
   6750 }
   6751 
   6752 // TODO: we store the wrong baseline group offset in some cases (bug 1632200)
   6753 void nsGridContainerFrame::Tracks::InitializeItemBaselinesInMasonryAxis(
   6754    GridReflowInput& aGridRI, nsTArray<GridItemInfo>& aGridItems,
   6755    BaselineAlignmentSet aSet, const nsSize& aContainerSize,
   6756    nsTArray<nscoord>& aTrackSizes,
   6757    nsTArray<ItemBaselineData>& aFirstBaselineItems,
   6758    nsTArray<ItemBaselineData>& aLastBaselineItems) {
   6759  MOZ_ASSERT(mIsMasonry);
   6760  WritingMode wm = aGridRI.mWM;
   6761  ComputedStyle* containerSC = aGridRI.mFrame->Style();
   6762  for (GridItemInfo& gridItem : aGridItems) {
   6763    if (gridItem.IsSubgrid(mAxis)) {
   6764      // A subgrid itself is never baseline-aligned.
   6765      continue;
   6766    }
   6767    const auto& area = gridItem.mArea;
   6768    if (aSet.mItemSet == BaselineAlignmentSet::LastItems) {
   6769      // NOTE: eIsLastItemInMasonryTrack is set also if the item is the ONLY
   6770      // item in its track; the eIsBaselineAligned check excludes it though
   6771      // since it participates in the start baseline groups in that case.
   6772      //
   6773      // XXX what if it's the only item in THAT baseline group?
   6774      // XXX should it participate in the last-item group instead then
   6775      // if there are more baseline-aligned items there?
   6776      if (!(gridItem.mState[mAxis] & ItemState::eIsLastItemInMasonryTrack) ||
   6777          (gridItem.mState[mAxis] & ItemState::eIsBaselineAligned)) {
   6778        continue;
   6779      }
   6780    } else {
   6781      if (area.LineRangeForAxis(mAxis).mStart > 0 ||
   6782          (gridItem.mState[mAxis] & ItemState::eIsBaselineAligned)) {
   6783        continue;
   6784      }
   6785    }
   6786    if (!aSet.MatchTrackAlignment(StyleAlignFlags::START)) {
   6787      continue;
   6788    }
   6789 
   6790    nsIFrame* child = gridItem.mFrame;
   6791    uint32_t baselineTrack = kAutoLine;
   6792    auto state = ItemState(0);
   6793    auto childWM = child->GetWritingMode();
   6794    const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
   6795    const bool isInlineAxis = mAxis == LogicalAxis::Inline;  // i.e. columns
   6796    // XXX update the line below to include orthogonal grid/table boxes
   6797    // XXX since they have baselines in both dimensions. And flexbox with
   6798    // XXX reversed main/cross axis?
   6799    const bool itemHasBaselineParallelToTrack = isInlineAxis == isOrthogonal;
   6800    if (itemHasBaselineParallelToTrack) {
   6801      const auto* pos = child->StylePosition();
   6802      // [align|justify]-self:[last ]baseline.
   6803      auto selfAlignment = pos->UsedSelfAlignment(mAxis, containerSC);
   6804      selfAlignment &= ~StyleAlignFlags::FLAG_BITS;
   6805      if (selfAlignment == StyleAlignFlags::BASELINE) {
   6806        state |= ItemState::eFirstBaseline | ItemState::eSelfBaseline;
   6807        baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
   6808      } else if (selfAlignment == StyleAlignFlags::LAST_BASELINE) {
   6809        state |= ItemState::eLastBaseline | ItemState::eSelfBaseline;
   6810        baselineTrack = (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
   6811      } else {
   6812        // [align|justify]-content:[last ]baseline.
   6813        auto childAxis = isOrthogonal ? GetOrthogonalAxis(mAxis) : mAxis;
   6814        auto alignContent = pos->UsedContentAlignment(childAxis).primary;
   6815        alignContent &= ~StyleAlignFlags::FLAG_BITS;
   6816        if (alignContent == StyleAlignFlags::BASELINE) {
   6817          state |= ItemState::eFirstBaseline | ItemState::eContentBaseline;
   6818          baselineTrack = isInlineAxis ? area.mCols.mStart : area.mRows.mStart;
   6819        } else if (alignContent == StyleAlignFlags::LAST_BASELINE) {
   6820          state |= ItemState::eLastBaseline | ItemState::eContentBaseline;
   6821          baselineTrack =
   6822              (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
   6823        }
   6824      }
   6825    }
   6826 
   6827    if (state & ItemState::eIsBaselineAligned) {
   6828      // XXXmats if |child| is a descendant of a subgrid then the metrics
   6829      // below needs to account for the accumulated MPB somehow...
   6830 
   6831      nscoord baseline;
   6832      nsGridContainerFrame* grid = do_QueryFrame(child);
   6833      if (state & ItemState::eFirstBaseline) {
   6834        if (grid) {
   6835          if (isOrthogonal == isInlineAxis) {
   6836            baseline = grid->GetBBaseline(BaselineSharingGroup::First);
   6837          } else {
   6838            baseline = grid->GetIBaseline(BaselineSharingGroup::First);
   6839          }
   6840        }
   6841        if (grid || nsLayoutUtils::GetFirstLineBaseline(wm, child, &baseline)) {
   6842          NS_ASSERTION(baseline != NS_INTRINSIC_ISIZE_UNKNOWN,
   6843                       "about to use an unknown baseline");
   6844          auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
   6845          nscoord alignSize;
   6846          LogicalPoint pos =
   6847              child->GetLogicalNormalPosition(wm, aContainerSize);
   6848          baseline += pos.Pos(mAxis, wm);
   6849          if (aSet.mTrackAlignmentSet == BaselineAlignmentSet::EndStretch) {
   6850            state |= ItemState::eEndSideBaseline;
   6851            // Convert to distance from the track end.
   6852            baseline =
   6853                aTrackSizes[gridItem.mArea
   6854                                .LineRangeForAxis(GetOrthogonalAxis(mAxis))
   6855                                .mStart] -
   6856                baseline;
   6857          }
   6858          alignSize = frameSize;
   6859          aFirstBaselineItems.AppendElement(ItemBaselineData(
   6860              {baselineTrack, baseline, alignSize, &gridItem}));
   6861        } else {
   6862          state &= ~ItemState::eAllBaselineBits;
   6863        }
   6864      } else {
   6865        if (grid) {
   6866          if (isOrthogonal == isInlineAxis) {
   6867            baseline = grid->GetBBaseline(BaselineSharingGroup::Last);
   6868          } else {
   6869            baseline = grid->GetIBaseline(BaselineSharingGroup::Last);
   6870          }
   6871        }
   6872        if (grid || nsLayoutUtils::GetLastLineBaseline(wm, child, &baseline)) {
   6873          NS_ASSERTION(baseline != NS_INTRINSIC_ISIZE_UNKNOWN,
   6874                       "about to use an unknown baseline");
   6875          auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
   6876          auto m = child->GetLogicalUsedMargin(wm);
   6877          if (!grid &&
   6878              aSet.mTrackAlignmentSet == BaselineAlignmentSet::EndStretch) {
   6879            // Convert to distance from border-box end.
   6880            state |= ItemState::eEndSideBaseline;
   6881            LogicalPoint pos =
   6882                child->GetLogicalNormalPosition(wm, aContainerSize);
   6883            baseline += pos.Pos(mAxis, wm);
   6884            baseline =
   6885                aTrackSizes[gridItem.mArea
   6886                                .LineRangeForAxis(GetOrthogonalAxis(mAxis))
   6887                                .mStart] -
   6888                baseline;
   6889          } else if (grid && aSet.mTrackAlignmentSet ==
   6890                                 BaselineAlignmentSet::StartStretch) {
   6891            // Convert to distance from border-box start.
   6892            baseline = frameSize - baseline;
   6893          }
   6894          if (aSet.mItemSet == BaselineAlignmentSet::LastItems &&
   6895              aSet.mTrackAlignmentSet == BaselineAlignmentSet::StartStretch) {
   6896            LogicalPoint pos =
   6897                child->GetLogicalNormalPosition(wm, aContainerSize);
   6898            baseline += pos.B(wm);
   6899          }
   6900          if (aSet.mTrackAlignmentSet == BaselineAlignmentSet::EndStretch) {
   6901            state |= ItemState::eEndSideBaseline;
   6902          }
   6903          auto descent =
   6904              baseline + ((state & ItemState::eEndSideBaseline)
   6905                              ? (isInlineAxis ? m.IEnd(wm) : m.BEnd(wm))
   6906                              : (isInlineAxis ? m.IStart(wm) : m.BStart(wm)));
   6907          auto alignSize =
   6908              frameSize + (isInlineAxis ? m.IStartEnd(wm) : m.BStartEnd(wm));
   6909          aLastBaselineItems.AppendElement(
   6910              ItemBaselineData({baselineTrack, descent, alignSize, &gridItem}));
   6911        } else {
   6912          state &= ~ItemState::eAllBaselineBits;
   6913        }
   6914      }
   6915    }
   6916    MOZ_ASSERT(
   6917        (state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) !=
   6918            (ItemState::eFirstBaseline | ItemState::eLastBaseline),
   6919        "first/last baseline bits are mutually exclusive");
   6920    MOZ_ASSERT(
   6921        (state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)) !=
   6922            (ItemState::eSelfBaseline | ItemState::eContentBaseline),
   6923        "*-self and *-content baseline bits are mutually exclusive");
   6924    MOZ_ASSERT(
   6925        !(state & (ItemState::eFirstBaseline | ItemState::eLastBaseline)) ==
   6926            !(state & (ItemState::eSelfBaseline | ItemState::eContentBaseline)),
   6927        "first/last bit requires self/content bit and vice versa");
   6928    gridItem.mState[mAxis] |= state;
   6929    gridItem.mBaselineOffset[mAxis] = nscoord(0);
   6930  }
   6931 
   6932  CalculateItemBaselines(aFirstBaselineItems, BaselineSharingGroup::First);
   6933  CalculateItemBaselines(aLastBaselineItems, BaselineSharingGroup::Last);
   6934 
   6935  // TODO: make sure the mBaselines (i.e. the baselines we export from
   6936  // the grid container) are offset from the correct container edge.
   6937  // Also, which of the baselines do we pick to export exactly?
   6938 
   6939  MOZ_ASSERT(aFirstBaselineItems.Length() != 1 ||
   6940                 aFirstBaselineItems[0].mGridItem->mBaselineOffset[mAxis] == 0,
   6941             "a baseline group that contains only one item should not "
   6942             "produce a non-zero item baseline offset");
   6943  MOZ_ASSERT(aLastBaselineItems.Length() != 1 ||
   6944                 aLastBaselineItems[0].mGridItem->mBaselineOffset[mAxis] == 0,
   6945             "a baseline group that contains only one item should not "
   6946             "produce a non-zero item baseline offset");
   6947 }
   6948 
   6949 void nsGridContainerFrame::Tracks::AlignBaselineSubtree(
   6950    const GridItemInfo& aGridItem) const {
   6951  if (mIsMasonry) {
   6952    return;
   6953  }
   6954  auto state = aGridItem.mState[mAxis];
   6955  if (!(state & ItemState::eIsBaselineAligned)) {
   6956    return;
   6957  }
   6958  const GridArea& area = aGridItem.mArea;
   6959  int32_t baselineTrack;
   6960  const bool isFirstBaseline = state & ItemState::eFirstBaseline;
   6961  if (isFirstBaseline) {
   6962    baselineTrack =
   6963        mAxis == LogicalAxis::Block ? area.mRows.mStart : area.mCols.mStart;
   6964  } else {
   6965    baselineTrack =
   6966        (mAxis == LogicalAxis::Block ? area.mRows.mEnd : area.mCols.mEnd) - 1;
   6967  }
   6968  const TrackSize& sz = mSizes[baselineTrack];
   6969  auto baselineGroup = isFirstBaseline ? BaselineSharingGroup::First
   6970                                       : BaselineSharingGroup::Last;
   6971  nscoord delta = sz.mBase - sz.mBaselineSubtreeSize[baselineGroup];
   6972  const auto subtreeAlign = mBaselineSubtreeAlign[baselineGroup];
   6973  if (subtreeAlign == StyleAlignFlags::START) {
   6974    if (state & ItemState::eLastBaseline) {
   6975      aGridItem.mBaselineOffset[mAxis] += delta;
   6976    }
   6977  } else if (subtreeAlign == StyleAlignFlags::END) {
   6978    if (isFirstBaseline) {
   6979      aGridItem.mBaselineOffset[mAxis] += delta;
   6980    }
   6981  } else if (subtreeAlign == StyleAlignFlags::CENTER) {
   6982    aGridItem.mBaselineOffset[mAxis] += delta / 2;
   6983  } else {
   6984    MOZ_ASSERT_UNREACHABLE("unexpected baseline subtree alignment");
   6985  }
   6986 }
   6987 
   6988 bool nsGridContainerFrame::Tracks::GrowSizeForSpanningItems(
   6989    TrackSizingStep aStep, TrackSizingPhase aPhase,
   6990    nsTArray<SpanningItemData>::iterator aIter,
   6991    nsTArray<SpanningItemData>::iterator aIterEnd, nsTArray<uint32_t>& aTracks,
   6992    TrackPlan& aTrackPlan, ItemPlan& aItemPlan, SizingConstraint aConstraint,
   6993    bool aIsGridIntrinsicSizing, const TrackSizingFunctions& aFunctions,
   6994    const FitContentClamper& aFitContentClamper,
   6995    bool aNeedInfinitelyGrowableFlag) {
   6996  const bool isMaxSizingPhase = aPhase == TrackSizingPhase::IntrinsicMaximums ||
   6997                                aPhase == TrackSizingPhase::MaxContentMaximums;
   6998  bool needToUpdateSizes = false;
   6999  aTrackPlan.Initialize(aPhase, *this);
   7000  for (; aIter != aIterEnd; ++aIter) {
   7001    const SpanningItemData& item = *aIter;
   7002    if (!(item.mState & SelectorForPhase(aPhase, aConstraint))) {
   7003      continue;
   7004    }
   7005    if (isMaxSizingPhase) {
   7006      for (auto i : item.mLineRange.Range()) {
   7007        aTrackPlan[i].mState |= TrackSize::eModified;
   7008      }
   7009    }
   7010    if (aStep == TrackSizingStep::Flex && aIsGridIntrinsicSizing) {
   7011      // We could only ever grow flex tracks, and when measuring we shouldn't
   7012      // grow flex tracks, so the remaining space will always be zero.
   7013      continue;
   7014    }
   7015    nscoord space = item.SizeContributionForPhase(aPhase);
   7016    if (space <= 0) {
   7017      continue;
   7018    }
   7019    aTracks.ClearAndRetainStorage();
   7020    space = CollectGrowable(aStep, aPhase, space, item.mLineRange, aConstraint,
   7021                            aTracks);
   7022    if (space > 0) {
   7023      DistributeToTrackSizes(aStep, aPhase, space, aTrackPlan, aItemPlan,
   7024                             aTracks, aConstraint, aFunctions,
   7025                             aFitContentClamper);
   7026      needToUpdateSizes = true;
   7027    }
   7028  }
   7029  if (isMaxSizingPhase) {
   7030    needToUpdateSizes = true;
   7031  }
   7032  if (needToUpdateSizes) {
   7033    CopyPlanToSize(aPhase, aTrackPlan, aNeedInfinitelyGrowableFlag);
   7034  }
   7035  return needToUpdateSizes;
   7036 }
   7037 
   7038 void nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
   7039    GridReflowInput& aGridRI, nsTArray<GridItemInfo>& aGridItems,
   7040    const TrackSizingFunctions& aFunctions, LineRange GridArea::* aRange,
   7041    nscoord aPercentageBasis, SizingConstraint aConstraint) {
   7042  // Intrinsic sizing algorithm 12.5 steps 2-5
   7043  // https://drafts.csswg.org/css-grid-2/#algo-content
   7044  //
   7045  // We're also setting eIsFlexing on the item state here to speed up
   7046  // FindUsedFlexFraction later.
   7047 
   7048  // nonFlexSpanningItems has spanning items that do not span any flex tracks.
   7049  // flexSpanningItems has spanning items that span one or more flex tracks.
   7050  nsTArray<SpanningItemData> nonFlexSpanningItems, flexSpanningItems;
   7051  // max span of items in `nonFlexSpanningItems` and `flexSpanningItems`.
   7052  uint32_t maxSpan = 0;
   7053 
   7054  const auto orthogonalAxis = GetOrthogonalAxis(mAxis);
   7055  const bool isMasonryInOtherAxis = aGridRI.mFrame->IsMasonry(orthogonalAxis);
   7056 
   7057  for (auto& gridItem : aGridItems) {
   7058    MOZ_ASSERT(!(gridItem.mState[mAxis] &
   7059                 (ItemState::eContentBasedAutoMinSize | ItemState::eIsFlexing |
   7060                  ItemState::eClampMarginBoxMinSize)),
   7061               "Why are any of these bits set already?");
   7062 
   7063    const GridArea& area = gridItem.mArea;
   7064    const LineRange& lineRange = area.*aRange;
   7065    const TrackSize::StateBits state = StateBitsForRange(lineRange);
   7066    // Set flex sizing flag as soon as possible to ensure
   7067    // MinContributionDependsOnAutoMinSize will function properly.
   7068    if (state & TrackSize::eFlexMaxSizing) {
   7069      gridItem.mState[mAxis] |= ItemState::eIsFlexing;
   7070    }
   7071 
   7072    // If we have masonry layout in the other axis then skip this item unless
   7073    // it's in the first masonry track, or has definite placement in this axis,
   7074    // or spans all tracks in this axis (since that implies it will be placed
   7075    // at line 1 regardless of layout results of other items).
   7076    if (isMasonryInOtherAxis &&
   7077        gridItem.mArea.LineRangeForAxis(orthogonalAxis).mStart != 0 &&
   7078        (gridItem.mState[mAxis] & ItemState::eAutoPlacement) &&
   7079        gridItem.mArea.LineRangeForAxis(mAxis).Extent() != mSizes.Length()) {
   7080      continue;
   7081    }
   7082 
   7083    uint32_t span = lineRange.Extent();
   7084    if (MOZ_UNLIKELY(gridItem.mState[mAxis] & ItemState::eIsSubgrid)) {
   7085      auto itemWM = gridItem.mFrame->GetWritingMode();
   7086      auto percentageBasis = aGridRI.PercentageBasisFor(mAxis, gridItem);
   7087 
   7088      if (percentageBasis.ISize(itemWM) == NS_UNCONSTRAINEDSIZE) {
   7089        percentageBasis.ISize(itemWM) = nscoord(0);
   7090      }
   7091 
   7092      if (percentageBasis.BSize(itemWM) == NS_UNCONSTRAINEDSIZE) {
   7093        percentageBasis.BSize(itemWM) = nscoord(0);
   7094      }
   7095 
   7096      const WritingMode wm = aGridRI.mWM;
   7097      auto* subgrid =
   7098          SubgridComputeMarginBorderPadding(gridItem, percentageBasis);
   7099      LogicalMargin mbp = SubgridAccumulatedMarginBorderPadding(
   7100          gridItem.SubgridFrame(), subgrid, wm, mAxis);
   7101 
   7102      if (span == 1) {
   7103        AddSubgridContribution(mSizes[lineRange.mStart],
   7104                               mbp.StartEnd(mAxis, wm));
   7105      } else {
   7106        AddSubgridContribution(mSizes[lineRange.mStart], mbp.Start(mAxis, wm));
   7107        AddSubgridContribution(mSizes[lineRange.mEnd - 1], mbp.End(mAxis, wm));
   7108      }
   7109      continue;
   7110    }
   7111 
   7112    // Set eContentBasedAutoMinSize if and only if the grid item has
   7113    // content-based automatic minimum size. This is the case if all of the
   7114    // following are true of the item:
   7115    // 1. its computed overflow is not a scrollable overflow value
   7116    // 2. it spans at least one track in that axis whose min track sizing
   7117    // function is auto
   7118    // 3. if it spans more than one track in that axis, none of those tracks
   7119    // are flexible
   7120    // https://drafts.csswg.org/css-grid-2/#min-size-auto
   7121    if (!gridItem.mFrame->StyleDisplay()->IsScrollableOverflow() &&
   7122        state & TrackSize::eAutoMinSizing &&
   7123        (span == 1 || !(state & TrackSize::eFlexMaxSizing))) {
   7124      gridItem.mState[mAxis] |= ItemState::eContentBasedAutoMinSize;
   7125    }
   7126 
   7127    if (span == 1) {
   7128      // Step 2. Size tracks to fit non-spanning items.
   7129      // https://drafts.csswg.org/css-grid-2/#algo-single-span-items
   7130      ResolveIntrinsicSizeForNonSpanningItems(aGridRI, aFunctions,
   7131                                              aPercentageBasis, aConstraint,
   7132                                              lineRange, gridItem);
   7133    } else {
   7134      // Collect information for step 3.
   7135      // https://drafts.csswg.org/css-grid-2/#algo-spanning-items
   7136 
   7137      nsTArray<SpanningItemData>* items;
   7138      if (state & TrackSize::eFlexMaxSizing) {
   7139        // Set eIsFlexing on the item state here to speed up
   7140        // FindUsedFlexFraction later.
   7141        gridItem.mState[mAxis] |= ItemState::eIsFlexing;
   7142        items = &flexSpanningItems;
   7143      } else {
   7144        items = &nonFlexSpanningItems;
   7145      }
   7146 
   7147      if (state &
   7148          (TrackSize::eIntrinsicMinSizing | TrackSize::eIntrinsicMaxSizing)) {
   7149        maxSpan = std::max(maxSpan, span);
   7150        CachedIntrinsicSizes cache{gridItem, aGridRI, mAxis};
   7151 
   7152        // Calculate data for "Automatic Minimum Size" clamping, if needed.
   7153        if (gridItem.mState[mAxis] & ItemState::eContentBasedAutoMinSize) {
   7154          if (const Maybe<nscoord> minSizeClamp = ComputeMinSizeClamp(
   7155                  aFunctions, aPercentageBasis, lineRange, state)) {
   7156            cache.mMinSizeClamp = *minSizeClamp;
   7157            gridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
   7158          }
   7159        }
   7160 
   7161        // Collect the various grid item size contributions we need.
   7162        EnumSet<GridIntrinsicSizeType> sizeTypesToCalculate;
   7163        // For 3.1
   7164        TrackSize::StateBits selector =
   7165            SelectorForPhase(TrackSizingPhase::IntrinsicMinimums, aConstraint);
   7166 
   7167        if (state & selector) {
   7168          sizeTypesToCalculate += GridIntrinsicSizeType::MinContribution;
   7169        }
   7170 
   7171        // For 3.2 and 3.5
   7172        selector =
   7173            SelectorForPhase(TrackSizingPhase::IntrinsicMaximums, aConstraint) |
   7174            SelectorForPhase(TrackSizingPhase::ContentBasedMinimums,
   7175                             aConstraint);
   7176        if (state & selector) {
   7177          sizeTypesToCalculate += GridIntrinsicSizeType::MinContentContribution;
   7178        }
   7179 
   7180        // For 3.3 and 3.6
   7181        selector =
   7182            SelectorForPhase(TrackSizingPhase::MaxContentMinimums,
   7183                             aConstraint) |
   7184            SelectorForPhase(TrackSizingPhase::MaxContentMaximums, aConstraint);
   7185        if (state & selector) {
   7186          sizeTypesToCalculate += GridIntrinsicSizeType::MaxContentContribution;
   7187        }
   7188 
   7189        cache.EnsureContributions(sizeTypesToCalculate, gridItem, aGridRI,
   7190                                  mAxis);
   7191        items->AppendElement(SpanningItemData(
   7192            {span, state, lineRange, cache.SizesOrDefault(), gridItem.mFrame}));
   7193      }
   7194    }
   7195 
   7196    MOZ_ASSERT(
   7197        !(gridItem.mState[mAxis] & ItemState::eClampMarginBoxMinSize) ||
   7198            (gridItem.mState[mAxis] & ItemState::eContentBasedAutoMinSize),
   7199        "clamping only applies to Automatic Minimum Size");
   7200  }
   7201 
   7202  MOZ_ASSERT(maxSpan != 1, "Should only count spans greater than 1");
   7203  // Step 3 - Increase sizes to accommodate spanning items crossing
   7204  // content-sized tracks.
   7205  if (maxSpan) {
   7206    auto fitContentClamper = [&aFunctions, aPercentageBasis](uint32_t aTrack,
   7207                                                             nscoord aMinSize,
   7208                                                             nscoord* aSize) {
   7209      nscoord fitContentLimit = ::ResolveToDefiniteSize(
   7210          aFunctions.MaxSizingFor(aTrack), aPercentageBasis);
   7211      if (*aSize > fitContentLimit) {
   7212        *aSize = std::max(aMinSize, fitContentLimit);
   7213        return true;
   7214      }
   7215      return false;
   7216    };
   7217 
   7218    // Step 3 should "Repeat incrementally for items with greater spans until
   7219    // all items have been considered."
   7220    // Sort the collected items on span length, shortest first. There's no need
   7221    // for a stable sort here since the sizing isn't order dependent within
   7222    // a group of items with the same span length.
   7223    // We don't need to sort flexSpanningItems, those items are all considered
   7224    // "together, rather than grouped by span size" for step 4.
   7225    std::sort(nonFlexSpanningItems.begin(), nonFlexSpanningItems.end(),
   7226              SpanningItemData::IsSpanLessThan);
   7227 
   7228    nsTArray<uint32_t> tracks(maxSpan);
   7229    TrackPlan plan(mSizes.Length());
   7230    plan.SetLength(mSizes.Length());
   7231    ItemPlan itemPlan(mSizes.Length());
   7232    itemPlan.SetLength(mSizes.Length());
   7233 
   7234    // Start / end iterator for items of the same span length:
   7235    auto spanGroupStart = nonFlexSpanningItems.begin();
   7236    auto spanGroupEnd = spanGroupStart;
   7237    const auto end = nonFlexSpanningItems.end();
   7238 
   7239    // nonFlexSpanningItems is sorted by span size. Each iteration will process
   7240    // one span size.
   7241    for (; spanGroupStart != end; spanGroupStart = spanGroupEnd) {
   7242      const uint32_t span = spanGroupStart->mSpan;
   7243      TrackSize::StateBits stateBitsForSpan{0};
   7244      MOZ_ASSERT(spanGroupEnd == spanGroupStart);
   7245      // Find the end of this group if items with the same span size.
   7246      // Accumulate state bits for the items with this span size to avoid
   7247      // calculations below that are not applicable to any of those items.
   7248      do {
   7249        stateBitsForSpan |= StateBitsForRange(spanGroupEnd->mLineRange);
   7250      } while (++spanGroupEnd != end && spanGroupEnd->mSpan == span);
   7251      MOZ_ASSERT(!(stateBitsForSpan & TrackSize::eFlexMaxSizing),
   7252                 "Non-flex spanning items should not include any flex tracks");
   7253      bool updatedBase = false;  // Did we update any mBase in step 3.1..3.3?
   7254      TrackSizingPhase phase = TrackSizingPhase::IntrinsicMinimums;
   7255      if (stateBitsForSpan & SelectorForPhase(phase, aConstraint)) {
   7256        // Step 3.1 MinSize to intrinsic min-sizing.
   7257        updatedBase = GrowSizeForSpanningItems(
   7258            TrackSizingStep::NotFlex, phase, spanGroupStart, spanGroupEnd,
   7259            tracks, plan, itemPlan, aConstraint, aGridRI.mIsGridIntrinsicSizing,
   7260            aFunctions);
   7261      }
   7262 
   7263      phase = TrackSizingPhase::ContentBasedMinimums;
   7264      if (stateBitsForSpan & SelectorForPhase(phase, aConstraint)) {
   7265        // Step 3.2 MinContentContribution to min-/max-content (and 'auto' when
   7266        // sizing under a min-content constraint) min-sizing.
   7267        updatedBase |= GrowSizeForSpanningItems(
   7268            TrackSizingStep::NotFlex, phase, spanGroupStart, spanGroupEnd,
   7269            tracks, plan, itemPlan, aConstraint, aGridRI.mIsGridIntrinsicSizing,
   7270            aFunctions);
   7271      }
   7272 
   7273      phase = TrackSizingPhase::MaxContentMinimums;
   7274      if (stateBitsForSpan & SelectorForPhase(phase, aConstraint)) {
   7275        // Step 3.3 MaxContentContribution to max-content (and 'auto' when
   7276        // sizing under a max-content constraint) min-sizing.
   7277        updatedBase |= GrowSizeForSpanningItems(
   7278            TrackSizingStep::NotFlex, phase, spanGroupStart, spanGroupEnd,
   7279            tracks, plan, itemPlan, aConstraint, aGridRI.mIsGridIntrinsicSizing,
   7280            aFunctions);
   7281      }
   7282 
   7283      if (updatedBase) {
   7284        // Step 3.4
   7285        for (TrackSize& sz : mSizes) {
   7286          if (sz.mBase > sz.mLimit) {
   7287            sz.mLimit = sz.mBase;
   7288          }
   7289        }
   7290      }
   7291 
   7292      phase = TrackSizingPhase::IntrinsicMaximums;
   7293      bool willRunStep3_6 = false;
   7294      if (stateBitsForSpan & SelectorForPhase(phase, aConstraint)) {
   7295        willRunStep3_6 =
   7296            stateBitsForSpan & TrackSize::eAutoOrMaxContentMaxSizing;
   7297        // Step 3.5 MinContentContribution to intrinsic max-sizing.
   7298        GrowSizeForSpanningItems(
   7299            TrackSizingStep::NotFlex, phase, spanGroupStart, spanGroupEnd,
   7300            tracks, plan, itemPlan, aConstraint, aGridRI.mIsGridIntrinsicSizing,
   7301            aFunctions, fitContentClamper, willRunStep3_6);
   7302      }
   7303      if (willRunStep3_6) {
   7304        // Step 2.6 MaxContentContribution to max-content max-sizing.
   7305        phase = TrackSizingPhase::MaxContentMaximums;
   7306        GrowSizeForSpanningItems(
   7307            TrackSizingStep::NotFlex, phase, spanGroupStart, spanGroupEnd,
   7308            tracks, plan, itemPlan, aConstraint, aGridRI.mIsGridIntrinsicSizing,
   7309            aFunctions, fitContentClamper);
   7310      }
   7311    }
   7312 
   7313    // Step 4
   7314    TrackSize::StateBits stateBitsForSpan{0};
   7315    for (const SpanningItemData& spanningData : flexSpanningItems) {
   7316      const TrackSize::StateBits bits =
   7317          StateBitsForRange(spanningData.mLineRange);
   7318      MOZ_ASSERT(bits & TrackSize::eFlexMaxSizing,
   7319                 "All flex spanning items should have at least one flex track");
   7320      stateBitsForSpan |= bits;
   7321    }
   7322    bool updatedBase = false;  // Did we update any mBase in step 4.1..4.3?
   7323    TrackSizingPhase phase = TrackSizingPhase::IntrinsicMinimums;
   7324    if (stateBitsForSpan & SelectorForPhase(phase, aConstraint)) {
   7325      // Step 4.1 MinSize to intrinsic min-sizing.
   7326      updatedBase = GrowSizeForSpanningItems(
   7327          TrackSizingStep::Flex, phase, flexSpanningItems.begin(),
   7328          flexSpanningItems.end(), tracks, plan, itemPlan, aConstraint,
   7329          aGridRI.mIsGridIntrinsicSizing, aFunctions);
   7330    }
   7331 
   7332    phase = TrackSizingPhase::ContentBasedMinimums;
   7333    if (stateBitsForSpan & SelectorForPhase(phase, aConstraint)) {
   7334      // Step 4.2 MinContentContribution to min-/max-content (and 'auto' when
   7335      // sizing under a min-content constraint) min-sizing.
   7336      updatedBase |= GrowSizeForSpanningItems(
   7337          TrackSizingStep::Flex, phase, flexSpanningItems.begin(),
   7338          flexSpanningItems.end(), tracks, plan, itemPlan, aConstraint,
   7339          aGridRI.mIsGridIntrinsicSizing, aFunctions);
   7340    }
   7341 
   7342    phase = TrackSizingPhase::MaxContentMinimums;
   7343    if (stateBitsForSpan & SelectorForPhase(phase, aConstraint)) {
   7344      // Step 4.3 MaxContentContribution to max-content (and 'auto' when
   7345      // sizing under a max-content constraint) min-sizing.
   7346      updatedBase |= GrowSizeForSpanningItems(
   7347          TrackSizingStep::Flex, phase, flexSpanningItems.begin(),
   7348          flexSpanningItems.end(), tracks, plan, itemPlan, aConstraint,
   7349          aGridRI.mIsGridIntrinsicSizing, aFunctions);
   7350    }
   7351 
   7352    if (updatedBase) {
   7353      // Step 4.4
   7354      for (TrackSize& sz : mSizes) {
   7355        if (sz.mBase > sz.mLimit) {
   7356          sz.mLimit = sz.mBase;
   7357        }
   7358      }
   7359    }
   7360  }
   7361 
   7362  // Step 5 - If any track still has an infinite growth limit, set its growth
   7363  // limit to its base size.
   7364  for (TrackSize& sz : mSizes) {
   7365    if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
   7366      sz.mLimit = sz.mBase;
   7367    }
   7368  }
   7369 }
   7370 
   7371 float nsGridContainerFrame::Tracks::FindFrUnitSize(
   7372    const LineRange& aRange, const nsTArray<uint32_t>& aFlexTracks,
   7373    const TrackSizingFunctions& aFunctions, nscoord aSpaceToFill) const {
   7374  MOZ_ASSERT(aSpaceToFill > 0 && !aFlexTracks.IsEmpty());
   7375  float flexFactorSum = 0.0f;
   7376  nscoord leftOverSpace = aSpaceToFill;
   7377  for (auto i : aRange.Range()) {
   7378    const TrackSize& sz = mSizes[i];
   7379    if (sz.mState & TrackSize::eFlexMaxSizing) {
   7380      flexFactorSum += aFunctions.MaxSizingFor(i).AsFr();
   7381    } else {
   7382      leftOverSpace -= sz.mBase;
   7383      if (leftOverSpace <= 0) {
   7384        return 0.0f;
   7385      }
   7386    }
   7387  }
   7388  bool restart;
   7389  float hypotheticalFrSize;
   7390  nsTArray<uint32_t> flexTracks(aFlexTracks.Clone());
   7391  uint32_t numFlexTracks = flexTracks.Length();
   7392  do {
   7393    restart = false;
   7394 
   7395    // 12.7.1.2: If flexFactorSum is less than 1, set it to 1 instead.
   7396    hypotheticalFrSize = leftOverSpace / std::max(flexFactorSum, 1.0f);
   7397    for (uint32_t& track : flexTracks) {
   7398      if (track == kAutoLine) {
   7399        continue;  // Track marked as inflexible in a prev. iter of this loop.
   7400      }
   7401      float flexFactor = aFunctions.MaxSizingFor(track).AsFr();
   7402      const nscoord base = mSizes[track].mBase;
   7403      if (flexFactor * hypotheticalFrSize < base) {
   7404        // 12.7.1.4: Treat this track as inflexible.
   7405        track = kAutoLine;
   7406        flexFactorSum -= flexFactor;
   7407        leftOverSpace -= base;
   7408        --numFlexTracks;
   7409        if (numFlexTracks == 0 || leftOverSpace <= 0) {
   7410          return 0.0f;
   7411        }
   7412        restart = true;
   7413        // break; XXX (bug 1176621 comment 16) measure which is more common
   7414      }
   7415    }
   7416  } while (restart);
   7417  return hypotheticalFrSize;
   7418 }
   7419 
   7420 float nsGridContainerFrame::Tracks::FindUsedFlexFraction(
   7421    GridReflowInput& aGridRI, const nsTArray<GridItemInfo>& aGridItems,
   7422    const nsTArray<uint32_t>& aFlexTracks,
   7423    const TrackSizingFunctions& aFunctions, nscoord aAvailableSize) const {
   7424  if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
   7425    // Use all of the grid tracks and a 'space to fill' of the available space.
   7426    const TranslatedLineRange range(0, mSizes.Length());
   7427    return FindFrUnitSize(range, aFlexTracks, aFunctions, aAvailableSize);
   7428  }
   7429 
   7430  // The used flex fraction is the maximum of:
   7431  // ... each flexible track's base size divided by its flex factor (which is
   7432  // floored at 1).
   7433  float fr = 0.0f;
   7434  for (uint32_t track : aFlexTracks) {
   7435    float flexFactor = aFunctions.MaxSizingFor(track).AsFr();
   7436    float possiblyDividedBaseSize = (flexFactor > 1.0f)
   7437                                        ? mSizes[track].mBase / flexFactor
   7438                                        : mSizes[track].mBase;
   7439    fr = std::max(fr, possiblyDividedBaseSize);
   7440  }
   7441  // ... the result of 'finding the size of an fr' for each item that spans
   7442  // a flex track with its max-content contribution as 'space to fill'
   7443  for (const GridItemInfo& item : aGridItems) {
   7444    if (item.mState[mAxis] & ItemState::eIsFlexing) {
   7445      // XXX optimize: bug 1194446
   7446      const auto percentageBasis = aGridRI.PercentageBasisFor(mAxis, item);
   7447      nscoord spaceToFill = ContentContribution(
   7448          item, aGridRI, mAxis, percentageBasis, IntrinsicISizeType::PrefISize);
   7449      const LineRange& range =
   7450          mAxis == LogicalAxis::Inline ? item.mArea.mCols : item.mArea.mRows;
   7451      MOZ_ASSERT(range.Extent() >= 1);
   7452      const auto spannedGaps = range.Extent() - 1;
   7453      if (spannedGaps > 0) {
   7454        spaceToFill -= mGridGap * spannedGaps;
   7455      }
   7456      if (spaceToFill <= 0) {
   7457        continue;
   7458      }
   7459      // ... and all its spanned tracks as input.
   7460      nsTArray<uint32_t> itemFlexTracks;
   7461      for (auto i : range.Range()) {
   7462        if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
   7463          itemFlexTracks.AppendElement(i);
   7464        }
   7465      }
   7466      float itemFr =
   7467          FindFrUnitSize(range, itemFlexTracks, aFunctions, spaceToFill);
   7468      fr = std::max(fr, itemFr);
   7469    }
   7470  }
   7471  return fr;
   7472 }
   7473 
   7474 void nsGridContainerFrame::Tracks::StretchFlexibleTracks(
   7475    GridReflowInput& aGridRI, const nsTArray<GridItemInfo>& aGridItems,
   7476    const TrackSizingFunctions& aFunctions, nscoord aAvailableSize) {
   7477  if (aAvailableSize <= 0) {
   7478    return;
   7479  }
   7480  nsTArray<uint32_t> flexTracks(mSizes.Length());
   7481  for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
   7482    if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
   7483      flexTracks.AppendElement(i);
   7484    }
   7485  }
   7486  if (flexTracks.IsEmpty()) {
   7487    return;
   7488  }
   7489  nscoord minSize = 0;
   7490  nscoord maxSize = NS_UNCONSTRAINEDSIZE;
   7491  if (aGridRI.mReflowInput) {
   7492    auto* ri = aGridRI.mReflowInput;
   7493    minSize = mAxis == LogicalAxis::Block ? ri->ComputedMinBSize()
   7494                                          : ri->ComputedMinISize();
   7495    maxSize = mAxis == LogicalAxis::Block ? ri->ComputedMaxBSize()
   7496                                          : ri->ComputedMaxISize();
   7497  }
   7498  Maybe<TrackPlan> origSizes;
   7499  bool applyMinMax = (minSize != 0 || maxSize != NS_UNCONSTRAINEDSIZE) &&
   7500                     aAvailableSize == NS_UNCONSTRAINEDSIZE;
   7501  // We iterate twice at most.  The 2nd time if the grid size changed after
   7502  // applying a min/max-size (can only occur if aAvailableSize is indefinite).
   7503  while (true) {
   7504    float fr = FindUsedFlexFraction(aGridRI, aGridItems, flexTracks, aFunctions,
   7505                                    aAvailableSize);
   7506    if (fr != 0.0f) {
   7507      for (uint32_t i : flexTracks) {
   7508        float flexFactor = aFunctions.MaxSizingFor(i).AsFr();
   7509        nscoord flexLength = NSToCoordRound(flexFactor * fr);
   7510        nscoord& base = mSizes[i].mBase;
   7511        if (flexLength > base) {
   7512          if (applyMinMax && origSizes.isNothing()) {
   7513            origSizes.emplace(mSizes);
   7514          }
   7515          base = flexLength;
   7516        }
   7517      }
   7518    }
   7519    if (applyMinMax) {
   7520      applyMinMax = false;
   7521      // https://drafts.csswg.org/css-grid-2/#algo-flex-tracks
   7522      // "If using this flex fraction would cause the grid to be smaller than
   7523      // the grid container’s min-width/height (or larger than the grid
   7524      // container’s max-width/height), then redo this step, treating the free
   7525      // space as definite [...]"
   7526      const auto sumOfGridGaps = SumOfGridGaps();
   7527      nscoord newSize = SumOfGridTracks() + sumOfGridGaps;
   7528      if (newSize > maxSize) {
   7529        aAvailableSize = maxSize;
   7530      } else if (newSize < minSize) {
   7531        aAvailableSize = minSize;
   7532      }
   7533      if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
   7534        aAvailableSize = std::max(0, aAvailableSize - sumOfGridGaps);
   7535        // Restart with the original track sizes and definite aAvailableSize.
   7536        if (origSizes.isSome()) {
   7537          mSizes = std::move(*origSizes);
   7538          origSizes.reset();
   7539        }  // else, no mSizes[].mBase were changed above so it's still correct
   7540        if (aAvailableSize == 0) {
   7541          break;  // zero available size wouldn't change any sizes though...
   7542        }
   7543        continue;
   7544      }
   7545    }
   7546    break;
   7547  }
   7548 }
   7549 
   7550 void nsGridContainerFrame::Tracks::AlignJustifyContent(
   7551    const nsStylePosition* aStyle, StyleContentDistribution aAligmentStyleValue,
   7552    WritingMode aWM, nscoord aContentBoxSize, bool aIsSubgriddedAxis) {
   7553  const bool isAlign = mAxis == LogicalAxis::Block;
   7554  // Align-/justify-content doesn't apply in a subgridded axis.
   7555  // Gap properties do apply though so we need to stretch/position the tracks
   7556  // to center-align the gaps with the parent's gaps.
   7557  if (MOZ_UNLIKELY(aIsSubgriddedAxis)) {
   7558    auto& gap = isAlign ? aStyle->mRowGap : aStyle->mColumnGap;
   7559    if (gap.IsNormal()) {
   7560      return;
   7561    }
   7562    auto len = mSizes.Length();
   7563    if (len <= 1) {
   7564      return;
   7565    }
   7566    // This stores the gap deltas between the subgrid gap and the gaps in
   7567    // the used track sizes (as encoded in its tracks' mPosition):
   7568    nsTArray<nscoord> gapDeltas;
   7569    const size_t numGaps = len - 1;
   7570    gapDeltas.SetLength(numGaps);
   7571    for (size_t i = 0; i < numGaps; ++i) {
   7572      TrackSize& sz1 = mSizes[i];
   7573      TrackSize& sz2 = mSizes[i + 1];
   7574      nscoord currentGap = sz2.mPosition - (sz1.mPosition + sz1.mBase);
   7575      gapDeltas[i] = mGridGap - currentGap;
   7576    }
   7577    // Recompute the tracks' size/position so that they end up with
   7578    // a subgrid-gap centered on the original track gap.
   7579    nscoord currentPos = mSizes[0].mPosition;
   7580    nscoord lastHalfDelta(0);
   7581    for (size_t i = 0; i < numGaps; ++i) {
   7582      TrackSize& sz = mSizes[i];
   7583      nscoord delta = gapDeltas[i];
   7584      nscoord halfDelta;
   7585      nscoord roundingError = NSCoordDivRem(delta, 2, &halfDelta);
   7586      auto newSize = sz.mBase - (halfDelta + roundingError) - lastHalfDelta;
   7587      lastHalfDelta = halfDelta;
   7588      // If the gap delta (in particular 'halfDelta + lastHalfDelta') is larger
   7589      // than the current track size, newSize can be negative. Don't let the new
   7590      // track size (mBase) be negative.
   7591      sz.mBase = std::max(newSize, 0);
   7592      sz.mPosition = currentPos;
   7593      currentPos += newSize + mGridGap;
   7594    }
   7595    auto& lastTrack = mSizes.LastElement();
   7596    auto newSize = lastTrack.mBase - lastHalfDelta;
   7597    lastTrack.mBase = std::max(newSize, 0);
   7598    lastTrack.mPosition = currentPos;
   7599    return;
   7600  }
   7601 
   7602  if (mSizes.IsEmpty()) {
   7603    return;
   7604  }
   7605 
   7606  bool overflowSafe;
   7607  auto alignment = ::GetAlignJustifyValue(aAligmentStyleValue.primary, aWM,
   7608                                          isAlign, &overflowSafe);
   7609  if (alignment == StyleAlignFlags::NORMAL) {
   7610    alignment = StyleAlignFlags::STRETCH;
   7611    // we may need a fallback for 'stretch' below
   7612    aAligmentStyleValue = {alignment};
   7613  }
   7614 
   7615  // Compute the free space and count auto-sized tracks.
   7616  size_t numAutoTracks = 0;
   7617  nscoord space;
   7618  if (alignment != StyleAlignFlags::START) {
   7619    nscoord trackSizeSum = 0;
   7620    if (aIsSubgriddedAxis) {
   7621      numAutoTracks = mSizes.Length();
   7622    } else {
   7623      for (const TrackSize& sz : mSizes) {
   7624        trackSizeSum += sz.mBase;
   7625        if (sz.mState & TrackSize::eAutoMaxSizing) {
   7626          ++numAutoTracks;
   7627        }
   7628      }
   7629    }
   7630    space = aContentBoxSize - trackSizeSum - SumOfGridGaps();
   7631    // Use the fallback value instead when applicable.
   7632    if (space < 0 ||
   7633        (alignment == StyleAlignFlags::SPACE_BETWEEN && mSizes.Length() == 1)) {
   7634      auto fallback = GetAlignJustifyDistributionFallback(aAligmentStyleValue,
   7635                                                          &overflowSafe);
   7636      if (fallback) {
   7637        alignment = *fallback;
   7638      }
   7639    }
   7640    if (space == 0 || (space < 0 && overflowSafe)) {
   7641      // XXX check that this makes sense also for [last ]baseline (bug 1151204).
   7642      alignment = StyleAlignFlags::START;
   7643    }
   7644  }
   7645 
   7646  // Optimize the cases where we just need to set each track's position.
   7647  nscoord pos = 0;
   7648  bool distribute = true;
   7649  if (alignment == StyleAlignFlags::BASELINE ||
   7650      alignment == StyleAlignFlags::LAST_BASELINE) {
   7651    NS_WARNING("NYI: 'first/last baseline' (bug 1151204)");  // XXX
   7652    alignment = StyleAlignFlags::START;
   7653  }
   7654  if (alignment == StyleAlignFlags::START) {
   7655    distribute = false;
   7656  } else if (alignment == StyleAlignFlags::END) {
   7657    pos = space;
   7658    distribute = false;
   7659  } else if (alignment == StyleAlignFlags::CENTER) {
   7660    pos = space / 2;
   7661    distribute = false;
   7662  } else if (alignment == StyleAlignFlags::STRETCH) {
   7663    distribute = numAutoTracks != 0;
   7664  }
   7665  if (!distribute) {
   7666    for (TrackSize& sz : mSizes) {
   7667      sz.mPosition = pos;
   7668      pos += sz.mBase + mGridGap;
   7669    }
   7670    return;
   7671  }
   7672 
   7673  // Distribute free space to/between tracks and set their position.
   7674  MOZ_ASSERT(space > 0, "should've handled that on the fallback path above");
   7675  nscoord between, roundingError;
   7676  if (alignment == StyleAlignFlags::STRETCH) {
   7677    MOZ_ASSERT(numAutoTracks > 0, "we handled numAutoTracks == 0 above");
   7678    // The outer loop typically only runs once - it repeats only in a masonry
   7679    // axis when some stretchable items reach their `max-size`.
   7680    // It's O(n^2) worst case; if all items are stretchable with a `max-size`
   7681    // and exactly one item reaches its `max-size` each round.
   7682    while (space) {
   7683      pos = 0;
   7684      nscoord spacePerTrack;
   7685      roundingError = NSCoordDivRem(space, numAutoTracks, &spacePerTrack);
   7686      space = 0;
   7687      for (TrackSize& sz : mSizes) {
   7688        sz.mPosition = pos;
   7689        if (!(sz.mState & TrackSize::eAutoMaxSizing)) {
   7690          pos += sz.mBase + mGridGap;
   7691          continue;
   7692        }
   7693        nscoord stretch = spacePerTrack;
   7694        if (roundingError) {
   7695          roundingError -= 1;
   7696          stretch += 1;
   7697        }
   7698        nscoord newBase = sz.mBase + stretch;
   7699        if (mIsMasonry && (sz.mState & TrackSize::eClampToLimit)) {
   7700          auto clampedSize = std::min(newBase, sz.mLimit);
   7701          auto sizeOverLimit = newBase - clampedSize;
   7702          if (sizeOverLimit > 0) {
   7703            newBase = clampedSize;
   7704            sz.mState &= ~(sz.mState & TrackSize::eAutoMaxSizing);
   7705            // This repeats the outer loop to distribute the superfluous space:
   7706            space += sizeOverLimit;
   7707            if (--numAutoTracks == 0) {
   7708              // ... except if we don't have any stretchable items left.
   7709              space = 0;
   7710            }
   7711          }
   7712        }
   7713        sz.mBase = newBase;
   7714        pos += newBase + mGridGap;
   7715      }
   7716    }
   7717    MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
   7718    return;
   7719  }
   7720  if (alignment == StyleAlignFlags::SPACE_BETWEEN) {
   7721    MOZ_ASSERT(mSizes.Length() > 1, "should've used a fallback above");
   7722    roundingError = NSCoordDivRem(space, mSizes.Length() - 1, &between);
   7723  } else if (alignment == StyleAlignFlags::SPACE_AROUND) {
   7724    roundingError = NSCoordDivRem(space, mSizes.Length(), &between);
   7725    pos = between / 2;
   7726  } else if (alignment == StyleAlignFlags::SPACE_EVENLY) {
   7727    roundingError = NSCoordDivRem(space, mSizes.Length() + 1, &between);
   7728    pos = between;
   7729  } else {
   7730    MOZ_ASSERT_UNREACHABLE("unknown align-/justify-content value");
   7731    between = 0;        // just to avoid a compiler warning
   7732    roundingError = 0;  // just to avoid a compiler warning
   7733  }
   7734  between += mGridGap;
   7735  for (TrackSize& sz : mSizes) {
   7736    sz.mPosition = pos;
   7737    nscoord spacing = between;
   7738    if (roundingError) {
   7739      roundingError -= 1;
   7740      spacing += 1;
   7741    }
   7742    pos += sz.mBase + spacing;
   7743  }
   7744  MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
   7745 }
   7746 
   7747 nscoord nsGridContainerFrame::Tracks::TotalTrackSizeWithoutAlignment(
   7748    const nsGridContainerFrame* aGridContainerFrame) const {
   7749  if (aGridContainerFrame->IsSubgrid(mAxis)) {
   7750    // TODO: Investigate whether GridLineEdge here may include extra packing
   7751    // space introduced by align-content or justify-content, and if that could
   7752    // lead to inconsistent metrics vs. the non-subgrid path.
   7753    return GridLineEdge(mSizes.Length(), GridLineSide::BeforeGridGap);
   7754  }
   7755 
   7756  // This method allows for the possibility that AlignJustifyContent() might not
   7757  // be called yet. Therefore, we can't use GridLineEdge() here, as mPosition
   7758  // may not be calculated.
   7759  return SumOfGridTracksAndGaps();
   7760 }
   7761 
   7762 void nsGridContainerFrame::LineRange::ToPositionAndLength(
   7763    const TrackPlan& aTrackSizes, nscoord* aPos, nscoord* aLength) const {
   7764  MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
   7765             "expected a definite LineRange");
   7766  MOZ_ASSERT(mStart < mEnd);
   7767  nscoord startPos = aTrackSizes[mStart].mPosition;
   7768  const TrackSize& sz = aTrackSizes[mEnd - 1];
   7769  *aPos = startPos;
   7770  *aLength = (sz.mPosition + sz.mBase) - startPos;
   7771 }
   7772 
   7773 nscoord nsGridContainerFrame::LineRange::ToLength(
   7774    const TrackPlan& aTrackSizes) const {
   7775  MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
   7776             "expected a definite LineRange");
   7777  MOZ_ASSERT(mStart < mEnd);
   7778  nscoord startPos = aTrackSizes[mStart].mPosition;
   7779  const TrackSize& sz = aTrackSizes[mEnd - 1];
   7780  return (sz.mPosition + sz.mBase) - startPos;
   7781 }
   7782 
   7783 void nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos(
   7784    const Tracks& aTracks, nscoord aGridOrigin, nscoord* aPos,
   7785    nscoord* aLength) const {
   7786  // kAutoLine for abspos children contributes the corresponding edge
   7787  // of the grid container's padding-box.
   7788  if (mEnd == kAutoLine) {
   7789    if (mStart == kAutoLine) {
   7790      // done
   7791    } else {
   7792      const nscoord endPos = *aPos + *aLength;
   7793      auto side = mStart == aTracks.mSizes.Length()
   7794                      ? GridLineSide::BeforeGridGap
   7795                      : GridLineSide::AfterGridGap;
   7796      nscoord startPos = aTracks.GridLineEdge(mStart, side);
   7797      *aPos = aGridOrigin + startPos;
   7798      *aLength = std::max(endPos - *aPos, 0);
   7799    }
   7800  } else {
   7801    if (mStart == kAutoLine) {
   7802      auto side =
   7803          mEnd == 0 ? GridLineSide::AfterGridGap : GridLineSide::BeforeGridGap;
   7804      nscoord endPos = aTracks.GridLineEdge(mEnd, side);
   7805      *aLength = std::max(aGridOrigin + endPos, 0);
   7806    } else if (MOZ_LIKELY(mStart != mEnd)) {
   7807      nscoord pos;
   7808      ToPositionAndLength(aTracks.mSizes, &pos, aLength);
   7809      *aPos = aGridOrigin + pos;
   7810    } else {
   7811      // The grid area only covers removed 'auto-fit' tracks.
   7812      nscoord pos = aTracks.GridLineEdge(mStart, GridLineSide::BeforeGridGap);
   7813      *aPos = aGridOrigin + pos;
   7814      *aLength = nscoord(0);
   7815    }
   7816  }
   7817 }
   7818 
   7819 LogicalSize nsGridContainerFrame::GridReflowInput::PercentageBasisFor(
   7820    LogicalAxis aAxis, const GridItemInfo& aGridItem) const {
   7821  auto wm = aGridItem.mFrame->GetWritingMode();
   7822  const auto* itemParent = aGridItem.mFrame->GetParent();
   7823  if (MOZ_UNLIKELY(itemParent != mFrame)) {
   7824    // The item comes from a descendant subgrid.  Use the subgrid's
   7825    // used track sizes to resolve the grid area size, if present.
   7826    MOZ_ASSERT(itemParent->IsGridContainerFrame());
   7827    auto* subgridFrame = static_cast<const nsGridContainerFrame*>(itemParent);
   7828    MOZ_ASSERT(subgridFrame->IsSubgrid());
   7829    if (auto* uts = subgridFrame->GetUsedTrackSizes()) {
   7830      auto subgridWM = subgridFrame->GetWritingMode();
   7831      LogicalSize cbSize(subgridWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   7832      if (!subgridFrame->IsColSubgrid() &&
   7833          uts->mCanResolveLineRangeSize[LogicalAxis::Inline]) {
   7834        // NOTE: At this point aGridItem.mArea is in this->mFrame coordinates
   7835        // and thus may have been transposed.  The range values in a non-
   7836        // subgridded axis still has its original values in subgridFrame's
   7837        // coordinates though.
   7838        const auto subgridIAxisInGridWM =
   7839            subgridWM.ConvertAxisTo(LogicalAxis::Inline, mWM);
   7840        const auto& range =
   7841            aGridItem.mArea.LineRangeForAxis(subgridIAxisInGridWM);
   7842        cbSize.ISize(subgridWM) =
   7843            range.ToLength(uts->mTrackPlans[LogicalAxis::Inline]);
   7844      }
   7845      if (!subgridFrame->IsRowSubgrid() &&
   7846          uts->mCanResolveLineRangeSize[LogicalAxis::Block]) {
   7847        const auto subgridBAxisInGridWM =
   7848            subgridWM.ConvertAxisTo(LogicalAxis::Block, mWM);
   7849        const auto& range =
   7850            aGridItem.mArea.LineRangeForAxis(subgridBAxisInGridWM);
   7851        cbSize.BSize(subgridWM) =
   7852            range.ToLength(uts->mTrackPlans[LogicalAxis::Block]);
   7853      }
   7854      return cbSize.ConvertTo(wm, subgridWM);
   7855    }
   7856 
   7857    return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   7858  }
   7859 
   7860  // Get row size and column size for the grid area occupied by aGridItem.
   7861  const nscoord colSize = mCols.mCanResolveLineRangeSize
   7862                              ? mCols.ResolveSize(aGridItem.mArea.mCols)
   7863                              : NS_UNCONSTRAINEDSIZE;
   7864  const nscoord rowSize = mRows.mCanResolveLineRangeSize
   7865                              ? mRows.ResolveSize(aGridItem.mArea.mRows)
   7866                              : NS_UNCONSTRAINEDSIZE;
   7867  return !wm.IsOrthogonalTo(mWM) ? LogicalSize(wm, colSize, rowSize)
   7868                                 : LogicalSize(wm, rowSize, colSize);
   7869 }
   7870 
   7871 LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockFor(
   7872    const GridArea& aArea) const {
   7873  nscoord i, b, iSize, bSize;
   7874  MOZ_ASSERT(aArea.mCols.Extent() > 0, "grid items cover at least one track");
   7875  MOZ_ASSERT(aArea.mRows.Extent() > 0, "grid items cover at least one track");
   7876  aArea.mCols.ToPositionAndLength(mCols.mSizes, &i, &iSize);
   7877  aArea.mRows.ToPositionAndLength(mRows.mSizes, &b, &bSize);
   7878  return LogicalRect(mWM, i, b, iSize, bSize);
   7879 }
   7880 
   7881 LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockForAbsPos(
   7882    const GridArea& aArea, const LogicalPoint& aGridOrigin,
   7883    const LogicalRect& aGridCB) const {
   7884  nscoord i = aGridCB.IStart(mWM);
   7885  nscoord b = aGridCB.BStart(mWM);
   7886  nscoord iSize = aGridCB.ISize(mWM);
   7887  nscoord bSize = aGridCB.BSize(mWM);
   7888  aArea.mCols.ToPositionAndLengthForAbsPos(mCols, aGridOrigin.I(mWM), &i,
   7889                                           &iSize);
   7890  aArea.mRows.ToPositionAndLengthForAbsPos(mRows, aGridOrigin.B(mWM), &b,
   7891                                           &bSize);
   7892  return LogicalRect(mWM, i, b, iSize, bSize);
   7893 }
   7894 
   7895 void nsGridContainerFrame::GridReflowInput::AlignJustifyContentInMasonryAxis(
   7896    nscoord aMasonryBoxSize, nscoord aContentBoxSize) {
   7897  if (aContentBoxSize == NS_UNCONSTRAINEDSIZE) {
   7898    aContentBoxSize = aMasonryBoxSize;
   7899  }
   7900  auto& masonryAxisTracks = mRows.mIsMasonry ? mRows : mCols;
   7901  MOZ_ASSERT(masonryAxisTracks.mSizes.Length() == 2,
   7902             "unexpected masonry axis tracks");
   7903  const auto masonryAxis = masonryAxisTracks.mAxis;
   7904  const auto contentAlignment = mGridStyle->UsedContentAlignment(masonryAxis);
   7905  if (contentAlignment.primary == StyleAlignFlags::NORMAL ||
   7906      contentAlignment.primary == StyleAlignFlags::STRETCH) {
   7907    // Stretch the "masonry box" to the full content box if it's smaller.
   7908    nscoord cbSize = std::max(aMasonryBoxSize, aContentBoxSize);
   7909    for (auto& sz : masonryAxisTracks.mSizes) {
   7910      sz.mBase = cbSize;
   7911    }
   7912    return;
   7913  }
   7914 
   7915  // Save our current track sizes; replace them with one track sized to
   7916  // the masonry box and align that within our content box.
   7917  auto savedTrackSizes(std::move(masonryAxisTracks.mSizes));
   7918  masonryAxisTracks.mSizes.AppendElement(savedTrackSizes[0]);
   7919  masonryAxisTracks.mSizes[0].mBase = aMasonryBoxSize;
   7920  masonryAxisTracks.AlignJustifyContent(mGridStyle, contentAlignment, mWM,
   7921                                        aContentBoxSize, false);
   7922  nscoord masonryBoxOffset = masonryAxisTracks.mSizes[0].mPosition;
   7923  // Restore the original track sizes...
   7924  masonryAxisTracks.mSizes = std::move(savedTrackSizes);
   7925  // ...then reposition and resize all of them to the aligned result.
   7926  for (auto& sz : masonryAxisTracks.mSizes) {
   7927    sz.mPosition = masonryBoxOffset;
   7928    sz.mBase = aMasonryBoxSize;
   7929  }
   7930 }
   7931 
   7932 // XXX This function was gutted when the 'align-tracks' and 'justify-tracks'
   7933 // properties were removed in
   7934 // https://bugzilla.mozilla.org/show_bug.cgi?id=1900195
   7935 // Possibly the current design of the Masonry code doesn't make much sense now
   7936 // without those properties, or at the very least this function should be
   7937 // renamed?
   7938 //
   7939 // Note: this is called after all items have been positioned/reflowed.
   7940 // The masonry-axis tracks have the size of the "masonry box" at this point
   7941 // and are positioned according to 'align/justify-content'.
   7942 void nsGridContainerFrame::GridReflowInput::AlignJustifyTracksInMasonryAxis(
   7943    const LogicalSize& aContentSize, const nsSize& aContainerSize) {
   7944  auto& masonryAxisTracks = mRows.mIsMasonry ? mRows : mCols;
   7945  MOZ_ASSERT(masonryAxisTracks.mSizes.Length() == 2,
   7946             "unexpected masonry axis tracks");
   7947  // The offset to the "masonry box" from our content-box start edge.
   7948  const nscoord masonryBoxOffset = masonryAxisTracks.mSizes[0].mPosition;
   7949  if (masonryBoxOffset == 0) {
   7950    return;
   7951  }
   7952 
   7953  const auto masonryAxis = masonryAxisTracks.mAxis;
   7954  auto gridAxis = GetOrthogonalAxis(masonryAxis);
   7955  auto& gridAxisTracks = TracksFor(gridAxis);
   7956  auto wm = mWM;
   7957 
   7958  for (auto i : IntegerRange(gridAxisTracks.mSizes.Length())) {
   7959    // TODO move placeholders too
   7960    auto delta = masonryBoxOffset;
   7961    LogicalPoint logicalDelta(wm);
   7962    logicalDelta.Pos(masonryAxis, wm) = delta;
   7963    for (const auto& item : mGridItems) {
   7964      if (item.mArea.LineRangeForAxis(gridAxis).mStart != i) {
   7965        continue;
   7966      }
   7967      item.mFrame->MovePositionBy(wm, logicalDelta);
   7968    }
   7969  }
   7970 }
   7971 
   7972 /**
   7973 * Return a Fragmentainer object if we have a fragmentainer frame in our
   7974 * ancestor chain of containing block (CB) reflow inputs.  We'll only
   7975 * continue traversing the ancestor chain as long as the CBs have
   7976 * the same writing-mode and have overflow:visible.
   7977 */
   7978 Maybe<nsGridContainerFrame::Fragmentainer>
   7979 nsGridContainerFrame::GetNearestFragmentainer(
   7980    const GridReflowInput& aGridRI) const {
   7981  Maybe<nsGridContainerFrame::Fragmentainer> data;
   7982  const ReflowInput* gridRI = aGridRI.mReflowInput;
   7983  if (!gridRI->IsInFragmentedContext()) {
   7984    return data;
   7985  }
   7986  WritingMode wm = aGridRI.mWM;
   7987  const ReflowInput* cbRI = gridRI->mCBReflowInput;
   7988  for (; cbRI; cbRI = cbRI->mCBReflowInput) {
   7989    ScrollContainerFrame* sf = do_QueryFrame(cbRI->mFrame);
   7990    if (sf) {
   7991      break;
   7992    }
   7993    if (wm.IsOrthogonalTo(cbRI->GetWritingMode())) {
   7994      break;
   7995    }
   7996    LayoutFrameType frameType = cbRI->mFrame->Type();
   7997    if ((frameType == LayoutFrameType::Canvas &&
   7998         PresContext()->IsPaginated()) ||
   7999        frameType == LayoutFrameType::ColumnSet) {
   8000      data.emplace();
   8001      data->mIsTopOfPage = gridRI->mFlags.mIsTopOfPage;
   8002      if (gridRI->AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
   8003        data->mToFragmentainerEnd = aGridRI.mFragBStart +
   8004                                    gridRI->AvailableBSize() -
   8005                                    aGridRI.mBorderPadding.BStart(wm);
   8006      } else {
   8007        // This occurs when nsColumnSetFrame reflows its last column in
   8008        // unconstrained available block-size.
   8009        data->mToFragmentainerEnd = NS_UNCONSTRAINEDSIZE;
   8010      }
   8011      const auto numRows = aGridRI.mRows.mSizes.Length();
   8012      data->mCanBreakAtStart =
   8013          numRows > 0 && aGridRI.mRows.mSizes[0].mPosition > 0;
   8014      nscoord bSize = gridRI->ComputedBSize();
   8015      data->mIsAutoBSize = bSize == NS_UNCONSTRAINEDSIZE;
   8016      if (data->mIsAutoBSize) {
   8017        bSize = gridRI->ComputedMinBSize();
   8018      } else {
   8019        bSize = gridRI->ApplyMinMaxBSize(bSize);
   8020      }
   8021      nscoord gridEnd =
   8022          aGridRI.mRows.GridLineEdge(numRows, GridLineSide::BeforeGridGap);
   8023      data->mCanBreakAtEnd = bSize > gridEnd && bSize > aGridRI.mFragBStart;
   8024      break;
   8025    }
   8026  }
   8027  return data;
   8028 }
   8029 
   8030 void nsGridContainerFrame::ReflowInFlowChild(
   8031    nsIFrame* aChild, const GridItemInfo* aGridItemInfo, nsSize aContainerSize,
   8032    const Maybe<nscoord>& aStretchBSize, const Fragmentainer* aFragmentainer,
   8033    const GridReflowInput& aGridRI, const LogicalRect& aContentArea,
   8034    ReflowOutput& aDesiredSize, nsReflowStatus& aStatus) {
   8035  nsPresContext* pc = PresContext();
   8036  ComputedStyle* containerSC = Style();
   8037  WritingMode wm = aGridRI.mReflowInput->GetWritingMode();
   8038  const bool isGridItem = !!aGridItemInfo;
   8039  MOZ_ASSERT(isGridItem == !aChild->IsPlaceholderFrame());
   8040  LogicalRect cb(wm);
   8041  WritingMode childWM = aChild->GetWritingMode();
   8042  bool isConstrainedBSize = false;
   8043  nscoord toFragmentainerEnd;
   8044  // The part of the child's grid area that's in previous container fragments.
   8045  nscoord consumedGridAreaBSize = 0;
   8046  if (MOZ_LIKELY(isGridItem)) {
   8047    MOZ_ASSERT(aGridItemInfo->mFrame == aChild);
   8048    const GridArea& area = aGridItemInfo->mArea;
   8049    MOZ_ASSERT(area.IsDefinite());
   8050    cb = aGridRI.ContainingBlockFor(area);
   8051    if (aFragmentainer && !wm.IsOrthogonalTo(childWM)) {
   8052      // |gridAreaBOffset| is the offset of the child's grid area in this
   8053      // container fragment (if negative, that distance is the child CB size
   8054      // consumed in previous container fragments).  Note that cb.BStart
   8055      // (initially) and aState.mFragBStart are in "global" grid coordinates
   8056      // (like all track positions).
   8057      nscoord gridAreaBOffset = cb.BStart(wm) - aGridRI.mFragBStart;
   8058      consumedGridAreaBSize = std::max(0, -gridAreaBOffset);
   8059      cb.BStart(wm) = std::max(0, gridAreaBOffset);
   8060      if (aFragmentainer->mToFragmentainerEnd != NS_UNCONSTRAINEDSIZE) {
   8061        toFragmentainerEnd = aFragmentainer->mToFragmentainerEnd -
   8062                             aGridRI.mFragBStart - cb.BStart(wm);
   8063        toFragmentainerEnd = std::max(toFragmentainerEnd, 0);
   8064        isConstrainedBSize = true;
   8065      }
   8066    }
   8067    cb += aContentArea.Origin(wm);
   8068    aGridRI.mRows.AlignBaselineSubtree(*aGridItemInfo);
   8069    aGridRI.mCols.AlignBaselineSubtree(*aGridItemInfo);
   8070    // Setup [align|justify]-content:[last ]baseline related frame properties.
   8071    // These are added to the padding in SizeComputationInput::InitOffsets.
   8072    // (a negative value signals the value is for 'last baseline' and should be
   8073    //  added to the (logical) end padding)
   8074    typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop;
   8075    auto SetProp = [aGridItemInfo, aChild](LogicalAxis aGridAxis, Prop aProp) {
   8076      auto state = aGridItemInfo->mState[aGridAxis];
   8077      auto baselineAdjust = (state & ItemState::eContentBaseline)
   8078                                ? aGridItemInfo->mBaselineOffset[aGridAxis]
   8079                                : nscoord(0);
   8080      if (baselineAdjust < nscoord(0)) {
   8081        // This happens when the subtree overflows its track.
   8082        // XXX spec issue? it's unclear how to handle this.
   8083        baselineAdjust = nscoord(0);
   8084      } else if (state & ItemState::eLastBaseline) {
   8085        // FIXME: We're not setting the ItemState::eEndSideBaseline flag any
   8086        // more as the new baseline sharing group calculation handles most of
   8087        // the cases we need.  For non-masonry grids this flag was always set
   8088        // for LAST_BASELINE items, so we're just mimicking that behavior here.
   8089        // That said, masonry grids might not work 100% any more..
   8090        baselineAdjust = -baselineAdjust;
   8091      }
   8092      if (baselineAdjust != nscoord(0)) {
   8093        aChild->SetProperty(aProp, baselineAdjust);
   8094      } else {
   8095        aChild->RemoveProperty(aProp);
   8096      }
   8097    };
   8098    const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
   8099    SetProp(LogicalAxis::Block,
   8100            isOrthogonal ? IBaselinePadProperty() : BBaselinePadProperty());
   8101    SetProp(LogicalAxis::Inline,
   8102            isOrthogonal ? BBaselinePadProperty() : IBaselinePadProperty());
   8103  } else {
   8104    // By convention, for frames that perform CSS Box Alignment, we position
   8105    // placeholder children at the start corner of their alignment container,
   8106    // and in this case that's usually the grid's content-box.
   8107    // ("Usually" - the exception is when the grid *also* forms the
   8108    // abs.pos. containing block. In that case, the alignment container isn't
   8109    // the content-box -- it's some grid area instead.  But that case doesn't
   8110    // require any special handling here, because we handle it later using a
   8111    // special flag (ReflowInput::InitFlag::StaticPosIsCBOrigin) which will make
   8112    // us ignore the placeholder's position entirely.)
   8113    cb = aContentArea;
   8114    aChild->AddStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN);
   8115  }
   8116 
   8117  LogicalSize reflowSize(cb.Size(wm));
   8118  if (isConstrainedBSize) {
   8119    reflowSize.BSize(wm) = toFragmentainerEnd;
   8120  }
   8121  LogicalSize childCBSize = reflowSize.ConvertTo(childWM, wm);
   8122 
   8123  // Setup the ClampMarginBoxMinSize reflow flags and property, if needed.
   8124  ComputeSizeFlags csFlags;
   8125  if (aGridItemInfo) {
   8126    const auto childIAxisInWM = childWM.ConvertAxisTo(LogicalAxis::Inline, wm);
   8127    // Clamp during reflow if we're stretching in that axis.
   8128    if (GridItemShouldStretch(aChild, LogicalAxis::Inline)) {
   8129      if (aGridItemInfo->mState[childIAxisInWM] &
   8130          ItemState::eClampMarginBoxMinSize) {
   8131        csFlags += ComputeSizeFlag::IClampMarginBoxMinSize;
   8132      }
   8133    } else {
   8134      csFlags += ComputeSizeFlag::ShrinkWrap;
   8135    }
   8136 
   8137    const auto childBAxisInWM = GetOrthogonalAxis(childIAxisInWM);
   8138    if (GridItemShouldStretch(aChild, LogicalAxis::Block) &&
   8139        aGridItemInfo->mState[childBAxisInWM] &
   8140            ItemState::eClampMarginBoxMinSize) {
   8141      csFlags += ComputeSizeFlag::BClampMarginBoxMinSize;
   8142      aChild->SetProperty(BClampMarginBoxMinSizeProperty(),
   8143                          childCBSize.BSize(childWM));
   8144    } else {
   8145      aChild->RemoveProperty(BClampMarginBoxMinSizeProperty());
   8146    }
   8147 
   8148    if ((aGridItemInfo->mState[childIAxisInWM] &
   8149         ItemState::eContentBasedAutoMinSize)) {
   8150      csFlags += ComputeSizeFlag::IApplyAutoMinSize;
   8151    }
   8152  }
   8153 
   8154  if (!isConstrainedBSize) {
   8155    childCBSize.BSize(childWM) = NS_UNCONSTRAINEDSIZE;
   8156  }
   8157  LogicalSize percentBasis(cb.Size(wm).ConvertTo(childWM, wm));
   8158  ReflowInput childRI(pc, *aGridRI.mReflowInput, aChild, childCBSize,
   8159                      Some(percentBasis), {}, {}, csFlags);
   8160  childRI.mFlags.mIsTopOfPage =
   8161      aFragmentainer ? aFragmentainer->mIsTopOfPage : false;
   8162 
   8163  // FIXME (perf): It would be faster to do this only if the previous reflow of
   8164  // the child was a measuring reflow, and only if the child does some of the
   8165  // things that are affected by ComputeSizeFlag::IsGridMeasuringReflow.
   8166  childRI.SetBResize(true);
   8167  childRI.SetBResizeForPercentages(true);
   8168 
   8169  // If the child is stretching in its block axis, and we might be fragmenting
   8170  // it in that axis, then setup a frame property to tell
   8171  // nsBlockFrame::ComputeFinalSize the size.
   8172  if (isConstrainedBSize && !wm.IsOrthogonalTo(childWM)) {
   8173    const bool stretch =
   8174        childRI.mStylePosition
   8175            ->BSize(childWM, AnchorPosResolutionParams::From(&childRI))
   8176            ->IsAuto() &&
   8177        GridItemShouldStretch(aChild, LogicalAxis::Block);
   8178    if (stretch) {
   8179      aChild->SetProperty(FragStretchBSizeProperty(), *aStretchBSize);
   8180    } else {
   8181      aChild->RemoveProperty(FragStretchBSizeProperty());
   8182    }
   8183  }
   8184 
   8185  // We need the width of the child before we can correctly convert
   8186  // the writing-mode of its origin, so we reflow at (0, 0) using a dummy
   8187  // aContainerSize, and then pass the correct position to FinishReflowChild.
   8188  ReflowOutput childSize(childRI);
   8189  const nsSize dummyContainerSize;
   8190 
   8191  ReflowChild(aChild, pc, childSize, childRI, childWM, LogicalPoint(childWM),
   8192              dummyContainerSize, ReflowChildFlags::Default, aStatus);
   8193 
   8194  // childPos here initially represents the position that the child would have
   8195  // (expressed as an istart,bstart corner *in its own writing-mode*) if it
   8196  // were placed at the cb origin:
   8197  LogicalPoint childPos = cb.Origin(wm).ConvertRectOriginTo(
   8198      childWM, wm, childSize.PhysicalSize(), aContainerSize);
   8199 
   8200  // Apply align/justify-self and reflow again if that affects the size.
   8201  if (MOZ_LIKELY(isGridItem)) {
   8202    LogicalSize size = childSize.Size(childWM);  // from the ReflowChild()
   8203    auto applyItemSelfAlignment = [&](LogicalAxis aAxis, nscoord aCBSize) {
   8204      auto align =
   8205          childRI.mStylePosition->UsedSelfAlignment(aAxis, containerSC);
   8206      auto state = aGridItemInfo->mState[aAxis];
   8207      AlignJustifyFlags flags;
   8208      if (IsMasonry(aAxis)) {
   8209        // In a masonry axis, we inhibit applying 'stretch' and auto-margins
   8210        // here since AlignJustifyTracksInMasonryAxis deals with that.
   8211        // The only other {align,justify}-{self,content} values that have an
   8212        // effect are '[last] baseline', the rest behave as 'start'.
   8213        if (MOZ_LIKELY(!(state & ItemState::eSelfBaseline))) {
   8214          align = {StyleAlignFlags::START};
   8215        } else {
   8216          auto group = (state & ItemState::eFirstBaseline)
   8217                           ? BaselineSharingGroup::First
   8218                           : BaselineSharingGroup::Last;
   8219          auto itemStart = aGridItemInfo->mArea.LineRangeForAxis(aAxis).mStart;
   8220          aCBSize = aGridRI.TracksFor(aAxis)
   8221                        .mSizes[itemStart]
   8222                        .mBaselineSubtreeSize[group];
   8223        }
   8224        flags += AlignJustifyFlag::IgnoreAutoMargins;
   8225      } else if (state & ItemState::eContentBaseline) {
   8226        align = {(state & ItemState::eFirstBaseline)
   8227                     ? StyleAlignFlags::SELF_START
   8228                     : StyleAlignFlags::SELF_END};
   8229      }
   8230      if (aAxis == LogicalAxis::Block) {
   8231        AlignSelf(*aGridItemInfo, align, aCBSize, wm, childRI, size, flags,
   8232                  &childPos);
   8233      } else {
   8234        JustifySelf(*aGridItemInfo, align, aCBSize, wm, childRI, size, flags,
   8235                    &childPos);
   8236      }
   8237    };
   8238    if (aStatus.IsComplete()) {
   8239      applyItemSelfAlignment(LogicalAxis::Block,
   8240                             cb.BSize(wm) - consumedGridAreaBSize);
   8241    }
   8242    applyItemSelfAlignment(LogicalAxis::Inline, cb.ISize(wm));
   8243  }  // else, AbsoluteContainingBlock.cpp will handle align/justify-self.
   8244 
   8245  FinishReflowChild(aChild, pc, childSize, &childRI, childWM, childPos,
   8246                    aContainerSize, ReflowChildFlags::ApplyRelativePositioning);
   8247  ConsiderChildOverflow(aDesiredSize.mOverflowAreas, aChild);
   8248 }
   8249 
   8250 nscoord nsGridContainerFrame::ReflowInFragmentainer(
   8251    GridReflowInput& aGridRI, const LogicalRect& aContentArea,
   8252    ReflowOutput& aDesiredSize, nsReflowStatus& aStatus,
   8253    Fragmentainer& aFragmentainer, const nsSize& aContainerSize) {
   8254  MOZ_ASSERT(aStatus.IsEmpty());
   8255  MOZ_ASSERT(aGridRI.mReflowInput);
   8256 
   8257  // Collect our grid items and sort them in row order.  Collect placeholders
   8258  // and put them in a separate array.
   8259  nsTArray<const GridItemInfo*> sortedItems(aGridRI.mGridItems.Length());
   8260  nsTArray<nsIFrame*> placeholders(aGridRI.mAbsPosItems.Length());
   8261  aGridRI.mIter.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll);
   8262  for (; !aGridRI.mIter.AtEnd(); aGridRI.mIter.Next()) {
   8263    nsIFrame* child = *aGridRI.mIter;
   8264    if (!child->IsPlaceholderFrame()) {
   8265      const GridItemInfo* info = &aGridRI.mGridItems[aGridRI.mIter.ItemIndex()];
   8266      sortedItems.AppendElement(info);
   8267    } else {
   8268      placeholders.AppendElement(child);
   8269    }
   8270  }
   8271  // NOTE: We don't need stable_sort here, except in Masonry layout.  There are
   8272  // no dependencies on having content order between items on the same row in
   8273  // the code below in the non-Masonry case.
   8274  if (IsMasonry()) {
   8275    std::stable_sort(sortedItems.begin(), sortedItems.end(),
   8276                     GridItemInfo::IsStartRowLessThan);
   8277  } else {
   8278    std::sort(sortedItems.begin(), sortedItems.end(),
   8279              GridItemInfo::IsStartRowLessThan);
   8280  }
   8281 
   8282  // Reflow our placeholder children; they must all be complete.
   8283  for (auto child : placeholders) {
   8284    nsReflowStatus childStatus;
   8285    ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(),
   8286                      &aFragmentainer, aGridRI, aContentArea, aDesiredSize,
   8287                      childStatus);
   8288    MOZ_ASSERT(childStatus.IsComplete(),
   8289               "nsPlaceholderFrame should never need to be fragmented");
   8290  }
   8291 
   8292  // The available size for children - we'll set this to the edge of the last
   8293  // row in most cases below, but for now use the full size.
   8294  nscoord childAvailableSize = aFragmentainer.mToFragmentainerEnd;
   8295  const uint32_t startRow = aGridRI.mStartRow;
   8296  const uint32_t numRows = aGridRI.mRows.mSizes.Length();
   8297  bool isBDBClone = aGridRI.mReflowInput->mStyleBorder->mBoxDecorationBreak ==
   8298                    StyleBoxDecorationBreak::Clone;
   8299  nscoord bpBEnd = aGridRI.mBorderPadding.BEnd(aGridRI.mWM);
   8300 
   8301  // Set |endRow| to the first row that doesn't fit.
   8302  uint32_t endRow = numRows;
   8303  for (uint32_t row = startRow; row < numRows; ++row) {
   8304    auto& sz = aGridRI.mRows.mSizes[row];
   8305    const nscoord bEnd = sz.mPosition + sz.mBase;
   8306    nscoord remainingAvailableSize = childAvailableSize - bEnd;
   8307    if (remainingAvailableSize < 0 ||
   8308        (isBDBClone && remainingAvailableSize < bpBEnd)) {
   8309      endRow = row;
   8310      break;
   8311    }
   8312  }
   8313 
   8314  // Check for forced breaks on the items if available block-size for children
   8315  // is constrained. That is, ignore forced breaks if available block-size for
   8316  // children is unconstrained since our parent expected us to be fully
   8317  // complete.
   8318  bool isForcedBreak = false;
   8319  const bool avoidBreakInside = ShouldAvoidBreakInside(*aGridRI.mReflowInput);
   8320  if (childAvailableSize != NS_UNCONSTRAINEDSIZE) {
   8321    const bool isTopOfPage = aFragmentainer.mIsTopOfPage;
   8322    for (const GridItemInfo* info : sortedItems) {
   8323      uint32_t itemStartRow = info->mArea.mRows.mStart;
   8324      if (itemStartRow == endRow) {
   8325        break;
   8326      }
   8327      const auto* disp = info->mFrame->StyleDisplay();
   8328      if (disp->BreakBefore()) {
   8329        // Propagate break-before on the first row to the container unless we're
   8330        // already at top-of-page.
   8331        if ((itemStartRow == 0 && !isTopOfPage) || avoidBreakInside) {
   8332          aStatus.SetInlineLineBreakBeforeAndReset();
   8333          return aGridRI.mFragBStart;
   8334        }
   8335        if ((itemStartRow > startRow ||
   8336             (itemStartRow == startRow && !isTopOfPage)) &&
   8337            itemStartRow < endRow) {
   8338          endRow = itemStartRow;
   8339          isForcedBreak = true;
   8340          // reset any BREAK_AFTER we found on an earlier item
   8341          aStatus.Reset();
   8342          break;  // we're done since the items are sorted in row order
   8343        }
   8344      }
   8345      uint32_t itemEndRow = info->mArea.mRows.mEnd;
   8346      if (disp->BreakAfter()) {
   8347        if (itemEndRow != numRows) {
   8348          if (itemEndRow > startRow && itemEndRow < endRow) {
   8349            endRow = itemEndRow;
   8350            isForcedBreak = true;
   8351            // No "break;" here since later items with break-after may have
   8352            // a shorter span.
   8353          }
   8354        } else {
   8355          // Propagate break-after on the last row to the container, we may
   8356          // still find a break-before on this row though (and reset aStatus).
   8357          aStatus.SetInlineLineBreakAfter();  // tentative
   8358        }
   8359      }
   8360    }
   8361 
   8362    // Consume at least one row in each fragment until we have consumed them
   8363    // all. Except for the first row if there's a break opportunity before it.
   8364    if (startRow == endRow && startRow != numRows &&
   8365        (startRow != 0 || !aFragmentainer.mCanBreakAtStart)) {
   8366      ++endRow;
   8367    }
   8368 
   8369    // Honor break-inside:avoid if we can't fit all rows.
   8370    if (avoidBreakInside && endRow < numRows) {
   8371      aStatus.SetInlineLineBreakBeforeAndReset();
   8372      return aGridRI.mFragBStart;
   8373    }
   8374  }
   8375 
   8376  // Calculate the block-size including this fragment.
   8377  nscoord bEndRow =
   8378      aGridRI.mRows.GridLineEdge(endRow, GridLineSide::BeforeGridGap);
   8379  nscoord bSize;
   8380  if (aFragmentainer.mIsAutoBSize) {
   8381    // We only apply min-bsize once all rows are complete (when bsize is auto).
   8382    if (endRow < numRows) {
   8383      bSize = bEndRow;
   8384      auto clampedBSize = ClampToCSSMaxBSize(bSize, aGridRI.mReflowInput);
   8385      if (MOZ_UNLIKELY(clampedBSize != bSize)) {
   8386        // We apply max-bsize in all fragments though.
   8387        bSize = clampedBSize;
   8388      } else if (!isBDBClone) {
   8389        // The max-bsize won't make this fragment COMPLETE, so the block-end
   8390        // border will be in a later fragment.
   8391        bpBEnd = 0;
   8392      }
   8393    } else {
   8394      bSize = aGridRI.mReflowInput->ApplyMinMaxBSize(bEndRow);
   8395    }
   8396  } else {
   8397    bSize = aGridRI.mReflowInput->ApplyMinMaxBSize(
   8398        aGridRI.mReflowInput->ComputedBSize());
   8399  }
   8400 
   8401  // Check for overflow and set aStatus INCOMPLETE if so.
   8402  bool overflow = bSize + bpBEnd > childAvailableSize;
   8403  if (overflow) {
   8404    if (avoidBreakInside) {
   8405      aStatus.SetInlineLineBreakBeforeAndReset();
   8406      return aGridRI.mFragBStart;
   8407    }
   8408    bool breakAfterLastRow = endRow == numRows && aFragmentainer.mCanBreakAtEnd;
   8409    if (breakAfterLastRow) {
   8410      MOZ_ASSERT(bEndRow < bSize, "bogus aFragmentainer.mCanBreakAtEnd");
   8411      nscoord availableSize = childAvailableSize;
   8412      if (isBDBClone) {
   8413        availableSize -= bpBEnd;
   8414      }
   8415      // Pretend we have at least 1px available size, otherwise we'll never make
   8416      // progress in consuming our bSize.
   8417      availableSize =
   8418          std::max(availableSize, aGridRI.mFragBStart + AppUnitsPerCSSPixel());
   8419      // Fill the fragmentainer, but not more than our desired block-size and
   8420      // at least to the size of the last row (even if that overflows).
   8421      nscoord newBSize = std::min(bSize, availableSize);
   8422      newBSize = std::max(newBSize, bEndRow);
   8423      // If it's just the border+padding that is overflowing and we have
   8424      // box-decoration-break:clone then we are technically COMPLETE.  There's
   8425      // no point in creating another zero-bsize fragment in this case.
   8426      if (newBSize < bSize || !isBDBClone) {
   8427        aStatus.SetIncomplete();
   8428      }
   8429      bSize = newBSize;
   8430    } else if (bSize <= bEndRow && startRow + 1 < endRow) {
   8431      if (endRow == numRows) {
   8432        // We have more than one row in this fragment, so we can break before
   8433        // the last row instead.
   8434        --endRow;
   8435        bEndRow =
   8436            aGridRI.mRows.GridLineEdge(endRow, GridLineSide::BeforeGridGap);
   8437        bSize = bEndRow;
   8438        if (aFragmentainer.mIsAutoBSize) {
   8439          bSize = ClampToCSSMaxBSize(bSize, aGridRI.mReflowInput);
   8440        }
   8441      }
   8442      aStatus.SetIncomplete();
   8443    } else if (endRow < numRows) {
   8444      bSize = ClampToCSSMaxBSize(bEndRow, aGridRI.mReflowInput, &aStatus);
   8445    }  // else - no break opportunities.
   8446  } else {
   8447    // Even though our block-size fits we need to honor forced breaks, or if
   8448    // a row doesn't fit in an auto-sized container (unless it's constrained
   8449    // by a max-bsize which make us overflow-incomplete).
   8450    if (endRow < numRows &&
   8451        (isForcedBreak || (aFragmentainer.mIsAutoBSize && bEndRow == bSize))) {
   8452      bSize = ClampToCSSMaxBSize(bEndRow, aGridRI.mReflowInput, &aStatus);
   8453    }
   8454  }
   8455 
   8456  // If we can't fit all rows then we're at least overflow-incomplete.
   8457  if (endRow < numRows) {
   8458    childAvailableSize = bEndRow;
   8459    if (aStatus.IsComplete()) {
   8460      aStatus.SetOverflowIncomplete();
   8461      aStatus.SetNextInFlowNeedsReflow();
   8462    }
   8463  } else {
   8464    // Children always have the full size of the rows in this fragment.
   8465    childAvailableSize = std::max(childAvailableSize, bEndRow);
   8466  }
   8467 
   8468  return ReflowRowsInFragmentainer(aGridRI, aContentArea, aDesiredSize, aStatus,
   8469                                   aFragmentainer, aContainerSize, sortedItems,
   8470                                   startRow, endRow, bSize, childAvailableSize);
   8471 }
   8472 
   8473 nscoord nsGridContainerFrame::ReflowRowsInFragmentainer(
   8474    GridReflowInput& aGridRI, const LogicalRect& aContentArea,
   8475    ReflowOutput& aDesiredSize, nsReflowStatus& aStatus,
   8476    Fragmentainer& aFragmentainer, const nsSize& aContainerSize,
   8477    const nsTArray<const GridItemInfo*>& aSortedItems, uint32_t aStartRow,
   8478    uint32_t aEndRow, nscoord aBSize, nscoord aAvailableSize) {
   8479  FrameHashtable pushedItems;
   8480  FrameHashtable incompleteItems;
   8481  FrameHashtable overflowIncompleteItems;
   8482  Maybe<nsTArray<nscoord>> masonryAxisPos;
   8483  const auto rowCount = aGridRI.mRows.mSizes.Length();
   8484  nscoord masonryAxisGap = 0;
   8485  const auto wm = aGridRI.mWM;
   8486  const bool isColMasonry = IsColMasonry();
   8487  if (isColMasonry) {
   8488    for (auto& sz : aGridRI.mCols.mSizes) {
   8489      sz.mPosition = 0;
   8490    }
   8491    masonryAxisGap = nsLayoutUtils::ResolveGapToLength(
   8492        aGridRI.mGridStyle->mColumnGap, aContentArea.ISize(wm));
   8493    aGridRI.mCols.mGridGap = masonryAxisGap;
   8494    masonryAxisPos.emplace(rowCount);
   8495    masonryAxisPos->SetLength(rowCount);
   8496    PodZero(masonryAxisPos->Elements(), rowCount);
   8497  }
   8498  bool isBDBClone = aGridRI.mReflowInput->mStyleBorder->mBoxDecorationBreak ==
   8499                    StyleBoxDecorationBreak::Clone;
   8500  bool didGrowRow = false;
   8501  // As we walk across rows, we track whether the current row is at the top
   8502  // of its grid-fragment, to help decide whether we can break before it. When
   8503  // this function starts, our row is at the top of the current fragment if:
   8504  //  - we're starting with a nonzero row (i.e. we're a continuation)
   8505  // OR:
   8506  //  - we're starting with the first row, & we're not allowed to break before
   8507  //    it (which makes it effectively at the top of its grid-fragment).
   8508  bool isRowTopOfPage = aStartRow != 0 || !aFragmentainer.mCanBreakAtStart;
   8509  const bool isStartRowTopOfPage = isRowTopOfPage;
   8510  // Save our full available size for later.
   8511  const nscoord gridAvailableSize = aFragmentainer.mToFragmentainerEnd;
   8512  // Propagate the constrained size to our children.
   8513  aFragmentainer.mToFragmentainerEnd = aAvailableSize;
   8514  // Reflow the items in row order up to |aEndRow| and push items after that.
   8515  uint32_t row = 0;
   8516  // |i| is intentionally signed, so we can set it to -1 to restart the loop.
   8517  for (int32_t i = 0, len = aSortedItems.Length(); i < len; ++i) {
   8518    const GridItemInfo* const info = aSortedItems[i];
   8519    nsIFrame* child = info->mFrame;
   8520    row = info->mArea.mRows.mStart;
   8521    MOZ_ASSERT(child->GetPrevInFlow() ? row < aStartRow : row >= aStartRow,
   8522               "unexpected child start row");
   8523    if (row >= aEndRow) {
   8524      pushedItems.Insert(child);
   8525      continue;
   8526    }
   8527 
   8528    bool rowCanGrow = false;
   8529    nscoord maxRowSize = 0;
   8530    if (row >= aStartRow) {
   8531      if (row > aStartRow) {
   8532        isRowTopOfPage = false;
   8533      }
   8534      // Can we grow this row?  Only consider span=1 items per spec...
   8535      rowCanGrow = !didGrowRow && info->mArea.mRows.Extent() == 1;
   8536      if (rowCanGrow) {
   8537        auto& sz = aGridRI.mRows.mSizes[row];
   8538        // and only min-/max-content rows or flex rows in an auto-sized
   8539        // container
   8540        rowCanGrow = (sz.mState & TrackSize::eMinOrMaxContentMinSizing) ||
   8541                     ((sz.mState & TrackSize::eFlexMaxSizing) &&
   8542                      aFragmentainer.mIsAutoBSize);
   8543        if (rowCanGrow) {
   8544          if (isBDBClone) {
   8545            maxRowSize = gridAvailableSize - aGridRI.mBorderPadding.BEnd(wm);
   8546          } else {
   8547            maxRowSize = gridAvailableSize;
   8548          }
   8549          maxRowSize -= sz.mPosition;
   8550          // ...and only if there is space for it to grow.
   8551          rowCanGrow = maxRowSize > sz.mBase;
   8552        }
   8553      }
   8554    }
   8555 
   8556    if (isColMasonry) {
   8557      const auto& cols = info->mArea.mCols;
   8558      MOZ_ASSERT((cols.mStart == 0 || cols.mStart == 1) && cols.Extent() == 1);
   8559      aGridRI.mCols.mSizes[cols.mStart].mPosition = masonryAxisPos.ref()[row];
   8560    }
   8561 
   8562    // aFragmentainer.mIsTopOfPage is propagated to the child reflow input.
   8563    // When it's false the child may request InlineBreak::Before.  We set it
   8564    // to false when the row is growable (as determined in the CSS Grid
   8565    // Fragmentation spec) and there is a non-zero space between it and the
   8566    // fragmentainer end (that can be used to grow it).  If the child reports
   8567    // a forced break in this case, we grow this row to fill the fragment and
   8568    // restart the loop.  We also restart the loop with |aEndRow = row|
   8569    // (but without growing any row) for a InlineBreak::Before child if it spans
   8570    // beyond the last row in this fragment.  This is to avoid fragmenting it.
   8571    // We only restart the loop once.
   8572    aFragmentainer.mIsTopOfPage = isRowTopOfPage && !rowCanGrow;
   8573    nsReflowStatus childStatus;
   8574    // Pass along how much to stretch this fragment, in case it's needed.
   8575    nscoord bSize =
   8576        aGridRI.mRows.GridLineEdge(std::min(aEndRow, info->mArea.mRows.mEnd),
   8577                                   GridLineSide::BeforeGridGap) -
   8578        aGridRI.mRows.GridLineEdge(std::max(aStartRow, row),
   8579                                   GridLineSide::AfterGridGap);
   8580    ReflowInFlowChild(child, info, aContainerSize, Some(bSize), &aFragmentainer,
   8581                      aGridRI, aContentArea, aDesiredSize, childStatus);
   8582    MOZ_ASSERT(childStatus.IsInlineBreakBefore() ||
   8583                   !childStatus.IsFullyComplete() || !child->GetNextInFlow(),
   8584               "fully-complete reflow should destroy any NIFs");
   8585 
   8586    if (childStatus.IsInlineBreakBefore()) {
   8587      MOZ_ASSERT(
   8588          !child->GetPrevInFlow(),
   8589          "continuations should never report InlineBreak::Before status");
   8590      MOZ_ASSERT(!aFragmentainer.mIsTopOfPage,
   8591                 "got IsInlineBreakBefore() at top of page");
   8592      if (!didGrowRow) {
   8593        if (rowCanGrow) {
   8594          // Grow this row and restart with the next row as |aEndRow|.
   8595          aGridRI.mRows.ResizeRow(row, maxRowSize);
   8596          if (aGridRI.mSharedGridData) {
   8597            aGridRI.mSharedGridData->mRows.ResizeRow(row, maxRowSize);
   8598          }
   8599          didGrowRow = true;
   8600          aEndRow = row + 1;  // growing this row makes the next one not fit
   8601          i = -1;             // i == 0 after the next loop increment
   8602          isRowTopOfPage = isStartRowTopOfPage;
   8603          overflowIncompleteItems.Clear();
   8604          incompleteItems.Clear();
   8605          nscoord bEndRow =
   8606              aGridRI.mRows.GridLineEdge(aEndRow, GridLineSide::BeforeGridGap);
   8607          aFragmentainer.mToFragmentainerEnd = bEndRow;
   8608          if (aFragmentainer.mIsAutoBSize) {
   8609            aBSize =
   8610                ClampToCSSMaxBSize(bEndRow, aGridRI.mReflowInput, &aStatus);
   8611          } else if (aStatus.IsIncomplete()) {
   8612            aBSize = aGridRI.mReflowInput->ApplyMinMaxBSize(
   8613                aGridRI.mReflowInput->ComputedBSize());
   8614            aBSize = std::min(bEndRow, aBSize);
   8615          }
   8616          continue;
   8617        }
   8618 
   8619        if (!isRowTopOfPage) {
   8620          // We can break before this row - restart with it as the new end row.
   8621          aEndRow = row;
   8622          aBSize =
   8623              aGridRI.mRows.GridLineEdge(aEndRow, GridLineSide::BeforeGridGap);
   8624          i = -1;  // i == 0 after the next loop increment
   8625          isRowTopOfPage = isStartRowTopOfPage;
   8626          overflowIncompleteItems.Clear();
   8627          incompleteItems.Clear();
   8628          aStatus.SetIncomplete();
   8629          continue;
   8630        }
   8631        NS_ERROR("got InlineBreak::Before at top-of-page");
   8632        childStatus.Reset();
   8633      } else {
   8634        // We got InlineBreak::Before again after growing the row - this can
   8635        // happen if the child isn't splittable, e.g. some form controls.
   8636        childStatus.Reset();
   8637        if (child->GetNextInFlow()) {
   8638          // The child already has a fragment, so we know it's splittable.
   8639          childStatus.SetIncomplete();
   8640        }  // else, report that it's complete
   8641      }
   8642    } else if (childStatus.IsInlineBreakAfter()) {
   8643      MOZ_ASSERT_UNREACHABLE("unexpected child reflow status");
   8644    }
   8645 
   8646    MOZ_ASSERT(!childStatus.IsInlineBreakBefore(),
   8647               "should've handled InlineBreak::Before above");
   8648    if (childStatus.IsIncomplete()) {
   8649      incompleteItems.Insert(child);
   8650    } else if (!childStatus.IsFullyComplete()) {
   8651      overflowIncompleteItems.Insert(child);
   8652    }
   8653    if (isColMasonry) {
   8654      auto childWM = child->GetWritingMode();
   8655      const auto childAxis = wm.ConvertAxisTo(LogicalAxis::Inline, childWM);
   8656      auto normalPos = child->GetLogicalNormalPosition(wm, aContainerSize);
   8657      auto sz =
   8658          childAxis == LogicalAxis::Block ? child->BSize() : child->ISize();
   8659      auto pos = normalPos.Pos(LogicalAxis::Inline, wm) + sz +
   8660                 child->GetLogicalUsedMargin(childWM).End(childAxis, childWM);
   8661      masonryAxisPos.ref()[row] =
   8662          pos + masonryAxisGap - aContentArea.Start(LogicalAxis::Inline, wm);
   8663    }
   8664  }
   8665 
   8666  // Record a break before |aEndRow|.
   8667  aGridRI.mNextFragmentStartRow = aEndRow;
   8668  if (aEndRow < rowCount) {
   8669    aGridRI.mRows.BreakBeforeRow(aEndRow);
   8670    if (aGridRI.mSharedGridData) {
   8671      aGridRI.mSharedGridData->mRows.BreakBeforeRow(aEndRow);
   8672    }
   8673  }
   8674 
   8675  const bool childrenMoved = PushIncompleteChildren(
   8676      pushedItems, incompleteItems, overflowIncompleteItems);
   8677  if (childrenMoved && aStatus.IsComplete()) {
   8678    aStatus.SetOverflowIncomplete();
   8679    aStatus.SetNextInFlowNeedsReflow();
   8680  }
   8681  if (!pushedItems.IsEmpty()) {
   8682    AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
   8683    // NOTE since we messed with our child list here, we intentionally
   8684    // make aState.mIter invalid to avoid any use of it after this point.
   8685    aGridRI.mIter.Invalidate();
   8686  }
   8687  if (!incompleteItems.IsEmpty()) {
   8688    // NOTE since we messed with our child list here, we intentionally
   8689    // make aState.mIter invalid to avoid any use of it after this point.
   8690    aGridRI.mIter.Invalidate();
   8691  }
   8692 
   8693  if (isColMasonry) {
   8694    nscoord maxSize = 0;
   8695    for (auto pos : masonryAxisPos.ref()) {
   8696      maxSize = std::max(maxSize, pos);
   8697    }
   8698    maxSize = std::max(nscoord(0), maxSize - masonryAxisGap);
   8699    aGridRI.AlignJustifyContentInMasonryAxis(maxSize, aContentArea.ISize(wm));
   8700  }
   8701 
   8702  return aBSize;
   8703 }
   8704 
   8705 // Here's a brief overview of how Masonry layout is implemented:
   8706 // We setup two synthetic tracks in the Masonry axis so that the Reflow code
   8707 // can treat it the same as for normal grid layout.  The first track is
   8708 // fixed (during item placement/layout) at the content box start and contains
   8709 // the start items for each grid-axis track.  The second track contains
   8710 // all other items and is moved to the position where we want to position
   8711 // the currently laid out item (like a sliding window as we place items).
   8712 // Once item layout is done, the tracks are resized to be the size of
   8713 // the "masonry box", which is the offset from the content box start to
   8714 // the margin-box end of the item that is furthest away (this happens in
   8715 // AlignJustifyContentInMasonryAxis() called at the end of this method).
   8716 // This is to prepare for AlignJustifyTracksInMasonryAxis, which is called
   8717 // later by our caller.
   8718 // Both tracks store their first-/last-baseline group offsets as usual.
   8719 // The first-baseline of the start track, and the last-baseline of the last
   8720 // track (if they exist) are exported as the grid container's baselines, or
   8721 // we fall back to picking an item's baseline (all this is per normal grid
   8722 // layout).  There's a slight difference in which items belongs to which
   8723 // group though - see InitializeItemBaselinesInMasonryAxis for details.
   8724 // This method returns the "masonry box" size (in the masonry axis).
   8725 nscoord nsGridContainerFrame::MasonryLayout(GridReflowInput& aGridRI,
   8726                                            const LogicalRect& aContentArea,
   8727                                            SizingConstraint aConstraint,
   8728                                            ReflowOutput& aDesiredSize,
   8729                                            nsReflowStatus& aStatus,
   8730                                            Fragmentainer* aFragmentainer,
   8731                                            const nsSize& aContainerSize) {
   8732  using BaselineAlignmentSet = Tracks::BaselineAlignmentSet;
   8733 
   8734  auto recordAutoPlacement = [this, &aGridRI](GridItemInfo* aItem,
   8735                                              LogicalAxis aGridAxis) {
   8736    // When we're auto-placing an item in a continuation we need to record
   8737    // the placement in mSharedGridData.
   8738    if (MOZ_UNLIKELY(aGridRI.mSharedGridData && GetPrevInFlow()) &&
   8739        (aItem->mState[aGridAxis] & ItemState::eAutoPlacement)) {
   8740      auto* child = aItem->mFrame;
   8741      MOZ_RELEASE_ASSERT(!child->GetPrevInFlow(),
   8742                         "continuations should never be auto-placed");
   8743      for (auto& sharedItem : aGridRI.mSharedGridData->mGridItems) {
   8744        if (sharedItem.mFrame == child) {
   8745          sharedItem.mArea.LineRangeForAxis(aGridAxis) =
   8746              aItem->mArea.LineRangeForAxis(aGridAxis);
   8747          MOZ_ASSERT(sharedItem.mState[aGridAxis] & ItemState::eAutoPlacement);
   8748          sharedItem.mState[aGridAxis] &= ~ItemState::eAutoPlacement;
   8749          break;
   8750        }
   8751      }
   8752    }
   8753    aItem->mState[aGridAxis] &= ~ItemState::eAutoPlacement;
   8754  };
   8755 
   8756  // Collect our grid items and sort them in grid order.
   8757  nsTArray<GridItemInfo*> sortedItems(aGridRI.mGridItems.Length());
   8758  aGridRI.mIter.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll);
   8759  size_t absposIndex = 0;
   8760  const LogicalAxis masonryAxis =
   8761      IsMasonry(LogicalAxis::Block) ? LogicalAxis::Block : LogicalAxis::Inline;
   8762  const auto wm = aGridRI.mWM;
   8763  for (; !aGridRI.mIter.AtEnd(); aGridRI.mIter.Next()) {
   8764    nsIFrame* child = *aGridRI.mIter;
   8765    if (MOZ_LIKELY(!child->IsPlaceholderFrame())) {
   8766      GridItemInfo* item = &aGridRI.mGridItems[aGridRI.mIter.ItemIndex()];
   8767      sortedItems.AppendElement(item);
   8768    } else if (aConstraint == SizingConstraint::NoConstraint) {
   8769      // (we only collect placeholders in the NoConstraint case since they
   8770      //  don't affect intrinsic sizing in any way)
   8771      GridItemInfo* item = nullptr;
   8772      auto* ph = static_cast<nsPlaceholderFrame*>(child);
   8773      if (ph->GetOutOfFlowFrame()->GetParent() == this) {
   8774        item = &aGridRI.mAbsPosItems[absposIndex++];
   8775        MOZ_RELEASE_ASSERT(item->mFrame == ph->GetOutOfFlowFrame());
   8776        auto masonryStart = item->mArea.LineRangeForAxis(masonryAxis).mStart;
   8777        // If the item was placed by the author at line 1 (masonryStart == 0)
   8778        // then include it to be placed at the masonry-box start.  If it's
   8779        // auto-placed and has an `auto` inset value in the masonry axis then
   8780        // we include it to be placed after the last grid item with the same
   8781        // grid-axis start track.
   8782        // XXXmats this is all a bit experimental at this point, pending a spec
   8783        const auto masonrySide = masonryAxis == LogicalAxis::Inline
   8784                                     ? LogicalSide::IStart
   8785                                     : LogicalSide::BStart;
   8786        if (masonryStart == 0 ||
   8787            (masonryStart == kAutoLine &&
   8788             item->mFrame->StylePosition()
   8789                 ->GetAnchorResolvedInset(
   8790                     masonrySide, wm,
   8791                     AnchorPosOffsetResolutionParams::UseCBFrameSize(
   8792                         AnchorPosResolutionParams::From(item->mFrame)))
   8793                 ->IsAuto())) {
   8794          sortedItems.AppendElement(item);
   8795        } else {
   8796          item = nullptr;
   8797        }
   8798      }
   8799      if (!item) {
   8800        // It wasn't included above - just reflow it and be done with it.
   8801        nsReflowStatus childStatus;
   8802        ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(), nullptr,
   8803                          aGridRI, aContentArea, aDesiredSize, childStatus);
   8804      }
   8805    }
   8806  }
   8807  const auto masonryAutoFlow = aGridRI.mGridStyle->mMasonryAutoFlow;
   8808  const bool definiteFirst =
   8809      masonryAutoFlow.order == StyleMasonryItemOrder::DefiniteFirst;
   8810  if (masonryAxis == LogicalAxis::Block) {
   8811    std::stable_sort(sortedItems.begin(), sortedItems.end(),
   8812                     definiteFirst ? GridItemInfo::RowMasonryDefiniteFirst
   8813                                   : GridItemInfo::RowMasonryOrdered);
   8814  } else {
   8815    std::stable_sort(sortedItems.begin(), sortedItems.end(),
   8816                     definiteFirst ? GridItemInfo::ColMasonryDefiniteFirst
   8817                                   : GridItemInfo::ColMasonryOrdered);
   8818  }
   8819 
   8820  FrameHashtable pushedItems;
   8821  FrameHashtable incompleteItems;
   8822  FrameHashtable overflowIncompleteItems;
   8823  nscoord toFragmentainerEnd = nscoord_MAX;
   8824  nscoord fragStartPos = aGridRI.mFragBStart;
   8825  const bool avoidBreakInside =
   8826      aFragmentainer && ShouldAvoidBreakInside(*aGridRI.mReflowInput);
   8827  const bool isTopOfPageAtStart =
   8828      aFragmentainer && aFragmentainer->mIsTopOfPage;
   8829  if (aFragmentainer) {
   8830    toFragmentainerEnd = std::max(0, aFragmentainer->mToFragmentainerEnd);
   8831  }
   8832  const LogicalAxis gridAxis = GetOrthogonalAxis(masonryAxis);
   8833  const auto gridAxisTrackCount = aGridRI.TracksFor(gridAxis).mSizes.Length();
   8834  auto& masonryTracks = aGridRI.TracksFor(masonryAxis);
   8835  auto& masonrySizes = masonryTracks.mSizes;
   8836  MOZ_ASSERT(masonrySizes.Length() == 2);
   8837  for (auto& sz : masonrySizes) {
   8838    sz.mPosition = fragStartPos;
   8839  }
   8840  // The current running position for each grid-axis track where the next item
   8841  // should be positioned.  When an item is placed we'll update the tracks it
   8842  // spans to the end of its margin box + 'gap'.
   8843  nsTArray<nscoord> currentPos(gridAxisTrackCount);
   8844  currentPos.SetLength(gridAxisTrackCount);
   8845  for (auto& sz : currentPos) {
   8846    sz = fragStartPos;
   8847  }
   8848  nsTArray<nscoord> lastPos(currentPos.Clone());
   8849  nsTArray<GridItemInfo*> lastItems(gridAxisTrackCount);
   8850  lastItems.SetLength(gridAxisTrackCount);
   8851  PodZero(lastItems.Elements(), gridAxisTrackCount);
   8852  const nscoord gap = nsLayoutUtils::ResolveGapToLength(
   8853      masonryAxis == LogicalAxis::Block ? aGridRI.mGridStyle->mRowGap
   8854                                        : aGridRI.mGridStyle->mColumnGap,
   8855      masonryTracks.mContentBoxSize);
   8856  masonryTracks.mGridGap = gap;
   8857  uint32_t cursor = 0;
   8858  const auto containerToMasonryBoxOffset =
   8859      fragStartPos - aContentArea.Start(masonryAxis, wm);
   8860  const bool isPack = masonryAutoFlow.placement == StyleMasonryPlacement::Pack;
   8861  bool didAlignStartAlignedFirstItems = false;
   8862 
   8863  // Return true if any of the lastItems in aRange are baseline-aligned in
   8864  // the masonry axis.
   8865  auto lastItemHasBaselineAlignment = [&](const LineRange& aRange) {
   8866    for (auto i : aRange.Range()) {
   8867      if (auto* child = lastItems[i] ? lastItems[i]->mFrame : nullptr) {
   8868        const auto& pos = child->StylePosition();
   8869        auto selfAlignment = pos->UsedSelfAlignment(masonryAxis, this->Style());
   8870        if (selfAlignment == StyleAlignFlags::BASELINE ||
   8871            selfAlignment == StyleAlignFlags::LAST_BASELINE) {
   8872          return true;
   8873        }
   8874        auto childAxis = masonryAxis;
   8875        if (child->GetWritingMode().IsOrthogonalTo(wm)) {
   8876          childAxis = gridAxis;
   8877        }
   8878        auto contentAlignment = pos->UsedContentAlignment(childAxis).primary;
   8879        if (contentAlignment == StyleAlignFlags::BASELINE ||
   8880            contentAlignment == StyleAlignFlags::LAST_BASELINE) {
   8881          return true;
   8882        }
   8883      }
   8884    }
   8885    return false;
   8886  };
   8887 
   8888  // Resolve aItem's placement, unless it's definite already.  Return its
   8889  // masonry axis position with that placement.
   8890  auto placeItem = [&](GridItemInfo* aItem) -> nscoord {
   8891    auto& masonryAxisRange = aItem->mArea.LineRangeForAxis(masonryAxis);
   8892    MOZ_ASSERT(masonryAxisRange.mStart != 0, "item placement is already final");
   8893    auto& gridAxisRange = aItem->mArea.LineRangeForAxis(gridAxis);
   8894    bool isAutoPlaced = aItem->mState[gridAxis] & ItemState::eAutoPlacement;
   8895    uint32_t start = isAutoPlaced ? 0 : gridAxisRange.mStart;
   8896    if (isAutoPlaced && !isPack) {
   8897      start = cursor;
   8898      isAutoPlaced = false;
   8899    }
   8900    const uint32_t extent = gridAxisRange.Extent();
   8901    if (start + extent > gridAxisTrackCount) {
   8902      // Note that this will only happen to auto-placed items since the grid is
   8903      // always wide enough to fit other items.
   8904      start = 0;
   8905    }
   8906    // This keeps track of the smallest `maxPosForRange` value that
   8907    // we discover in the loop below:
   8908    nscoord minPos = nscoord_MAX;
   8909    MOZ_ASSERT(extent <= gridAxisTrackCount);
   8910    const uint32_t iEnd = gridAxisTrackCount + 1 - extent;
   8911    for (uint32_t i = start; i < iEnd; ++i) {
   8912      // Find the max `currentPos` value for the tracks that we would span
   8913      // if we were to use `i` as our start track:
   8914      nscoord maxPosForRange = 0;
   8915      for (auto j = i, jEnd = j + extent; j < jEnd; ++j) {
   8916        maxPosForRange = std::max(currentPos[j], maxPosForRange);
   8917      }
   8918      if (maxPosForRange < minPos) {
   8919        minPos = maxPosForRange;
   8920        start = i;
   8921      }
   8922      if (!isAutoPlaced) {
   8923        break;
   8924      }
   8925    }
   8926    gridAxisRange.mStart = start;
   8927    gridAxisRange.mEnd = start + extent;
   8928    bool isFirstItem = true;
   8929    for (uint32_t i : gridAxisRange.Range()) {
   8930      if (lastItems[i]) {
   8931        isFirstItem = false;
   8932        break;
   8933      }
   8934    }
   8935    // If this is the first item in its spanned grid tracks, then place it in
   8936    // the first masonry track. Otherwise, place it in the second masonry track.
   8937    masonryAxisRange.mStart = isFirstItem ? 0 : 1;
   8938    masonryAxisRange.mEnd = masonryAxisRange.mStart + 1;
   8939    return minPos;
   8940  };
   8941 
   8942  // Handle the resulting reflow status after reflowing aItem.
   8943  // This may set aStatus to BreakBefore which the caller is expected
   8944  // to handle by returning from MasonryLayout.
   8945  // @return true if this item should consume all remaining space
   8946  auto handleChildStatus = [&](GridItemInfo* aItem,
   8947                               const nsReflowStatus& aChildStatus) {
   8948    bool result = false;
   8949    if (MOZ_UNLIKELY(aFragmentainer)) {
   8950      auto* child = aItem->mFrame;
   8951      if (!aChildStatus.IsComplete() || aChildStatus.IsInlineBreakBefore() ||
   8952          aChildStatus.IsInlineBreakAfter() ||
   8953          child->StyleDisplay()->BreakAfter()) {
   8954        if (!isTopOfPageAtStart && avoidBreakInside) {
   8955          aStatus.SetInlineLineBreakBeforeAndReset();
   8956          return result;
   8957        }
   8958        result = true;
   8959      }
   8960      if (aChildStatus.IsInlineBreakBefore()) {
   8961        aStatus.SetIncomplete();
   8962        pushedItems.Insert(child);
   8963      } else if (aChildStatus.IsIncomplete()) {
   8964        recordAutoPlacement(aItem, gridAxis);
   8965        aStatus.SetIncomplete();
   8966        incompleteItems.Insert(child);
   8967      } else if (!aChildStatus.IsFullyComplete()) {
   8968        recordAutoPlacement(aItem, gridAxis);
   8969        overflowIncompleteItems.Insert(child);
   8970      }
   8971    }
   8972    return result;
   8973  };
   8974 
   8975  // @return the distance from the masonry-box start to the end of the margin-
   8976  // box of aChild
   8977  auto offsetToMarginBoxEnd = [&](nsIFrame* aChild) {
   8978    auto childWM = aChild->GetWritingMode();
   8979    auto childAxis = !childWM.IsOrthogonalTo(wm) ? masonryAxis : gridAxis;
   8980    auto normalPos = aChild->GetLogicalNormalPosition(wm, aContainerSize);
   8981    auto sz =
   8982        childAxis == LogicalAxis::Block ? aChild->BSize() : aChild->ISize();
   8983    return containerToMasonryBoxOffset + normalPos.Pos(masonryAxis, wm) + sz +
   8984           aChild->GetLogicalUsedMargin(childWM).End(childAxis, childWM);
   8985  };
   8986 
   8987  // Apply baseline alignment to items belonging to the given set.
   8988  nsTArray<Tracks::ItemBaselineData> firstBaselineItems;
   8989  nsTArray<Tracks::ItemBaselineData> lastBaselineItems;
   8990  auto applyBaselineAlignment = [&](BaselineAlignmentSet aSet) {
   8991    firstBaselineItems.ClearAndRetainStorage();
   8992    lastBaselineItems.ClearAndRetainStorage();
   8993    masonryTracks.InitializeItemBaselinesInMasonryAxis(
   8994        aGridRI, aGridRI.mGridItems, aSet, aContainerSize, currentPos,
   8995        firstBaselineItems, lastBaselineItems);
   8996 
   8997    bool didBaselineAdjustment = false;
   8998    nsTArray<Tracks::ItemBaselineData>* baselineItems[] = {&firstBaselineItems,
   8999                                                           &lastBaselineItems};
   9000    for (const auto* items : baselineItems) {
   9001      for (const auto& data : *items) {
   9002        GridItemInfo* item = data.mGridItem;
   9003        MOZ_ASSERT((item->mState[masonryAxis] & ItemState::eIsBaselineAligned));
   9004        nscoord baselineOffset = item->mBaselineOffset[masonryAxis];
   9005        if (baselineOffset == nscoord(0)) {
   9006          continue;  // no adjustment needed for this item
   9007        }
   9008        didBaselineAdjustment = true;
   9009        auto* child = item->mFrame;
   9010        auto masonryAxisStart =
   9011            item->mArea.LineRangeForAxis(masonryAxis).mStart;
   9012        auto gridAxisRange = item->mArea.LineRangeForAxis(gridAxis);
   9013        masonrySizes[masonryAxisStart].mPosition =
   9014            aSet.mItemSet == BaselineAlignmentSet::LastItems
   9015                ? lastPos[gridAxisRange.mStart]
   9016                : fragStartPos;
   9017        bool consumeAllSpace = false;
   9018        const auto state = item->mState[masonryAxis];
   9019        if ((state & ItemState::eContentBaseline) ||
   9020            MOZ_UNLIKELY(aFragmentainer)) {
   9021          if (MOZ_UNLIKELY(aFragmentainer)) {
   9022            aFragmentainer->mIsTopOfPage =
   9023                isTopOfPageAtStart &&
   9024                masonrySizes[masonryAxisStart].mPosition == fragStartPos;
   9025          }
   9026          nsReflowStatus childStatus;
   9027          ReflowInFlowChild(child, item, aContainerSize, Nothing(),
   9028                            aFragmentainer, aGridRI, aContentArea, aDesiredSize,
   9029                            childStatus);
   9030          consumeAllSpace = handleChildStatus(item, childStatus);
   9031          if (aStatus.IsInlineBreakBefore()) {
   9032            return false;
   9033          }
   9034        } else if (!(state & ItemState::eEndSideBaseline)) {
   9035          // `align/justify-self` baselines on the start side can be handled by
   9036          // just moving the frame (except in a fragmentainer in which case we
   9037          // reflow it above instead since it might make it INCOMPLETE).
   9038          LogicalPoint logicalDelta(wm);
   9039          logicalDelta.Pos(masonryAxis, wm) = baselineOffset;
   9040          child->MovePositionBy(wm, logicalDelta);
   9041        }
   9042        if ((state & ItemState::eEndSideBaseline) && !consumeAllSpace) {
   9043          // Account for an end-side baseline adjustment.
   9044          for (uint32_t i : gridAxisRange.Range()) {
   9045            currentPos[i] += baselineOffset;
   9046          }
   9047        } else {
   9048          nscoord pos = consumeAllSpace ? toFragmentainerEnd
   9049                                        : offsetToMarginBoxEnd(child);
   9050          pos += gap;
   9051          for (uint32_t i : gridAxisRange.Range()) {
   9052            currentPos[i] = pos;
   9053          }
   9054        }
   9055      }
   9056    }
   9057    return didBaselineAdjustment;
   9058  };
   9059 
   9060  // Place and reflow items.  We'll use two fake tracks in the masonry axis.
   9061  // The first contains items that were placed there by the regular grid
   9062  // placement algo (PlaceGridItems) and we may add some items here if there
   9063  // are still empty slots.  The second track contains all other items.
   9064  // Both tracks always have the size of the content box in the masonry axis.
   9065  // The position of the first track is always at the start.  The position
   9066  // of the second track is updated as we go to a position where we want
   9067  // the current item to be positioned.
   9068  for (GridItemInfo* item : sortedItems) {
   9069    auto* child = item->mFrame;
   9070    auto& masonryRange = item->mArea.LineRangeForAxis(masonryAxis);
   9071    auto& gridRange = item->mArea.LineRangeForAxis(gridAxis);
   9072    nsReflowStatus childStatus;
   9073    if (MOZ_UNLIKELY(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) {
   9074      auto contentArea = aContentArea;
   9075      nscoord pos = nscoord_MAX;
   9076      // XXXmats take mEnd into consideration...
   9077      if (gridRange.mStart == kAutoLine) {
   9078        for (auto p : currentPos) {
   9079          pos = std::min(p, pos);
   9080        }
   9081      } else if (gridRange.mStart < currentPos.Length()) {
   9082        pos = currentPos[gridRange.mStart];
   9083      } else if (currentPos.Length() > 0) {
   9084        pos = currentPos.LastElement();
   9085      }
   9086      if (pos == nscoord_MAX) {
   9087        pos = nscoord(0);
   9088      }
   9089      contentArea.Start(masonryAxis, wm) = pos;
   9090      child = child->GetPlaceholderFrame();
   9091      ReflowInFlowChild(child, nullptr, aContainerSize, Nothing(), nullptr,
   9092                        aGridRI, contentArea, aDesiredSize, childStatus);
   9093    } else {
   9094      MOZ_ASSERT(gridRange.Extent() > 0 &&
   9095                 gridRange.Extent() <= gridAxisTrackCount);
   9096      MOZ_ASSERT((masonryRange.mStart == 0 || masonryRange.mStart == 1) &&
   9097                 masonryRange.Extent() == 1);
   9098      if (masonryRange.mStart != 0) {
   9099        masonrySizes[1].mPosition = placeItem(item);
   9100      }
   9101 
   9102      // If this is the first item NOT in the first track and if any of
   9103      // the grid-axis tracks we span has a baseline-aligned item then we
   9104      // need to do that baseline alignment now since it may affect
   9105      // the placement of this and later items.
   9106      if (!didAlignStartAlignedFirstItems &&
   9107          aConstraint == SizingConstraint::NoConstraint &&
   9108          masonryRange.mStart != 0 && lastItemHasBaselineAlignment(gridRange)) {
   9109        didAlignStartAlignedFirstItems = true;
   9110        if (applyBaselineAlignment({BaselineAlignmentSet::FirstItems,
   9111                                    BaselineAlignmentSet::StartStretch})) {
   9112          // Baseline alignment resized some items - redo our placement.
   9113          masonrySizes[1].mPosition = placeItem(item);
   9114        }
   9115        if (aStatus.IsInlineBreakBefore()) {
   9116          return fragStartPos;
   9117        }
   9118      }
   9119 
   9120      for (uint32_t i : gridRange.Range()) {
   9121        lastItems[i] = item;
   9122      }
   9123      cursor = gridRange.mEnd;
   9124      if (cursor >= gridAxisTrackCount) {
   9125        cursor = 0;
   9126      }
   9127 
   9128      nscoord pos;
   9129      if (aConstraint == SizingConstraint::NoConstraint) {
   9130        const auto* disp = child->StyleDisplay();
   9131        if (MOZ_UNLIKELY(aFragmentainer)) {
   9132          aFragmentainer->mIsTopOfPage =
   9133              isTopOfPageAtStart &&
   9134              masonrySizes[masonryRange.mStart].mPosition == fragStartPos;
   9135          if (!aFragmentainer->mIsTopOfPage &&
   9136              (disp->BreakBefore() ||
   9137               masonrySizes[masonryRange.mStart].mPosition >=
   9138                   toFragmentainerEnd)) {
   9139            childStatus.SetInlineLineBreakBeforeAndReset();
   9140          }
   9141        }
   9142        if (!childStatus.IsInlineBreakBefore()) {
   9143          ReflowInFlowChild(child, item, aContainerSize, Nothing(),
   9144                            aFragmentainer, aGridRI, aContentArea, aDesiredSize,
   9145                            childStatus);
   9146        }
   9147        bool consumeAllSpace = handleChildStatus(item, childStatus);
   9148        if (aStatus.IsInlineBreakBefore()) {
   9149          return fragStartPos;
   9150        }
   9151        pos =
   9152            consumeAllSpace ? toFragmentainerEnd : offsetToMarginBoxEnd(child);
   9153      } else {
   9154        LogicalSize percentBasis(
   9155            aGridRI.PercentageBasisFor(LogicalAxis::Inline, *item));
   9156        IntrinsicISizeType type = aConstraint == SizingConstraint::MaxContent
   9157                                      ? IntrinsicISizeType::PrefISize
   9158                                      : IntrinsicISizeType::MinISize;
   9159        auto sz = ::ContentContribution(*item, aGridRI, masonryAxis,
   9160                                        percentBasis, type);
   9161        pos = sz + masonrySizes[masonryRange.mStart].mPosition;
   9162      }
   9163      pos += gap;
   9164      for (uint32_t i : gridRange.Range()) {
   9165        lastPos[i] = currentPos[i];
   9166        currentPos[i] = pos;
   9167      }
   9168    }
   9169  }
   9170 
   9171  // Do the remaining baseline alignment sets.
   9172  if (aConstraint == SizingConstraint::NoConstraint) {
   9173    for (auto*& item : lastItems) {
   9174      if (item) {
   9175        item->mState[masonryAxis] |= ItemState::eIsLastItemInMasonryTrack;
   9176      }
   9177    }
   9178    BaselineAlignmentSet baselineSets[] = {
   9179        {BaselineAlignmentSet::FirstItems, BaselineAlignmentSet::StartStretch},
   9180        {BaselineAlignmentSet::FirstItems, BaselineAlignmentSet::EndStretch},
   9181        {BaselineAlignmentSet::LastItems, BaselineAlignmentSet::StartStretch},
   9182        {BaselineAlignmentSet::LastItems, BaselineAlignmentSet::EndStretch},
   9183    };
   9184    for (uint32_t i = 0; i < std::size(baselineSets); ++i) {
   9185      if (i == 0 && didAlignStartAlignedFirstItems) {
   9186        continue;
   9187      }
   9188      applyBaselineAlignment(baselineSets[i]);
   9189    }
   9190  }
   9191 
   9192  const bool childrenMoved = PushIncompleteChildren(
   9193      pushedItems, incompleteItems, overflowIncompleteItems);
   9194  if (childrenMoved && aStatus.IsComplete()) {
   9195    aStatus.SetOverflowIncomplete();
   9196    aStatus.SetNextInFlowNeedsReflow();
   9197  }
   9198  if (!pushedItems.IsEmpty()) {
   9199    AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
   9200    // NOTE since we messed with our child list here, we intentionally
   9201    // make aState.mIter invalid to avoid any use of it after this point.
   9202    aGridRI.mIter.Invalidate();
   9203  }
   9204  if (!incompleteItems.IsEmpty()) {
   9205    // NOTE since we messed with our child list here, we intentionally
   9206    // make aState.mIter invalid to avoid any use of it after this point.
   9207    aGridRI.mIter.Invalidate();
   9208  }
   9209 
   9210  nscoord masonryBoxSize = 0;
   9211  for (auto pos : currentPos) {
   9212    masonryBoxSize = std::max(masonryBoxSize, pos);
   9213  }
   9214  masonryBoxSize = std::max(nscoord(0), masonryBoxSize - gap);
   9215  if (aConstraint == SizingConstraint::NoConstraint) {
   9216    aGridRI.AlignJustifyContentInMasonryAxis(masonryBoxSize,
   9217                                             masonryTracks.mContentBoxSize);
   9218  }
   9219  return masonryBoxSize;
   9220 }
   9221 
   9222 nsGridContainerFrame* nsGridContainerFrame::ParentGridContainerForSubgrid()
   9223    const {
   9224  MOZ_ASSERT(IsSubgrid());
   9225  nsIFrame* p = GetParent();
   9226  while (p->GetContent() == GetContent()) {
   9227    p = p->GetParent();
   9228  }
   9229  MOZ_ASSERT(p->IsGridContainerFrame());
   9230  auto* parent = static_cast<nsGridContainerFrame*>(p);
   9231  MOZ_ASSERT(parent->HasSubgridItems());
   9232  return parent;
   9233 }
   9234 
   9235 nscoord nsGridContainerFrame::ReflowChildren(GridReflowInput& aGridRI,
   9236                                             const LogicalRect& aContentArea,
   9237                                             const nsSize& aContainerSize,
   9238                                             ReflowOutput& aDesiredSize,
   9239                                             nsReflowStatus& aStatus) {
   9240  WritingMode wm = aGridRI.mReflowInput->GetWritingMode();
   9241  nscoord bSize = aContentArea.BSize(wm);
   9242  MOZ_ASSERT(aGridRI.mReflowInput);
   9243  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
   9244  if (HidesContentForLayout()) {
   9245    return bSize;
   9246  }
   9247 
   9248  OverflowAreas ocBounds;
   9249  nsReflowStatus ocStatus;
   9250  if (GetPrevInFlow()) {
   9251    ReflowOverflowContainerChildren(PresContext(), *aGridRI.mReflowInput,
   9252                                    ocBounds, ReflowChildFlags::Default,
   9253                                    ocStatus, MergeSortedFrameListsFor);
   9254  }
   9255 
   9256  Maybe<Fragmentainer> fragmentainer = GetNearestFragmentainer(aGridRI);
   9257  // MasonryLayout() can only handle fragmentation in the masonry-axis,
   9258  // so we let ReflowInFragmentainer() deal with grid-axis fragmentation
   9259  // in the else-clause below.
   9260  if (IsMasonry() && !(IsColMasonry() && fragmentainer.isSome())) {
   9261    aGridRI.mInFragmentainer = fragmentainer.isSome();
   9262    nscoord sz = MasonryLayout(
   9263        aGridRI, aContentArea, SizingConstraint::NoConstraint, aDesiredSize,
   9264        aStatus, fragmentainer.ptrOr(nullptr), aContainerSize);
   9265    if (IsRowMasonry()) {
   9266      bSize = aGridRI.mReflowInput->ComputedBSize();
   9267      if (bSize == NS_UNCONSTRAINEDSIZE) {
   9268        bSize = aGridRI.mReflowInput->ApplyMinMaxBSize(sz);
   9269      }
   9270    }
   9271  } else if (MOZ_UNLIKELY(fragmentainer.isSome())) {
   9272    if (IsColMasonry() && !GetPrevInFlow()) {
   9273      // First we do an unconstrained reflow to resolve the item placement
   9274      // which is then kept as-is in the constrained reflow below.
   9275      MasonryLayout(aGridRI, aContentArea, SizingConstraint::NoConstraint,
   9276                    aDesiredSize, aStatus, nullptr, aContainerSize);
   9277    }
   9278    aGridRI.mInFragmentainer = true;
   9279    bSize = ReflowInFragmentainer(aGridRI, aContentArea, aDesiredSize, aStatus,
   9280                                  *fragmentainer, aContainerSize);
   9281  } else {
   9282    aGridRI.mIter.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll);
   9283    for (; !aGridRI.mIter.AtEnd(); aGridRI.mIter.Next()) {
   9284      nsIFrame* child = *aGridRI.mIter;
   9285      const GridItemInfo* info = nullptr;
   9286      if (!child->IsPlaceholderFrame()) {
   9287        info = &aGridRI.mGridItems[aGridRI.mIter.ItemIndex()];
   9288      }
   9289      nsReflowStatus childStatus;
   9290      ReflowInFlowChild(child, info, aContainerSize, Nothing(), nullptr,
   9291                        aGridRI, aContentArea, aDesiredSize, childStatus);
   9292      MOZ_ASSERT(childStatus.IsComplete(),
   9293                 "child should be complete in unconstrained reflow");
   9294      aStatus.MergeCompletionStatusFrom(childStatus);
   9295    }
   9296  }
   9297 
   9298  // Merge overflow container bounds and status.
   9299  aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
   9300  aStatus.MergeCompletionStatusFrom(ocStatus);
   9301 
   9302  return bSize;
   9303 }
   9304 
   9305 void nsGridContainerFrame::ReflowAbsoluteChildren(
   9306    GridReflowInput& aGridRI, const LogicalRect& aContentArea,
   9307    nscoord aContentBSize, ReflowOutput& aDesiredSize,
   9308    nsReflowStatus& aStatus) {
   9309  WritingMode wm = aGridRI.mReflowInput->GetWritingMode();
   9310  auto* absoluteContainer = GetAbsoluteContainingBlock();
   9311  // We have prepared the absolute frames in Grid::PlaceGridItems() or in
   9312  // GridReflowInput::InitializeForContinuation().
   9313  if (!absoluteContainer || !absoluteContainer->HasAbsoluteFrames()) {
   9314    return;
   9315  }
   9316  // 'gridOrigin' is the origin of the grid (the start of the first track),
   9317  // with respect to the grid container's padding-box (CB).
   9318  LogicalMargin pad(aGridRI.mReflowInput->ComputedLogicalPadding(wm));
   9319  const LogicalPoint gridOrigin(wm, pad.IStart(wm), pad.BStart(wm));
   9320  const LogicalRect gridCB(wm, 0, 0, aContentArea.ISize(wm) + pad.IStartEnd(wm),
   9321                           aContentBSize + pad.BStartEnd(wm));
   9322  const nsSize gridCBPhysicalSize = gridCB.Size(wm).GetPhysicalSize(wm);
   9323  size_t i = 0;
   9324  for (nsIFrame* child : absoluteContainer->GetChildList()) {
   9325    MOZ_ASSERT(i < aGridRI.mAbsPosItems.Length());
   9326    MOZ_ASSERT(aGridRI.mAbsPosItems[i].mFrame == child);
   9327    GridArea& area = aGridRI.mAbsPosItems[i].mArea;
   9328    LogicalRect itemCB =
   9329        aGridRI.ContainingBlockForAbsPos(area, gridOrigin, gridCB);
   9330    // AbsoluteContainingBlock::Reflow uses physical coordinates.
   9331    nsRect* cb = child->GetProperty(GridItemContainingBlockRect());
   9332    if (!cb) {
   9333      cb = new nsRect;
   9334      child->SetProperty(GridItemContainingBlockRect(), cb);
   9335    }
   9336    *cb = itemCB.GetPhysicalRect(wm, gridCBPhysicalSize);
   9337    ++i;
   9338  }
   9339  const auto border = aGridRI.mReflowInput->ComputedPhysicalBorder();
   9340  const nsPoint borderShift{border.left, border.top};
   9341  const nsRect paddingRect(borderShift, gridCBPhysicalSize);
   9342  // XXX: To optimize the performance, set the flags only when the CB width
   9343  // or height actually changes.
   9344  AbsPosReflowFlags flags{
   9345      AbsPosReflowFlag::AllowFragmentation, AbsPosReflowFlag::CBWidthChanged,
   9346      AbsPosReflowFlag::CBHeightChanged, AbsPosReflowFlag::IsGridContainerCB};
   9347  absoluteContainer->Reflow(this, PresContext(), *aGridRI.mReflowInput, aStatus,
   9348                            paddingRect, flags, &aDesiredSize.mOverflowAreas);
   9349 }
   9350 
   9351 nscoord nsGridContainerFrame::ComputeBSizeForResolvingRowSizes(
   9352    GridReflowInput& aGridRI, nscoord aComputedBSize,
   9353    const Maybe<nscoord>& aContainIntrinsicBSize) const {
   9354  if (aComputedBSize != NS_UNCONSTRAINEDSIZE) {
   9355    // We don't need to apply the min/max constraints to the computed block-size
   9356    // because ReflowInput (specifically when computing the block-size in
   9357    // nsIFrame::ComputeSize()) has already clamped the block-size.
   9358    return aComputedBSize;
   9359  }
   9360 
   9361  if (aContainIntrinsicBSize) {
   9362    // We have an unconstrained block-size, but we also have a specified
   9363    // 'contain-intrinsic-block-size'. We apply the min/max constraints to the
   9364    // value, and use that for track sizing.
   9365    return aGridRI.mReflowInput->ApplyMinMaxBSize(*aContainIntrinsicBSize);
   9366  }
   9367 
   9368  return NS_UNCONSTRAINEDSIZE;
   9369 }
   9370 
   9371 nscoord nsGridContainerFrame::ComputeIntrinsicContentBSize(
   9372    const GridReflowInput& aGridRI, nscoord aComputedBSize,
   9373    nscoord aBSizeForResolvingRowSizes,
   9374    const Maybe<nscoord>& aContainIntrinsicBSize) const {
   9375  MOZ_ASSERT(
   9376      aComputedBSize == NS_UNCONSTRAINEDSIZE ||
   9377          aGridRI.mReflowInput->ShouldApplyAutomaticMinimumOnBlockAxis(),
   9378      "Why call this method when intrinsic content block-size is not needed?");
   9379 
   9380  if (aComputedBSize == NS_UNCONSTRAINEDSIZE) {
   9381    // When we have an unconstrained block-size, the intrinsic content
   9382    // block-size would have been determined after we resolved the row sizes the
   9383    // first time. Just return that value.
   9384    return aBSizeForResolvingRowSizes;
   9385  }
   9386 
   9387  if (aContainIntrinsicBSize) {
   9388    // We have a specified 'contain-intrinsic-block-size' which we need to
   9389    // honor.
   9390    return *aContainIntrinsicBSize;
   9391  }
   9392 
   9393  if (IsRowMasonry()) {
   9394    // There aren't any tracks to derive a block-size from, if we're doing
   9395    // masonry rather than forming rows in the block direction.
   9396    return aBSizeForResolvingRowSizes;
   9397  }
   9398 
   9399  return aGridRI.mRows.TotalTrackSizeWithoutAlignment(this);
   9400 }
   9401 
   9402 void nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
   9403                                  ReflowOutput& aDesiredSize,
   9404                                  const ReflowInput& aReflowInput,
   9405                                  nsReflowStatus& aStatus) {
   9406  if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
   9407    return;
   9408  }
   9409 
   9410  MarkInReflow();
   9411  DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
   9412  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
   9413 
   9414  GRID_LOG("Reflow grid container frame %p", this);
   9415 
   9416  if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) {
   9417    return;
   9418  }
   9419 
   9420  NormalizeChildLists();
   9421 
   9422 #ifdef DEBUG
   9423  mDidPushItemsBitMayLie = false;
   9424  SanityCheckChildListsBeforeReflow();
   9425 #endif  // DEBUG
   9426 
   9427  for (auto& perAxisBaseline : mBaseline) {
   9428    for (auto& baseline : perAxisBaseline) {
   9429      baseline = NS_INTRINSIC_ISIZE_UNKNOWN;
   9430    }
   9431  }
   9432 
   9433  const nsStylePosition* stylePos = aReflowInput.mStylePosition;
   9434  auto prevInFlow = static_cast<nsGridContainerFrame*>(GetPrevInFlow());
   9435  if (MOZ_LIKELY(!prevInFlow)) {
   9436    InitImplicitNamedAreas(stylePos);
   9437  } else {
   9438    MOZ_ASSERT(prevInFlow->HasAnyStateBits(kIsSubgridBits) ==
   9439                   HasAnyStateBits(kIsSubgridBits),
   9440               "continuations should have same kIsSubgridBits");
   9441  }
   9442  GridReflowInput gridRI(this, aReflowInput);
   9443  if (gridRI.mIter.ItemsAreAlreadyInOrder()) {
   9444    AddStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
   9445  } else {
   9446    RemoveStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
   9447  }
   9448  if (gridRI.mIter.AtEnd() || aReflowInput.mStyleDisplay->IsContainLayout()) {
   9449    // We have no grid items, or we're layout-contained. So, we have no
   9450    // baseline, and our parent should synthesize a baseline if needed.
   9451    AddStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
   9452  } else {
   9453    RemoveStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
   9454  }
   9455  const nscoord computedBSize = aReflowInput.ComputedBSize();
   9456  const nscoord computedISize = aReflowInput.ComputedISize();
   9457 
   9458  // XXX Technically incorrect: 'contain-intrinsic-block-size: none' is
   9459  // treated as 0, ignoring our row sizes, when really we should use them but
   9460  // *they* should be computed as if we had no children. To be fixed in bug
   9461  // 1488878.
   9462  const Maybe<nscoord> containIntrinsicBSize =
   9463      aReflowInput.mFrame->ContainIntrinsicBSize();
   9464  const WritingMode& wm = gridRI.mWM;
   9465 
   9466  nscoord consumedBSize = 0;
   9467  nscoord contentBSize = 0;
   9468  if (MOZ_LIKELY(!prevInFlow)) {
   9469    Grid grid;
   9470    if (MOZ_LIKELY(!IsSubgrid())) {
   9471      RepeatTrackSizingInput repeatSizing(aReflowInput.ComputedMinSize(),
   9472                                          aReflowInput.ComputedSize(),
   9473                                          aReflowInput.ComputedMaxSize());
   9474      grid.PlaceGridItems(gridRI, repeatSizing);
   9475    } else {
   9476      auto* subgrid = GetProperty(Subgrid::Prop());
   9477      MOZ_ASSERT(subgrid, "an ancestor forgot to call PlaceGridItems?");
   9478      gridRI.mGridItems = subgrid->mGridItems.Clone();
   9479      gridRI.mAbsPosItems = subgrid->mAbsPosItems.Clone();
   9480      grid.mGridColEnd = subgrid->mGridColEnd;
   9481      grid.mGridRowEnd = subgrid->mGridRowEnd;
   9482    }
   9483 
   9484    // Resolve the column sizes with the grid container's inline size.
   9485    // 12.1.1: https://drafts.csswg.org/css-grid-2/#algo-grid-sizing
   9486    gridRI.CalculateTrackSizesForAxis(LogicalAxis::Inline, grid, computedISize,
   9487                                      SizingConstraint::NoConstraint);
   9488 
   9489    nscoord bSizeForResolvingRowSizes = ComputeBSizeForResolvingRowSizes(
   9490        gridRI, computedBSize, containIntrinsicBSize);
   9491 
   9492    // Resolve the row sizes with the determined bSizeForResolvingRowSizes.
   9493    // 12.1.2: https://drafts.csswg.org/css-grid-2/#algo-grid-sizing
   9494    //
   9495    // If bSizeForResolvingRowSizes is unconstrained, that's fine. It forces
   9496    // percent-valued row sizes to be treated as 'auto', yielding an intrinsic
   9497    // content block-size needed later to *actually* resolve percent-valued row
   9498    // gaps and row sizes.
   9499    gridRI.CalculateTrackSizesForAxis(LogicalAxis::Block, grid,
   9500                                      bSizeForResolvingRowSizes,
   9501                                      SizingConstraint::NoConstraint);
   9502 
   9503    // Invalidate the column sizes before re-resolving them.
   9504    gridRI.InvalidateTrackSizesForAxis(LogicalAxis::Inline);
   9505 
   9506    // Re-resolve the column sizes.
   9507    // 12.1.3: https://drafts.csswg.org/css-grid-2/#algo-grid-sizing
   9508    gridRI.CalculateTrackSizesForAxis(LogicalAxis::Inline, grid, computedISize,
   9509                                      SizingConstraint::NoConstraint);
   9510 
   9511    // If our bSizeForResolvingRowSizes is still indefinite, replace it with
   9512    // the sum of the row sizes we just resolved, then re-resolve the row
   9513    // sizes against that value. We skip this for masonry, which doesn't need
   9514    // two-pass row sizes resolution.
   9515    if (bSizeForResolvingRowSizes == NS_UNCONSTRAINEDSIZE && !IsRowMasonry()) {
   9516      bSizeForResolvingRowSizes =
   9517          std::max(gridRI.mRows.TotalTrackSizeWithoutAlignment(this),
   9518                   gridRI.mReflowInput->ComputedMinBSize());
   9519 
   9520      NS_ASSERTION(bSizeForResolvingRowSizes != NS_UNCONSTRAINEDSIZE,
   9521                   "The block-size for re-resolving the row sizes should be "
   9522                   "definite in non-masonry layout!");
   9523 
   9524      // Invalidate the row sizes before re-resolving them.
   9525      gridRI.InvalidateTrackSizesForAxis(LogicalAxis::Block);
   9526 
   9527      // Re-resolve the row sizes.
   9528      // 12.1.4: https://drafts.csswg.org/css-grid-2/#algo-grid-sizing
   9529      gridRI.CalculateTrackSizesForAxis(LogicalAxis::Block, grid,
   9530                                        bSizeForResolvingRowSizes,
   9531                                        SizingConstraint::NoConstraint);
   9532    }
   9533 
   9534    if (computedBSize == NS_UNCONSTRAINEDSIZE ||
   9535        aReflowInput.ShouldApplyAutomaticMinimumOnBlockAxis()) {
   9536      // We either have an unconstrained block-size, or we have a definite
   9537      // block-size derived from the inline-size (transferred via aspect-ratio)
   9538      // and need to apply the automatic content-based minimum sizes on the
   9539      // block-axis. In both case, we need to compute the intrinsic
   9540      // content block-size.
   9541      contentBSize = ComputeIntrinsicContentBSize(gridRI, computedBSize,
   9542                                                  bSizeForResolvingRowSizes,
   9543                                                  containIntrinsicBSize);
   9544    }
   9545  } else {
   9546    consumedBSize = CalcAndCacheConsumedBSize();
   9547    gridRI.InitializeForContinuation(this, consumedBSize);
   9548    if (containIntrinsicBSize) {
   9549      contentBSize = *containIntrinsicBSize;
   9550    } else {
   9551      const uint32_t numRows = gridRI.mRows.mSizes.Length();
   9552      contentBSize =
   9553          gridRI.mRows.GridLineEdge(numRows, GridLineSide::AfterGridGap);
   9554    }
   9555  }
   9556  if (computedBSize == NS_UNCONSTRAINEDSIZE) {
   9557    contentBSize = aReflowInput.ApplyMinMaxBSize(contentBSize);
   9558  } else if (aReflowInput.ShouldApplyAutomaticMinimumOnBlockAxis()) {
   9559    contentBSize = aReflowInput.ApplyMinMaxBSize(contentBSize);
   9560    contentBSize = std::max(contentBSize, computedBSize);
   9561  } else {
   9562    contentBSize = computedBSize;
   9563  }
   9564  if (contentBSize != NS_UNCONSTRAINEDSIZE) {
   9565    contentBSize = std::max(contentBSize - consumedBSize, 0);
   9566  }
   9567  auto& bp = gridRI.mBorderPadding;
   9568  LogicalRect contentArea(wm, bp.IStart(wm), bp.BStart(wm), computedISize,
   9569                          contentBSize);
   9570 
   9571  if (!prevInFlow) {
   9572    const auto& rowSizes = gridRI.mRows.mSizes;
   9573    if (!IsRowSubgrid()) {
   9574      if (!gridRI.mRows.mIsMasonry) {
   9575        // Apply 'align-content' to the grid.
   9576        auto alignment = stylePos->mAlignContent;
   9577        gridRI.mRows.AlignJustifyContent(stylePos, alignment, wm, contentBSize,
   9578                                         false);
   9579      }
   9580    } else {
   9581      if (computedBSize == NS_UNCONSTRAINEDSIZE) {
   9582        contentBSize = gridRI.mRows.GridLineEdge(rowSizes.Length(),
   9583                                                 GridLineSide::BeforeGridGap);
   9584        contentArea.BSize(wm) = std::max(contentBSize, nscoord(0));
   9585      }
   9586    }
   9587    // Save the final row sizes for use by subgrids, if needed.
   9588    if (HasSubgridItems() || IsSubgrid()) {
   9589      StoreUsedTrackSizes(LogicalAxis::Block, rowSizes);
   9590    }
   9591  }
   9592 
   9593  nsSize containerSize = contentArea.Size(wm).GetPhysicalSize(wm);
   9594  bool repositionChildren = false;
   9595  if (containerSize.width == NS_UNCONSTRAINEDSIZE && wm.IsVerticalRL()) {
   9596    // Note that writing-mode:vertical-rl is the only case where the block
   9597    // logical direction progresses in a negative physical direction, and
   9598    // therefore block-dir coordinate conversion depends on knowing the width
   9599    // of the coordinate space in order to translate between the logical and
   9600    // physical origins.
   9601    //
   9602    // A masonry axis size may be unconstrained, otherwise in a regular grid
   9603    // our intrinsic size is always known by now.  We'll re-position
   9604    // the children below once our size is known.
   9605    repositionChildren = true;
   9606    containerSize.width = 0;
   9607  }
   9608  containerSize.width += bp.LeftRight(wm);
   9609  containerSize.height += bp.TopBottom(wm);
   9610 
   9611  contentBSize =
   9612      ReflowChildren(gridRI, contentArea, containerSize, aDesiredSize, aStatus);
   9613  if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
   9614    // Per spec, the grid area is included in a grid container's scrollable
   9615    // overflow region [1], as well as the padding on the end-edge sides that
   9616    // would satisfy the requirements of 'place-content: end' alignment [2].
   9617    //
   9618    // Note that we include the padding from all sides of the grid area, not
   9619    // just the end sides; this is fine because the grid area is relative to our
   9620    // content-box origin. The inflated bounds won't go beyond our padding-box
   9621    // edges on the start sides.
   9622    //
   9623    // The margin areas of grid item boxes are also included in the scrollable
   9624    // overflow region [2].
   9625    //
   9626    // [1] https://drafts.csswg.org/css-grid-2/#overflow
   9627    // [2] https://drafts.csswg.org/css-overflow-3/#scrollable
   9628 
   9629    // Synthesize a grid area covering all columns and rows, and compute its
   9630    // rect relative to our border-box.
   9631    //
   9632    // Note: the grid columns and rows exist only if there is an explicit grid;
   9633    // or when an implicit grid is needed to place any grid items. See
   9634    // nsGridContainerFrame::Grid::PlaceGridItems().
   9635    const auto numCols = static_cast<int32_t>(gridRI.mCols.mSizes.Length());
   9636    const auto numRows = static_cast<int32_t>(gridRI.mRows.mSizes.Length());
   9637    if (numCols > 0 && numRows > 0) {
   9638      const GridArea gridArea(LineRange(0, numCols), LineRange(0, numRows));
   9639      const LogicalRect gridAreaRect =
   9640          gridRI.ContainingBlockFor(gridArea) +
   9641          LogicalPoint(wm, bp.IStart(wm), bp.BStart(wm));
   9642 
   9643      MOZ_ASSERT(bp == aReflowInput.ComputedLogicalPadding(wm),
   9644                 "A scrolled inner frame shouldn't have any border!");
   9645      const LogicalMargin& padding = bp;
   9646      nsRect physicalGridAreaRectWithPadding =
   9647          gridAreaRect.GetPhysicalRect(wm, containerSize);
   9648      physicalGridAreaRectWithPadding.Inflate(padding.GetPhysicalMargin(wm));
   9649      aDesiredSize.mOverflowAreas.UnionAllWith(physicalGridAreaRectWithPadding);
   9650    }
   9651 
   9652    nsRect gridItemMarginBoxBounds;
   9653    for (const auto& item : gridRI.mGridItems) {
   9654      gridItemMarginBoxBounds =
   9655          gridItemMarginBoxBounds.Union(item.mFrame->GetMarginRect());
   9656    }
   9657    aDesiredSize.mOverflowAreas.UnionAllWith(gridItemMarginBoxBounds);
   9658  }
   9659  ReflowAbsoluteChildren(gridRI, contentArea, contentBSize, aDesiredSize,
   9660                         aStatus);
   9661  contentBSize = std::max(contentBSize - consumedBSize, 0);
   9662 
   9663  // Skip our block-end border if we're INCOMPLETE.
   9664  if (!aStatus.IsComplete() && !gridRI.mSkipSides.BEnd() &&
   9665      StyleBorder()->mBoxDecorationBreak != StyleBoxDecorationBreak::Clone) {
   9666    bp.BEnd(wm) = nscoord(0);
   9667  }
   9668 
   9669  LogicalSize desiredSize(wm, computedISize + bp.IStartEnd(wm),
   9670                          contentBSize + bp.BStartEnd(wm));
   9671  aDesiredSize.SetSize(wm, desiredSize);
   9672  nsRect frameRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height());
   9673  aDesiredSize.mOverflowAreas.UnionAllWith(frameRect);
   9674 
   9675  if (repositionChildren) {
   9676    nsPoint physicalDelta(aDesiredSize.Width() - bp.LeftRight(wm), 0);
   9677    for (const auto& item : gridRI.mGridItems) {
   9678      auto* child = item.mFrame;
   9679      child->MovePositionBy(physicalDelta);
   9680      ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child);
   9681    }
   9682  }
   9683 
   9684  // TODO: fix align-tracks alignment in fragments
   9685  if ((IsRowMasonry() && !prevInFlow) || IsColMasonry()) {
   9686    gridRI.AlignJustifyTracksInMasonryAxis(contentArea.Size(wm),
   9687                                           aDesiredSize.PhysicalSize());
   9688  }
   9689 
   9690  // Convert INCOMPLETE -> OVERFLOW_INCOMPLETE and zero bsize if we're an OC.
   9691  if (HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   9692    if (!aStatus.IsComplete()) {
   9693      aStatus.SetOverflowIncomplete();
   9694      aStatus.SetNextInFlowNeedsReflow();
   9695    }
   9696    contentBSize = 0;
   9697    desiredSize.BSize(wm) = contentBSize + bp.BStartEnd(wm);
   9698    aDesiredSize.SetSize(wm, desiredSize);
   9699  }
   9700 
   9701  if (!gridRI.mInFragmentainer) {
   9702    MOZ_ASSERT(gridRI.mIter.IsValid());
   9703    auto sz = frameRect.Size();
   9704    CalculateBaselines(BaselineSet::eBoth, &gridRI.mIter, &gridRI.mGridItems,
   9705                       gridRI.mCols, 0, gridRI.mCols.mSizes.Length(), wm, sz,
   9706                       bp.IStart(wm), bp.IEnd(wm), desiredSize.ISize(wm));
   9707    CalculateBaselines(BaselineSet::eBoth, &gridRI.mIter, &gridRI.mGridItems,
   9708                       gridRI.mRows, 0, gridRI.mRows.mSizes.Length(), wm, sz,
   9709                       bp.BStart(wm), bp.BEnd(wm), desiredSize.BSize(wm));
   9710  } else {
   9711    // Only compute 'first baseline' if this fragment contains the first track.
   9712    // XXXmats maybe remove this condition? bug 1306499
   9713    BaselineSet baselines = BaselineSet::eNone;
   9714    if (gridRI.mStartRow == 0 &&
   9715        gridRI.mStartRow != gridRI.mNextFragmentStartRow) {
   9716      baselines = BaselineSet::eFirst;
   9717    }
   9718    // Only compute 'last baseline' if this fragment contains the last track.
   9719    // XXXmats maybe remove this condition? bug 1306499
   9720    uint32_t len = gridRI.mRows.mSizes.Length();
   9721    if (gridRI.mStartRow != len && gridRI.mNextFragmentStartRow == len) {
   9722      baselines = BaselineSet(baselines | BaselineSet::eLast);
   9723    }
   9724    Maybe<CSSOrderAwareFrameIterator> iter;
   9725    Maybe<nsTArray<GridItemInfo>> gridItems;
   9726    if (baselines != BaselineSet::eNone) {
   9727      // We need to create a new iterator and GridItemInfo array because we
   9728      // might have pushed some children at this point.
   9729      // Even if gridRI.mIter is invalid, we can reuse its
   9730      // state about order to optimize initialization of the new iterator.
   9731      // An ordered child list can't become unordered by pushing frames.
   9732      // An unordered list can become ordered in a number of cases, but we
   9733      // ignore that here and guess that the child list is still unordered.
   9734      // XXX this is O(n^2) in the number of items in this fragment: bug 1306705
   9735      using Filter = CSSOrderAwareFrameIterator::ChildFilter;
   9736      using Order = CSSOrderAwareFrameIterator::OrderState;
   9737      bool ordered = gridRI.mIter.ItemsAreAlreadyInOrder();
   9738      auto orderState = ordered ? Order::Ordered : Order::Unordered;
   9739      iter.emplace(this, FrameChildListID::Principal, Filter::SkipPlaceholders,
   9740                   orderState);
   9741      gridItems.emplace();
   9742      for (; !iter->AtEnd(); iter->Next()) {
   9743        auto child = **iter;
   9744        for (const auto& info : gridRI.mGridItems) {
   9745          if (info.mFrame == child) {
   9746            gridItems->AppendElement(info);
   9747          }
   9748        }
   9749      }
   9750    }
   9751    auto sz = frameRect.Size();
   9752    CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
   9753                       gridRI.mCols, 0, gridRI.mCols.mSizes.Length(), wm, sz,
   9754                       bp.IStart(wm), bp.IEnd(wm), desiredSize.ISize(wm));
   9755    CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
   9756                       gridRI.mRows, gridRI.mStartRow,
   9757                       gridRI.mNextFragmentStartRow, wm, sz, bp.BStart(wm),
   9758                       bp.BEnd(wm), desiredSize.BSize(wm));
   9759  }
   9760 
   9761  if (HasAnyStateBits(NS_STATE_GRID_COMPUTED_INFO)) {
   9762    // This state bit will never be cleared, since reflow can be called
   9763    // multiple times in fragmented grids, and it's challenging to scope
   9764    // the bit to only that sequence of calls. This is relatively harmless
   9765    // since this bit is only set by accessing a ChromeOnly property, and
   9766    // therefore can't unduly slow down normal web browsing.
   9767 
   9768    // Clear our GridFragmentInfo property, which might be holding a stale
   9769    // dom::Grid object built from previously-computed info. This will
   9770    // ensure that the next call to GetGridFragments will create a new one.
   9771    if (mozilla::dom::Grid* grid = TakeProperty(GridFragmentInfo())) {
   9772      grid->ForgetFrame();
   9773    }
   9774 
   9775    // Now that we know column and row sizes and positions, set
   9776    // the ComputedGridTrackInfo and related properties
   9777 
   9778    const auto* subgrid = GetProperty(Subgrid::Prop());
   9779    const auto* subgridColRange =
   9780        subgrid && IsColSubgrid() ? &subgrid->SubgridCols() : nullptr;
   9781 
   9782    LineNameMap colLineNameMap(gridRI.mGridStyle, GetImplicitNamedAreas(),
   9783                               gridRI.mColFunctions, nullptr, subgridColRange,
   9784                               true);
   9785    uint32_t colTrackCount = gridRI.mCols.mSizes.Length();
   9786    nsTArray<nscoord> colTrackPositions(colTrackCount);
   9787    nsTArray<nscoord> colTrackSizes(colTrackCount);
   9788    nsTArray<uint32_t> colTrackStates(colTrackCount);
   9789    nsTArray<bool> colRemovedRepeatTracks(
   9790        gridRI.mColFunctions.mRemovedRepeatTracks.Clone());
   9791    uint32_t col = 0;
   9792    for (const TrackSize& sz : gridRI.mCols.mSizes) {
   9793      colTrackPositions.AppendElement(sz.mPosition);
   9794      colTrackSizes.AppendElement(sz.mBase);
   9795      bool isRepeat = ((col >= gridRI.mColFunctions.mRepeatAutoStart) &&
   9796                       (col < gridRI.mColFunctions.mRepeatAutoEnd));
   9797      colTrackStates.AppendElement(
   9798          isRepeat ? (uint32_t)mozilla::dom::GridTrackState::Repeat
   9799                   : (uint32_t)mozilla::dom::GridTrackState::Static);
   9800 
   9801      col++;
   9802    }
   9803    // Get the number of explicit tracks first. The order of argument evaluation
   9804    // is implementation-defined. We should be OK here because colTrackSizes is
   9805    // taken by rvalue, but computing the size first prevents any changes in the
   9806    // argument types of the constructor from breaking this.
   9807    const uint32_t numColExplicitTracks =
   9808        IsColSubgrid() ? colTrackSizes.Length()
   9809                       : gridRI.mColFunctions.NumExplicitTracks();
   9810    ComputedGridTrackInfo* colInfo = new ComputedGridTrackInfo(
   9811        gridRI.mColFunctions.mExplicitGridOffset, numColExplicitTracks, 0, col,
   9812        std::move(colTrackPositions), std::move(colTrackSizes),
   9813        std::move(colTrackStates), std::move(colRemovedRepeatTracks),
   9814        gridRI.mColFunctions.mRepeatAutoStart,
   9815        colLineNameMap.GetResolvedLineNamesForComputedGridTrackInfo(),
   9816        IsColSubgrid(), IsColMasonry());
   9817    SetProperty(GridColTrackInfo(), colInfo);
   9818 
   9819    const auto* subgridRowRange =
   9820        subgrid && IsRowSubgrid() ? &subgrid->SubgridRows() : nullptr;
   9821    LineNameMap rowLineNameMap(gridRI.mGridStyle, GetImplicitNamedAreas(),
   9822                               gridRI.mRowFunctions, nullptr, subgridRowRange,
   9823                               true);
   9824    uint32_t rowTrackCount = gridRI.mRows.mSizes.Length();
   9825    nsTArray<nscoord> rowTrackPositions(rowTrackCount);
   9826    nsTArray<nscoord> rowTrackSizes(rowTrackCount);
   9827    nsTArray<uint32_t> rowTrackStates(rowTrackCount);
   9828    nsTArray<bool> rowRemovedRepeatTracks(
   9829        gridRI.mRowFunctions.mRemovedRepeatTracks.Clone());
   9830    uint32_t row = 0;
   9831    for (const TrackSize& sz : gridRI.mRows.mSizes) {
   9832      rowTrackPositions.AppendElement(sz.mPosition);
   9833      rowTrackSizes.AppendElement(sz.mBase);
   9834      bool isRepeat = ((row >= gridRI.mRowFunctions.mRepeatAutoStart) &&
   9835                       (row < gridRI.mRowFunctions.mRepeatAutoEnd));
   9836      rowTrackStates.AppendElement(
   9837          isRepeat ? (uint32_t)mozilla::dom::GridTrackState::Repeat
   9838                   : (uint32_t)mozilla::dom::GridTrackState::Static);
   9839 
   9840      row++;
   9841    }
   9842    // Get the number of explicit tracks first. The order of argument evaluation
   9843    // is implementation-defined. We should be OK here because colTrackSizes is
   9844    // taken by rvalue, but computing the size first prevents any changes in the
   9845    // argument types of the constructor from breaking this.
   9846    const uint32_t numRowExplicitTracks =
   9847        IsRowSubgrid() ? rowTrackSizes.Length()
   9848                       : gridRI.mRowFunctions.NumExplicitTracks();
   9849    // Row info has to accommodate fragmentation of the grid, which may happen
   9850    // in later calls to Reflow. For now, presume that no more fragmentation
   9851    // will occur.
   9852    ComputedGridTrackInfo* rowInfo = new ComputedGridTrackInfo(
   9853        gridRI.mRowFunctions.mExplicitGridOffset, numRowExplicitTracks,
   9854        gridRI.mStartRow, row, std::move(rowTrackPositions),
   9855        std::move(rowTrackSizes), std::move(rowTrackStates),
   9856        std::move(rowRemovedRepeatTracks),
   9857        gridRI.mRowFunctions.mRepeatAutoStart,
   9858        rowLineNameMap.GetResolvedLineNamesForComputedGridTrackInfo(),
   9859        IsRowSubgrid(), IsRowMasonry());
   9860    SetProperty(GridRowTrackInfo(), rowInfo);
   9861 
   9862    if (prevInFlow) {
   9863      // This frame is fragmenting rows from a previous frame, so patch up
   9864      // the prior GridRowTrackInfo with a new end row.
   9865 
   9866      // FIXME: This can be streamlined and/or removed when bug 1151204 lands.
   9867 
   9868      ComputedGridTrackInfo* priorRowInfo =
   9869          prevInFlow->GetProperty(GridRowTrackInfo());
   9870 
   9871      // Adjust track positions based on the first track in this fragment.
   9872      if (priorRowInfo->mPositions.Length() >
   9873          priorRowInfo->mStartFragmentTrack) {
   9874        nscoord delta =
   9875            priorRowInfo->mPositions[priorRowInfo->mStartFragmentTrack];
   9876        for (nscoord& pos : priorRowInfo->mPositions) {
   9877          pos -= delta;
   9878        }
   9879      }
   9880 
   9881      ComputedGridTrackInfo* revisedPriorRowInfo = new ComputedGridTrackInfo(
   9882          priorRowInfo->mNumLeadingImplicitTracks,
   9883          priorRowInfo->mNumExplicitTracks, priorRowInfo->mStartFragmentTrack,
   9884          gridRI.mStartRow, std::move(priorRowInfo->mPositions),
   9885          std::move(priorRowInfo->mSizes), std::move(priorRowInfo->mStates),
   9886          std::move(priorRowInfo->mRemovedRepeatTracks),
   9887          priorRowInfo->mRepeatFirstTrack,
   9888          std::move(priorRowInfo->mResolvedLineNames), priorRowInfo->mIsSubgrid,
   9889          priorRowInfo->mIsMasonry);
   9890      prevInFlow->SetProperty(GridRowTrackInfo(), revisedPriorRowInfo);
   9891    }
   9892 
   9893    // Generate the line info properties. We need to provide the number of
   9894    // repeat tracks produced in the reflow. Only explicit names are assigned
   9895    // to lines here; the mozilla::dom::GridLines class will later extract
   9896    // implicit names from grid areas and assign them to the appropriate lines.
   9897 
   9898    auto& colFunctions = gridRI.mColFunctions;
   9899 
   9900    // Generate column lines first.
   9901    uint32_t capacity = gridRI.mCols.mSizes.Length();
   9902    nsTArray<nsTArray<RefPtr<nsAtom>>> columnLineNames(capacity);
   9903    for (col = 0; col <= gridRI.mCols.mSizes.Length(); col++) {
   9904      // Offset col by the explicit grid offset, to get the original names.
   9905      nsTArray<RefPtr<nsAtom>> explicitNames =
   9906          colLineNameMap.GetExplicitLineNamesAtIndex(
   9907              col - colFunctions.mExplicitGridOffset);
   9908 
   9909      columnLineNames.EmplaceBack(std::move(explicitNames));
   9910    }
   9911    // Get the explicit names that follow a repeat auto declaration.
   9912    nsTArray<RefPtr<nsAtom>> colNamesFollowingRepeat;
   9913    nsTArray<RefPtr<nsAtom>> colBeforeRepeatAuto;
   9914    nsTArray<RefPtr<nsAtom>> colAfterRepeatAuto;
   9915    // Note: the following is only used for a non-subgridded axis.
   9916    if (colLineNameMap.HasRepeatAuto()) {
   9917      MOZ_ASSERT(!colFunctions.mTemplate.IsSubgrid());
   9918      // The line name list after the repeatAutoIndex holds the line names
   9919      // for the first explicit line after the repeat auto declaration.
   9920      uint32_t repeatAutoEnd = colLineNameMap.RepeatAutoStart() + 1;
   9921      for (auto* list : colLineNameMap.ExpandedLineNames()[repeatAutoEnd]) {
   9922        for (auto& name : list->AsSpan()) {
   9923          colNamesFollowingRepeat.AppendElement(name.AsAtom());
   9924        }
   9925      }
   9926      auto names = colLineNameMap.TrackAutoRepeatLineNames();
   9927      for (auto& name : names[0].AsSpan()) {
   9928        colBeforeRepeatAuto.AppendElement(name.AsAtom());
   9929      }
   9930      for (auto& name : names[1].AsSpan()) {
   9931        colAfterRepeatAuto.AppendElement(name.AsAtom());
   9932      }
   9933    }
   9934 
   9935    ComputedGridLineInfo* columnLineInfo = new ComputedGridLineInfo(
   9936        std::move(columnLineNames), std::move(colBeforeRepeatAuto),
   9937        std::move(colAfterRepeatAuto), std::move(colNamesFollowingRepeat));
   9938    SetProperty(GridColumnLineInfo(), columnLineInfo);
   9939 
   9940    // Generate row lines next.
   9941    auto& rowFunctions = gridRI.mRowFunctions;
   9942    capacity = gridRI.mRows.mSizes.Length();
   9943    nsTArray<nsTArray<RefPtr<nsAtom>>> rowLineNames(capacity);
   9944    for (row = 0; row <= gridRI.mRows.mSizes.Length(); row++) {
   9945      // Offset row by the explicit grid offset, to get the original names.
   9946      nsTArray<RefPtr<nsAtom>> explicitNames =
   9947          rowLineNameMap.GetExplicitLineNamesAtIndex(
   9948              row - rowFunctions.mExplicitGridOffset);
   9949      rowLineNames.EmplaceBack(std::move(explicitNames));
   9950    }
   9951    // Get the explicit names that follow a repeat auto declaration.
   9952    nsTArray<RefPtr<nsAtom>> rowNamesFollowingRepeat;
   9953    nsTArray<RefPtr<nsAtom>> rowBeforeRepeatAuto;
   9954    nsTArray<RefPtr<nsAtom>> rowAfterRepeatAuto;
   9955    // Note: the following is only used for a non-subgridded axis.
   9956    if (rowLineNameMap.HasRepeatAuto()) {
   9957      MOZ_ASSERT(!rowFunctions.mTemplate.IsSubgrid());
   9958      // The line name list after the repeatAutoIndex holds the line names
   9959      // for the first explicit line after the repeat auto declaration.
   9960      uint32_t repeatAutoEnd = rowLineNameMap.RepeatAutoStart() + 1;
   9961      for (auto* list : rowLineNameMap.ExpandedLineNames()[repeatAutoEnd]) {
   9962        for (auto& name : list->AsSpan()) {
   9963          rowNamesFollowingRepeat.AppendElement(name.AsAtom());
   9964        }
   9965      }
   9966      auto names = rowLineNameMap.TrackAutoRepeatLineNames();
   9967      for (auto& name : names[0].AsSpan()) {
   9968        rowBeforeRepeatAuto.AppendElement(name.AsAtom());
   9969      }
   9970      for (auto& name : names[1].AsSpan()) {
   9971        rowAfterRepeatAuto.AppendElement(name.AsAtom());
   9972      }
   9973    }
   9974 
   9975    ComputedGridLineInfo* rowLineInfo = new ComputedGridLineInfo(
   9976        std::move(rowLineNames), std::move(rowBeforeRepeatAuto),
   9977        std::move(rowAfterRepeatAuto), std::move(rowNamesFollowingRepeat));
   9978    SetProperty(GridRowLineInfo(), rowLineInfo);
   9979 
   9980    // Generate area info for explicit areas. Implicit areas are handled
   9981    // elsewhere.
   9982    if (!gridRI.mGridStyle->mGridTemplateAreas.IsNone()) {
   9983      auto* areas = new StyleOwnedSlice<NamedArea>(
   9984          gridRI.mGridStyle->mGridTemplateAreas.AsAreas()->areas);
   9985      SetProperty(ExplicitNamedAreasProperty(), areas);
   9986    } else {
   9987      RemoveProperty(ExplicitNamedAreasProperty());
   9988    }
   9989  }
   9990 
   9991  if (!prevInFlow) {
   9992    SharedGridData* sharedGridData = GetProperty(SharedGridData::Prop());
   9993    if (!aStatus.IsFullyComplete()) {
   9994      if (!sharedGridData) {
   9995        sharedGridData = new SharedGridData;
   9996        SetProperty(SharedGridData::Prop(), sharedGridData);
   9997      }
   9998      sharedGridData->mCols.mSizes = std::move(gridRI.mCols.mSizes);
   9999      sharedGridData->mCols.mContentBoxSize = gridRI.mCols.mContentBoxSize;
  10000      sharedGridData->mCols.mBaselineSubtreeAlign =
  10001          gridRI.mCols.mBaselineSubtreeAlign;
  10002      sharedGridData->mCols.mIsMasonry = gridRI.mCols.mIsMasonry;
  10003      sharedGridData->mRows.mSizes = std::move(gridRI.mRows.mSizes);
  10004      // Save the original row grid sizes and gaps so we can restore them later
  10005      // in GridReflowInput::Initialize for the continuations.
  10006      auto& origRowData = sharedGridData->mOriginalRowData;
  10007      origRowData.ClearAndRetainStorage();
  10008      origRowData.SetCapacity(sharedGridData->mRows.mSizes.Length());
  10009      nscoord prevTrackEnd = 0;
  10010      for (auto& sz : sharedGridData->mRows.mSizes) {
  10011        SharedGridData::RowData data = {sz.mBase, sz.mPosition - prevTrackEnd};
  10012        origRowData.AppendElement(data);
  10013        prevTrackEnd = sz.mPosition + sz.mBase;
  10014      }
  10015      sharedGridData->mRows.mContentBoxSize = gridRI.mRows.mContentBoxSize;
  10016      sharedGridData->mRows.mBaselineSubtreeAlign =
  10017          gridRI.mRows.mBaselineSubtreeAlign;
  10018      sharedGridData->mRows.mIsMasonry = gridRI.mRows.mIsMasonry;
  10019      sharedGridData->mGridItems = std::move(gridRI.mGridItems);
  10020      sharedGridData->mAbsPosItems = std::move(gridRI.mAbsPosItems);
  10021 
  10022      sharedGridData->mGenerateComputedGridInfo =
  10023          HasAnyStateBits(NS_STATE_GRID_COMPUTED_INFO);
  10024    } else if (sharedGridData && !GetNextInFlow()) {
  10025      RemoveProperty(SharedGridData::Prop());
  10026    }
  10027  }
  10028 
  10029  FinishAndStoreOverflow(&aDesiredSize);
  10030 }
  10031 
  10032 void nsGridContainerFrame::UpdateSubgridFrameState() {
  10033  nsFrameState oldBits = GetStateBits() & kIsSubgridBits;
  10034  nsFrameState newBits = ComputeSelfSubgridMasonryBits() & kIsSubgridBits;
  10035  if (newBits != oldBits) {
  10036    RemoveStateBits(kIsSubgridBits);
  10037    if (!newBits) {
  10038      RemoveProperty(Subgrid::Prop());
  10039    } else {
  10040      AddStateBits(newBits);
  10041    }
  10042  }
  10043 }
  10044 
  10045 nsFrameState nsGridContainerFrame::ComputeSelfSubgridMasonryBits() const {
  10046  nsFrameState bits = nsFrameState(0);
  10047  const auto* pos = StylePosition();
  10048 
  10049  // We can only have masonry layout in one axis.
  10050  if (pos->mGridTemplateRows.IsMasonry()) {
  10051    bits |= NS_STATE_GRID_IS_ROW_MASONRY;
  10052  } else if (pos->mGridTemplateColumns.IsMasonry()) {
  10053    bits |= NS_STATE_GRID_IS_COL_MASONRY;
  10054  }
  10055 
  10056  // NOTE: The rest of this function is only relevant if we're a subgrid;
  10057  // hence, we return early as soon as we rule out that possibility.
  10058 
  10059  // 'contain:layout/paint' makes us an "independent formatting context",
  10060  // which prevents us from being a subgrid in this case (but not always).
  10061  // We will also need to check our containing scroll frame for this property.
  10062  // https://drafts.csswg.org/css-display-3/#establish-an-independent-formatting-context
  10063  if (ShouldInhibitSubgridDueToIFC(this)) {
  10064    return bits;
  10065  }
  10066 
  10067  // Skip over our scroll frame and such if we have it, to find our "parent
  10068  // grid", if we have one.
  10069 
  10070  // After this loop, 'parent' will represent the parent of the outermost frame
  10071  // that shares our content node. (Normally this is just our parent frame, but
  10072  // if we're e.g. a scrolled frame, then this will be the parent of our
  10073  // wrapper-scrollable-frame.) If 'parent' turns out to be a grid container,
  10074  // then it's our "parent grid", and we could potentially be a subgrid of it.
  10075  auto* parent = GetParent();
  10076  while (parent && parent->GetContent() == GetContent()) {
  10077    // If we find our containing frame (e.g. our scroll frame) can't be a
  10078    // subgrid, then we can't be a subgrid, for the same reasons as above. This
  10079    // can happen when this frame is itself a grid item with "overflow:scroll"
  10080    // or similar.
  10081    if (ShouldInhibitSubgridDueToIFC(parent)) {
  10082      return bits;
  10083    }
  10084    parent = parent->GetParent();
  10085  }
  10086  const nsGridContainerFrame* parentGrid = do_QueryFrame(parent);
  10087  if (parentGrid) {
  10088    bool isOrthogonal =
  10089        GetWritingMode().IsOrthogonalTo(parent->GetWritingMode());
  10090    bool isColSubgrid = pos->mGridTemplateColumns.IsSubgrid();
  10091    // Subgridding a parent masonry axis makes us use masonry layout too,
  10092    // unless our other axis is a masonry axis.
  10093    if (isColSubgrid &&
  10094        parent->HasAnyStateBits(isOrthogonal ? NS_STATE_GRID_IS_ROW_MASONRY
  10095                                             : NS_STATE_GRID_IS_COL_MASONRY)) {
  10096      isColSubgrid = false;
  10097      if (!HasAnyStateBits(NS_STATE_GRID_IS_ROW_MASONRY)) {
  10098        bits |= NS_STATE_GRID_IS_COL_MASONRY;
  10099      }
  10100    }
  10101    if (isColSubgrid) {
  10102      bits |= NS_STATE_GRID_IS_COL_SUBGRID;
  10103    }
  10104 
  10105    bool isRowSubgrid = pos->mGridTemplateRows.IsSubgrid();
  10106    if (isRowSubgrid &&
  10107        parent->HasAnyStateBits(isOrthogonal ? NS_STATE_GRID_IS_COL_MASONRY
  10108                                             : NS_STATE_GRID_IS_ROW_MASONRY)) {
  10109      isRowSubgrid = false;
  10110      if (!HasAnyStateBits(NS_STATE_GRID_IS_COL_MASONRY)) {
  10111        bits |= NS_STATE_GRID_IS_ROW_MASONRY;
  10112      }
  10113    }
  10114    if (isRowSubgrid) {
  10115      bits |= NS_STATE_GRID_IS_ROW_SUBGRID;
  10116    }
  10117  }
  10118  return bits;
  10119 }
  10120 
  10121 void nsGridContainerFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
  10122                                nsIFrame* aPrevInFlow) {
  10123  nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
  10124 
  10125  if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
  10126    AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
  10127  }
  10128 
  10129  nsFrameState bits = nsFrameState(0);
  10130  if (MOZ_LIKELY(!aPrevInFlow)) {
  10131    bits = ComputeSelfSubgridMasonryBits();
  10132  } else {
  10133    bits = aPrevInFlow->GetStateBits() &
  10134           (NS_STATE_GRID_IS_ROW_MASONRY | NS_STATE_GRID_IS_COL_MASONRY |
  10135            kIsSubgridBits | NS_STATE_GRID_HAS_COL_SUBGRID_ITEM |
  10136            NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
  10137  }
  10138  AddStateBits(bits);
  10139 }
  10140 
  10141 void nsGridContainerFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
  10142  nsContainerFrame::DidSetComputedStyle(aOldStyle);
  10143 
  10144  if (!aOldStyle) {
  10145    return;  // Init() already initialized the bits.
  10146  }
  10147  UpdateSubgridFrameState();
  10148 }
  10149 
  10150 nscoord nsGridContainerFrame::ComputeIntrinsicISize(
  10151    const IntrinsicSizeInput& aInput, IntrinsicISizeType aType) {
  10152  GRID_LOG("Compute %s isize for grid container frame %p",
  10153           aType == IntrinsicISizeType::MinISize ? "min" : "pref", this);
  10154 
  10155  if (Maybe<nscoord> containISize = ContainIntrinsicISize()) {
  10156    return *containISize;
  10157  }
  10158 
  10159  // Calculate the sum of column sizes under intrinsic sizing.
  10160  // https://drafts.csswg.org/css-grid-2/#intrinsic-sizes
  10161  NormalizeChildLists();
  10162  GridReflowInput gridRI(this, *aInput.mContext);
  10163  // Ensure we do not measure flex tracks against unconstrained bounds.
  10164  gridRI.mIsGridIntrinsicSizing = true;
  10165  InitImplicitNamedAreas(gridRI.mGridStyle);  // XXX optimize
  10166 
  10167  // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
  10168  // https://drafts.csswg.org/css-grid-2/#auto-repeat
  10169  // They're only used for auto-repeat so we skip computing them otherwise.
  10170  RepeatTrackSizingInput repeatSizing(gridRI.mWM);
  10171  if (!IsColSubgrid() && gridRI.mColFunctions.mHasRepeatAuto) {
  10172    repeatSizing.InitFromStyle(
  10173        LogicalAxis::Inline, gridRI.mWM, gridRI.mFrame, gridRI.mFrame->Style(),
  10174        gridRI.mFrame->GetAspectRatio(), aInput.mContainingBlockSize);
  10175  }
  10176  if ((!IsRowSubgrid() && gridRI.mRowFunctions.mHasRepeatAuto &&
  10177       !(gridRI.mGridStyle->mGridAutoFlow & StyleGridAutoFlow::ROW)) ||
  10178      IsColMasonry()) {
  10179    // Only 'grid-auto-flow:column' can create new implicit columns, so that's
  10180    // the only case where our block-size can affect the number of columns.
  10181    // Masonry layout always depends on how many rows we have though.
  10182    repeatSizing.InitFromStyle(
  10183        LogicalAxis::Block, gridRI.mWM, gridRI.mFrame, gridRI.mFrame->Style(),
  10184        gridRI.mFrame->GetAspectRatio(), aInput.mContainingBlockSize);
  10185  }
  10186 
  10187  Grid grid;
  10188  if (MOZ_LIKELY(!IsSubgrid())) {
  10189    grid.PlaceGridItems(gridRI, repeatSizing);  // XXX optimize
  10190  } else {
  10191    auto* subgrid = GetProperty(Subgrid::Prop());
  10192    gridRI.mGridItems = subgrid->mGridItems.Clone();
  10193    gridRI.mAbsPosItems = subgrid->mAbsPosItems.Clone();
  10194    grid.mGridColEnd = subgrid->mGridColEnd;
  10195    grid.mGridRowEnd = subgrid->mGridRowEnd;
  10196  }
  10197 
  10198  auto constraint = aType == IntrinsicISizeType::MinISize
  10199                        ? SizingConstraint::MinContent
  10200                        : SizingConstraint::MaxContent;
  10201  if (IsColMasonry()) {
  10202    ReflowOutput desiredSize(gridRI.mWM);
  10203    nsSize containerSize;
  10204    LogicalRect contentArea(gridRI.mWM);
  10205    nsReflowStatus status;
  10206    gridRI.mRows.mSizes.SetLength(grid.mGridRowEnd);
  10207    gridRI.CalculateTrackSizesForAxis(LogicalAxis::Inline, grid,
  10208                                      NS_UNCONSTRAINEDSIZE, constraint);
  10209    return MasonryLayout(gridRI, contentArea, constraint, desiredSize, status,
  10210                         nullptr, containerSize);
  10211  }
  10212 
  10213  if (grid.mGridColEnd == 0) {
  10214    return nscoord(0);
  10215  }
  10216 
  10217  gridRI.CalculateTrackSizesForAxis(LogicalAxis::Inline, grid,
  10218                                    NS_UNCONSTRAINEDSIZE, constraint);
  10219 
  10220  const nscoord contentBoxBSize =
  10221      aInput.mPercentageBasisForChildren
  10222          ? aInput.mPercentageBasisForChildren->BSize(gridRI.mWM)
  10223          : NS_UNCONSTRAINEDSIZE;
  10224 
  10225  // Resolve row sizes so that when we re-resolve the column sizes, grid items
  10226  // with percent-valued block-sizes (and aspect ratios) have definite row
  10227  // sizes as the percentage basis. Their resolved block-size can then
  10228  // transfer to the inline-axis, contributing correctly to the grid
  10229  // container's intrinsic inline-size.
  10230  gridRI.CalculateTrackSizesForAxis(LogicalAxis::Block, grid, contentBoxBSize,
  10231                                    SizingConstraint::NoConstraint);
  10232 
  10233  // Invalidate the column sizes before re-resolving them.
  10234  gridRI.InvalidateTrackSizesForAxis(LogicalAxis::Inline);
  10235 
  10236  // Re-resolve the column sizes, using the resolved row sizes established
  10237  // above. See 12.1.3 of the Grid Sizing Algorithm for more scenarios where
  10238  // re-resolving the column sizes is necessary:
  10239  // https://drafts.csswg.org/css-grid-2/#algo-grid-sizing
  10240  gridRI.CalculateTrackSizesForAxis(LogicalAxis::Inline, grid,
  10241                                    NS_UNCONSTRAINEDSIZE, constraint);
  10242 
  10243  return gridRI.mCols.TotalTrackSizeWithoutAlignment(this);
  10244 }
  10245 
  10246 nscoord nsGridContainerFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
  10247                                             IntrinsicISizeType aType) {
  10248  auto* firstCont = static_cast<nsGridContainerFrame*>(FirstContinuation());
  10249  if (firstCont != this) {
  10250    return firstCont->IntrinsicISize(aInput, aType);
  10251  }
  10252  return mCachedIntrinsicSizes.GetOrSet(*this, aType, aInput, [&] {
  10253    return ComputeIntrinsicISize(aInput, aType);
  10254  });
  10255 }
  10256 
  10257 void nsGridContainerFrame::MarkIntrinsicISizesDirty() {
  10258  mCachedIntrinsicSizes.Clear();
  10259  for (auto& perAxisBaseline : mBaseline) {
  10260    for (auto& baseline : perAxisBaseline) {
  10261      baseline = NS_INTRINSIC_ISIZE_UNKNOWN;
  10262    }
  10263  }
  10264  nsContainerFrame::MarkIntrinsicISizesDirty();
  10265 }
  10266 
  10267 void nsGridContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
  10268                                            const nsDisplayListSet& aLists) {
  10269  DisplayBorderBackgroundOutline(aBuilder, aLists);
  10270  if (HidesContent()) {
  10271    return;
  10272  }
  10273 
  10274  if (GetPrevInFlow()) {
  10275    DisplayOverflowContainers(aBuilder, aLists);
  10276    DisplayPushedAbsoluteFrames(aBuilder, aLists);
  10277  }
  10278 
  10279  // Our children are all grid-level boxes, which behave the same as
  10280  // inline-blocks in painting, so their borders/backgrounds all go on
  10281  // the BlockBorderBackgrounds list.
  10282  typedef CSSOrderAwareFrameIterator::OrderState OrderState;
  10283  OrderState order =
  10284      HasAnyStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
  10285          ? OrderState::Ordered
  10286          : OrderState::Unordered;
  10287  CSSOrderAwareFrameIterator iter(
  10288      this, FrameChildListID::Principal,
  10289      CSSOrderAwareFrameIterator::ChildFilter::IncludeAll, order);
  10290  const auto flags = DisplayFlagsForFlexOrGridItem();
  10291  for (; !iter.AtEnd(); iter.Next()) {
  10292    nsIFrame* child = *iter;
  10293    BuildDisplayListForChild(aBuilder, child, aLists, flags);
  10294  }
  10295 }
  10296 
  10297 bool nsGridContainerFrame::DrainSelfOverflowList() {
  10298  return DrainAndMergeSelfOverflowList();
  10299 }
  10300 
  10301 void nsGridContainerFrame::AppendFrames(ChildListID aListID,
  10302                                        nsFrameList&& aFrameList) {
  10303  NoteNewChildren(aListID, aFrameList);
  10304  nsContainerFrame::AppendFrames(aListID, std::move(aFrameList));
  10305 }
  10306 
  10307 void nsGridContainerFrame::InsertFrames(
  10308    ChildListID aListID, nsIFrame* aPrevFrame,
  10309    const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
  10310  NoteNewChildren(aListID, aFrameList);
  10311  nsContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
  10312                                 std::move(aFrameList));
  10313 }
  10314 
  10315 void nsGridContainerFrame::RemoveFrame(DestroyContext& aContext,
  10316                                       ChildListID aListID,
  10317                                       nsIFrame* aOldFrame) {
  10318  MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list");
  10319 
  10320 #ifdef DEBUG
  10321  SetDidPushItemsBitIfNeeded(aListID, aOldFrame);
  10322 #endif
  10323 
  10324  nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame);
  10325 }
  10326 
  10327 nscoord nsGridContainerFrame::SynthesizeBaseline(
  10328    const FindItemInGridOrderResult& aGridOrderItem, LogicalAxis aAxis,
  10329    BaselineSharingGroup aGroup, const nsSize& aCBPhysicalSize, nscoord aCBSize,
  10330    WritingMode aCBWM) {
  10331  if (MOZ_UNLIKELY(!aGridOrderItem.mItem)) {
  10332    // No item in this fragment - synthesize a baseline from our border-box.
  10333    return ::SynthesizeBaselineFromBorderBox(aGroup, aCBWM, aAxis, aCBSize);
  10334  }
  10335 
  10336  nsIFrame* child = aGridOrderItem.mItem->mFrame;
  10337  nsGridContainerFrame* grid = do_QueryFrame(child);
  10338  auto childWM = child->GetWritingMode();
  10339  bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
  10340  const LogicalAxis childAxis = aCBWM.ConvertAxisTo(aAxis, childWM);
  10341  nscoord baseline;
  10342  nscoord start;
  10343  nscoord size;
  10344 
  10345  if (aAxis == LogicalAxis::Block) {
  10346    start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM);
  10347    size = child->BSize(aCBWM);
  10348    if (grid && aGridOrderItem.mIsInEdgeTrack) {
  10349      baseline = isOrthogonal ? grid->GetIBaseline(aGroup)
  10350                              : grid->GetBBaseline(aGroup);
  10351    } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack) {
  10352      // This assertion is mostly for documentation purposes; it must hold,
  10353      // given the checks in our 'if' statements. (We know aAxis is
  10354      // LogicalAxis::Block, and isOrthogonal is false, which means childAxis
  10355      // must be LogicalAxis::Block). If instead we got here with a childAxis of
  10356      // LogicalAxis::Inline, then our call to
  10357      // Baseline::SynthesizeBaselineFromBorderBox might incorrectly think
  10358      // it makes sense to use a central baseline, in an axis where that
  10359      // doesn't make sense.
  10360      MOZ_ASSERT(childAxis == LogicalAxis::Block, "unexpected childAxis");
  10361      baseline = child
  10362                     ->GetNaturalBaselineBOffset(childWM, aGroup,
  10363                                                 BaselineExportContext::Other)
  10364                     .valueOrFrom([aGroup, child, childWM]() {
  10365                       return Baseline::SynthesizeBOffsetFromBorderBox(
  10366                           child, childWM, aGroup);
  10367                     });
  10368    } else {
  10369      baseline =
  10370          ::SynthesizeBaselineFromBorderBox(aGroup, childWM, childAxis, size);
  10371    }
  10372  } else {
  10373    start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).I(aCBWM);
  10374    size = child->ISize(aCBWM);
  10375    if (grid && aGridOrderItem.mIsInEdgeTrack) {
  10376      baseline = isOrthogonal ? grid->GetBBaseline(aGroup)
  10377                              : grid->GetIBaseline(aGroup);
  10378    } else if (isOrthogonal && aGridOrderItem.mIsInEdgeTrack) {
  10379      baseline = child
  10380                     ->GetNaturalBaselineBOffset(childWM, aGroup,
  10381                                                 BaselineExportContext::Other)
  10382                     .valueOrFrom([aGroup, childWM, childAxis, size]() {
  10383                       return ::SynthesizeBaselineFromBorderBox(
  10384                           aGroup, childWM, childAxis, size);
  10385                     });
  10386    } else {
  10387      baseline =
  10388          ::SynthesizeBaselineFromBorderBox(aGroup, childWM, childAxis, size);
  10389    }
  10390  }
  10391  return aGroup == BaselineSharingGroup::First
  10392             ? start + baseline
  10393             : aCBSize - start - size + baseline;
  10394 }
  10395 
  10396 void nsGridContainerFrame::CalculateBaselines(
  10397    BaselineSet aBaselineSet, CSSOrderAwareFrameIterator* aIter,
  10398    const nsTArray<GridItemInfo>* aGridItems, const Tracks& aTracks,
  10399    uint32_t aFragmentStartTrack, uint32_t aFirstExcludedTrack, WritingMode aWM,
  10400    const nsSize& aCBPhysicalSize, nscoord aCBBorderPaddingStart,
  10401    nscoord aCBBorderPaddingEnd, nscoord aCBSize) {
  10402  const auto axis = aTracks.mAxis;
  10403 
  10404  auto firstBaseline = aTracks.GetBaseline(0, BaselineSharingGroup::First);
  10405  if (!(aBaselineSet & BaselineSet::eFirst)) {
  10406    mBaseline[axis][BaselineSharingGroup::First] =
  10407        ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::First, aWM,
  10408                                          axis, aCBSize);
  10409  } else if (firstBaseline.isNothing()) {
  10410    FindItemInGridOrderResult gridOrderFirstItem = FindFirstItemInGridOrder(
  10411        *aIter, *aGridItems,
  10412        axis == LogicalAxis::Block ? &GridArea::mRows : &GridArea::mCols,
  10413        axis == LogicalAxis::Block ? &GridArea::mCols : &GridArea::mRows,
  10414        aFragmentStartTrack);
  10415    mBaseline[axis][BaselineSharingGroup::First] = SynthesizeBaseline(
  10416        gridOrderFirstItem, axis, BaselineSharingGroup::First, aCBPhysicalSize,
  10417        aCBSize, aWM);
  10418  } else {
  10419    // We have a 'first baseline' group in the start track in this fragment.
  10420    // Convert it from track to grid container border-box coordinates.
  10421    MOZ_ASSERT(!aGridItems->IsEmpty());
  10422    nscoord gapBeforeStartTrack =
  10423        aFragmentStartTrack == 0
  10424            ? aTracks.GridLineEdge(aFragmentStartTrack,
  10425                                   GridLineSide::AfterGridGap)
  10426            : nscoord(0);  // no content gap at start of fragment
  10427    mBaseline[axis][BaselineSharingGroup::First] =
  10428        aCBBorderPaddingStart + gapBeforeStartTrack + *firstBaseline;
  10429  }
  10430 
  10431  auto lastBaseline = aTracks.GetBaseline(aTracks.mBaselines.Length() - 1,
  10432                                          BaselineSharingGroup::Last);
  10433  if (!(aBaselineSet & BaselineSet::eLast)) {
  10434    mBaseline[axis][BaselineSharingGroup::Last] =
  10435        ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::Last, aWM, axis,
  10436                                          aCBSize);
  10437  } else if (lastBaseline.isNothing()) {
  10438    // For finding items for the 'last baseline' we need to create a reverse
  10439    // iterator ('aIter' is the forward iterator from the GridReflowInput).
  10440    using Iter = ReverseCSSOrderAwareFrameIterator;
  10441    auto orderState = aIter->ItemsAreAlreadyInOrder()
  10442                          ? Iter::OrderState::Ordered
  10443                          : Iter::OrderState::Unordered;
  10444    Iter iter(this, FrameChildListID::Principal,
  10445              Iter::ChildFilter::SkipPlaceholders, orderState);
  10446    iter.SetItemCount(aGridItems->Length());
  10447    FindItemInGridOrderResult gridOrderLastItem = FindLastItemInGridOrder(
  10448        iter, *aGridItems,
  10449        axis == LogicalAxis::Block ? &GridArea::mRows : &GridArea::mCols,
  10450        axis == LogicalAxis::Block ? &GridArea::mCols : &GridArea::mRows,
  10451        aFragmentStartTrack, aFirstExcludedTrack);
  10452    mBaseline[axis][BaselineSharingGroup::Last] =
  10453        SynthesizeBaseline(gridOrderLastItem, axis, BaselineSharingGroup::Last,
  10454                           aCBPhysicalSize, aCBSize, aWM);
  10455  } else {
  10456    // We have a 'last baseline' group in the end track in this fragment.
  10457    // Convert it from track to grid container border-box coordinates.
  10458    MOZ_ASSERT(!aGridItems->IsEmpty());
  10459    auto borderBoxStartToEndOfEndTrack =
  10460        aCBBorderPaddingStart +
  10461        aTracks.GridLineEdge(aFirstExcludedTrack, GridLineSide::BeforeGridGap) -
  10462        aTracks.GridLineEdge(aFragmentStartTrack, GridLineSide::BeforeGridGap);
  10463    mBaseline[axis][BaselineSharingGroup::Last] =
  10464        (aCBSize - borderBoxStartToEndOfEndTrack) + *lastBaseline;
  10465  }
  10466 }
  10467 
  10468 #ifdef DEBUG_FRAME_DUMP
  10469 nsresult nsGridContainerFrame::GetFrameName(nsAString& aResult) const {
  10470  return MakeFrameName(u"GridContainer"_ns, aResult);
  10471 }
  10472 
  10473 void nsGridContainerFrame::ExtraContainerFrameInfo(
  10474    nsACString& aTo, bool aListOnlyDeterministic) const {
  10475  if (const void* const subgrid = GetProperty(Subgrid::Prop())) {
  10476    aTo += "[subgrid";
  10477    ListPtr(aTo, aListOnlyDeterministic, subgrid);
  10478    aTo += "]";
  10479  }
  10480 }
  10481 
  10482 #endif
  10483 
  10484 /* static */ nsGridContainerFrame::FindItemInGridOrderResult
  10485 nsGridContainerFrame::FindFirstItemInGridOrder(
  10486    CSSOrderAwareFrameIterator& aIter, const nsTArray<GridItemInfo>& aGridItems,
  10487    LineRange GridArea::* aMajor, LineRange GridArea::* aMinor,
  10488    uint32_t aFragmentStartTrack) {
  10489  FindItemInGridOrderResult result = {nullptr, false};
  10490  uint32_t minMajor = kTranslatedMaxLine + 1;
  10491  uint32_t minMinor = kTranslatedMaxLine + 1;
  10492  aIter.Reset();
  10493  for (; !aIter.AtEnd(); aIter.Next()) {
  10494    const GridItemInfo& item = aGridItems[aIter.ItemIndex()];
  10495    if ((item.mArea.*aMajor).mEnd <= aFragmentStartTrack) {
  10496      continue;  // item doesn't span any track in this fragment
  10497    }
  10498    uint32_t major = (item.mArea.*aMajor).mStart;
  10499    uint32_t minor = (item.mArea.*aMinor).mStart;
  10500    if (major < minMajor || (major == minMajor && minor < minMinor)) {
  10501      minMajor = major;
  10502      minMinor = minor;
  10503      result.mItem = &item;
  10504      result.mIsInEdgeTrack = major == 0U;
  10505    }
  10506  }
  10507  return result;
  10508 }
  10509 
  10510 /* static */ nsGridContainerFrame::FindItemInGridOrderResult
  10511 nsGridContainerFrame::FindLastItemInGridOrder(
  10512    ReverseCSSOrderAwareFrameIterator& aIter,
  10513    const nsTArray<GridItemInfo>& aGridItems, LineRange GridArea::* aMajor,
  10514    LineRange GridArea::* aMinor, uint32_t aFragmentStartTrack,
  10515    uint32_t aFirstExcludedTrack) {
  10516  FindItemInGridOrderResult result = {nullptr, false};
  10517  int32_t maxMajor = -1;
  10518  int32_t maxMinor = -1;
  10519  aIter.Reset();
  10520  int32_t lastMajorTrack = int32_t(aFirstExcludedTrack) - 1;
  10521  for (; !aIter.AtEnd(); aIter.Next()) {
  10522    const GridItemInfo& item = aGridItems[aIter.ItemIndex()];
  10523    // Subtract 1 from the end line to get the item's last track index.
  10524    int32_t major = (item.mArea.*aMajor).mEnd - 1;
  10525    // Currently, this method is only called with aFirstExcludedTrack ==
  10526    // the first track in the next fragment, so we take the opportunity
  10527    // to assert this item really belongs to this fragment.
  10528    MOZ_ASSERT((item.mArea.*aMajor).mStart < aFirstExcludedTrack,
  10529               "found an item that belongs to some later fragment");
  10530    if (major < int32_t(aFragmentStartTrack)) {
  10531      continue;  // item doesn't span any track in this fragment
  10532    }
  10533    int32_t minor = (item.mArea.*aMinor).mEnd - 1;
  10534    MOZ_ASSERT(minor >= 0 && major >= 0, "grid item must have span >= 1");
  10535    if (major > maxMajor || (major == maxMajor && minor > maxMinor)) {
  10536      maxMajor = major;
  10537      maxMinor = minor;
  10538      result.mItem = &item;
  10539      result.mIsInEdgeTrack = major == lastMajorTrack;
  10540    }
  10541  }
  10542  return result;
  10543 }
  10544 
  10545 nsGridContainerFrame::UsedTrackSizes* nsGridContainerFrame::GetUsedTrackSizes()
  10546    const {
  10547  return GetProperty(UsedTrackSizes::Prop());
  10548 }
  10549 
  10550 void nsGridContainerFrame::StoreUsedTrackSizes(LogicalAxis aAxis,
  10551                                               const TrackPlan& aSizes) {
  10552  auto* uts = GetUsedTrackSizes();
  10553  if (!uts) {
  10554    uts = new UsedTrackSizes();
  10555    SetProperty(UsedTrackSizes::Prop(), uts);
  10556  }
  10557  uts->mTrackPlans[aAxis].Assign(aSizes);
  10558  uts->mCanResolveLineRangeSize[aAxis] = true;
  10559  // XXX is resetting these bits necessary?
  10560  for (auto& sz : uts->mTrackPlans[aAxis]) {
  10561    sz.mState &= ~(TrackSize::eFrozen | TrackSize::eSkipGrowUnlimited |
  10562                   TrackSize::eInfinitelyGrowable);
  10563  }
  10564 }
  10565 
  10566 #ifdef DEBUG
  10567 void nsGridContainerFrame::SetInitialChildList(ChildListID aListID,
  10568                                               nsFrameList&& aChildList) {
  10569  ChildListIDs supportedLists = {FrameChildListID::Principal};
  10570  MOZ_ASSERT(supportedLists.contains(aListID), "unexpected child list");
  10571  return nsContainerFrame::SetInitialChildList(aListID, std::move(aChildList));
  10572 }
  10573 
  10574 void nsGridContainerFrame::TrackSize::DumpStateBits(StateBits aState) {
  10575  printf("min:");
  10576  if (aState & eAutoMinSizing) {
  10577    printf("auto ");
  10578  } else if (aState & eMinContentMinSizing) {
  10579    printf("min-content ");
  10580  } else if (aState & eMaxContentMinSizing) {
  10581    printf("max-content ");
  10582  }
  10583  printf(" max:");
  10584  if (aState & eAutoMaxSizing) {
  10585    printf("auto ");
  10586  } else if (aState & eMinContentMaxSizing) {
  10587    printf("min-content ");
  10588  } else if (aState & eMaxContentMaxSizing) {
  10589    printf("max-content ");
  10590  } else if (aState & eFlexMaxSizing) {
  10591    printf("flex ");
  10592  }
  10593  if (aState & eFrozen) {
  10594    printf("frozen ");
  10595  }
  10596  if (aState & eModified) {
  10597    printf("modified ");
  10598  }
  10599  if (aState & eBreakBefore) {
  10600    printf("break-before ");
  10601  }
  10602 }
  10603 
  10604 void nsGridContainerFrame::TrackSize::Dump() const {
  10605  printf("mPosition=%d mBase=%d mLimit=%d ", mPosition, mBase, mLimit);
  10606  DumpStateBits(mState);
  10607 }
  10608 
  10609 #endif  // DEBUG
  10610 
  10611 bool nsGridContainerFrame::IsMasonry(LogicalAxis aAxis) const {
  10612  return HasAnyStateBits(aAxis == mozilla::LogicalAxis::Block
  10613                             ? NS_STATE_GRID_IS_ROW_MASONRY
  10614                             : NS_STATE_GRID_IS_COL_MASONRY);
  10615 }
  10616 
  10617 bool nsGridContainerFrame::GridItemShouldStretch(const nsIFrame* aChild,
  10618                                                 LogicalAxis aAxis) const {
  10619  MOZ_ASSERT(aChild->IsGridItem());
  10620 
  10621  if (aChild->IsGridContainerFrame()) {
  10622    // The subgrid is always stretched in its subgridded dimensions.
  10623    // https://drafts.csswg.org/css-grid-2/#subgrid-box-alignment
  10624    const auto* gridContainer =
  10625        static_cast<const nsGridContainerFrame*>(aChild);
  10626    if (gridContainer->IsSubgrid(aAxis)) {
  10627      return true;
  10628    }
  10629  }
  10630 
  10631  const auto wm = aChild->GetWritingMode();
  10632  if (aChild->StyleMargin()->HasAuto(aAxis, wm,
  10633                                     AnchorPosResolutionParams::From(aChild))) {
  10634    // Per https://drafts.csswg.org/css-grid-2/#auto-margins, any 'auto' margin
  10635    // in an axis disables the alignment property in that axis.
  10636    return false;
  10637  }
  10638 
  10639  const auto cbwm = GetWritingMode();
  10640  if (IsMasonry(wm, aAxis)) {
  10641    // The child is in the container's masonry-axis.
  10642    // AlignJustifyTracksInMasonryAxis will stretch it, so we don't report that
  10643    // here.
  10644    return false;
  10645  }
  10646 
  10647  const auto alignment =
  10648      aChild->StylePosition()->UsedSelfAlignment(wm, aAxis, cbwm, Style());
  10649  // An item with 'normal' alignment that is a replaced frame should use its
  10650  // natural size, and not fill the grid area.
  10651  // https://drafts.csswg.org/css-grid-2/#grid-item-sizing
  10652  if (MOZ_LIKELY(alignment == StyleAlignFlags::NORMAL)) {
  10653    return !aChild->HasReplacedSizing();
  10654  }
  10655  return alignment == StyleAlignFlags::STRETCH;
  10656 }
  10657 
  10658 bool nsGridContainerFrame::ShouldInhibitSubgridDueToIFC(
  10659    const nsIFrame* aFrame) {
  10660  // Just checking for things that make us establish an independent formatting
  10661  // context (IFC) and hence prevent us from being a subgrid:
  10662  // * Out-of-flow (e.g. abspos) frames also establish an IFC.  Note, our
  10663  // NS_FRAME_OUT_OF_FLOW bit potentially isn't set yet, so we check our style.
  10664  // * contain:layout and contain:paint each make us establish an IFC.
  10665  const auto* display = aFrame->StyleDisplay();
  10666  return display->IsContainLayout() || display->IsContainPaint() ||
  10667         display->mContainerType &
  10668             (StyleContainerType::SIZE | StyleContainerType::INLINE_SIZE) ||
  10669         display->IsAbsolutelyPositionedStyle();
  10670 }
  10671 
  10672 nsGridContainerFrame* nsGridContainerFrame::GetGridContainerFrame(
  10673    nsIFrame* aFrame) {
  10674  nsGridContainerFrame* gridFrame = nullptr;
  10675 
  10676  if (aFrame) {
  10677    nsIFrame* inner = aFrame;
  10678    if (MOZ_UNLIKELY(aFrame->IsFieldSetFrame())) {
  10679      inner = static_cast<nsFieldSetFrame*>(aFrame)->GetInner();
  10680    }
  10681    // Since "Get" methods like GetInner and GetContentInsertionFrame can
  10682    // return null, we check the return values before dereferencing. Our
  10683    // calling pattern makes this unlikely, but we're being careful.
  10684    nsIFrame* insertionFrame =
  10685        inner ? inner->GetContentInsertionFrame() : nullptr;
  10686    nsIFrame* possibleGridFrame = insertionFrame ? insertionFrame : aFrame;
  10687    gridFrame = possibleGridFrame->IsGridContainerFrame()
  10688                    ? static_cast<nsGridContainerFrame*>(possibleGridFrame)
  10689                    : nullptr;
  10690  }
  10691  return gridFrame;
  10692 }
  10693 
  10694 nsGridContainerFrame* nsGridContainerFrame::GetGridFrameWithComputedInfo(
  10695    nsIFrame* aFrame) {
  10696  nsGridContainerFrame* gridFrame = GetGridContainerFrame(aFrame);
  10697  if (!gridFrame) {
  10698    return nullptr;
  10699  }
  10700 
  10701  auto HasComputedInfo = [](const nsGridContainerFrame& aFrame) -> bool {
  10702    return aFrame.HasProperty(GridColTrackInfo()) &&
  10703           aFrame.HasProperty(GridRowTrackInfo()) &&
  10704           aFrame.HasProperty(GridColumnLineInfo()) &&
  10705           aFrame.HasProperty(GridRowLineInfo());
  10706  };
  10707 
  10708  if (HasComputedInfo(*gridFrame)) {
  10709    return gridFrame;
  10710  }
  10711 
  10712  // Trigger a reflow that generates additional grid property data.
  10713  // Hold onto aFrame while we do this, in case reflow destroys it.
  10714  AutoWeakFrame weakFrameRef(gridFrame);
  10715 
  10716  RefPtr<mozilla::PresShell> presShell = gridFrame->PresShell();
  10717  gridFrame->AddStateBits(NS_STATE_GRID_COMPUTED_INFO);
  10718  presShell->FrameNeedsReflow(gridFrame, IntrinsicDirty::None,
  10719                              NS_FRAME_IS_DIRTY);
  10720  presShell->FlushPendingNotifications(FlushType::Layout);
  10721 
  10722  // If the weakFrameRef is no longer valid, then we must bail out.
  10723  if (!weakFrameRef.IsAlive()) {
  10724    return nullptr;
  10725  }
  10726 
  10727  // This can happen if for some reason we ended up not reflowing, like in print
  10728  // preview under some circumstances.
  10729  if (MOZ_UNLIKELY(!HasComputedInfo(*gridFrame))) {
  10730    return nullptr;
  10731  }
  10732 
  10733  return gridFrame;
  10734 }
  10735 
  10736 void nsGridContainerFrame::MarkCachedGridMeasurementsDirty(
  10737    nsIFrame* aItemFrame) {
  10738  MOZ_ASSERT(aItemFrame->IsGridItem());
  10739  aItemFrame->RemoveProperty(CachedBAxisMeasurement::Prop());
  10740 }
  10741 
  10742 // TODO: This is a rather dumb implementation of nsILineIterator, but it's
  10743 // better than our pre-existing behavior. Ideally, we should probably use the
  10744 // grid information to return a meaningful number of lines etc.
  10745 bool nsGridContainerFrame::IsLineIteratorFlowRTL() { return false; }
  10746 
  10747 int32_t nsGridContainerFrame::GetNumLines() const {
  10748  return mFrames.GetLength();
  10749 }
  10750 
  10751 Result<nsILineIterator::LineInfo, nsresult> nsGridContainerFrame::GetLine(
  10752    int32_t aLineNumber) {
  10753  if (aLineNumber < 0 || aLineNumber >= GetNumLines()) {
  10754    return Err(NS_ERROR_FAILURE);
  10755  }
  10756  LineInfo rv;
  10757  nsIFrame* f = mFrames.FrameAt(aLineNumber);
  10758  rv.mLineBounds = f->GetRect();
  10759  rv.mFirstFrameOnLine = f;
  10760  rv.mNumFramesOnLine = 1;
  10761  return rv;
  10762 }
  10763 
  10764 int32_t nsGridContainerFrame::FindLineContaining(const nsIFrame* aFrame,
  10765                                                 int32_t aStartLine) {
  10766  const int32_t index = mFrames.IndexOf(aFrame);
  10767  if (index < 0) {
  10768    return -1;
  10769  }
  10770  if (index < aStartLine) {
  10771    return -1;
  10772  }
  10773  return index;
  10774 }
  10775 
  10776 NS_IMETHODIMP
  10777 nsGridContainerFrame::CheckLineOrder(int32_t aLine, bool* aIsReordered,
  10778                                     nsIFrame** aFirstVisual,
  10779                                     nsIFrame** aLastVisual) {
  10780  *aIsReordered = false;
  10781  *aFirstVisual = nullptr;
  10782  *aLastVisual = nullptr;
  10783  return NS_OK;
  10784 }
  10785 
  10786 NS_IMETHODIMP
  10787 nsGridContainerFrame::FindFrameAt(int32_t aLineNumber, nsPoint aPos,
  10788                                  nsIFrame** aFrameFound,
  10789                                  bool* aPosIsBeforeFirstFrame,
  10790                                  bool* aPosIsAfterLastFrame) {
  10791  const auto wm = GetWritingMode();
  10792  const LogicalPoint pos(wm, aPos, GetSize());
  10793 
  10794  *aFrameFound = nullptr;
  10795  *aPosIsBeforeFirstFrame = true;
  10796  *aPosIsAfterLastFrame = false;
  10797 
  10798  nsIFrame* f = mFrames.FrameAt(aLineNumber);
  10799  if (!f) {
  10800    return NS_OK;
  10801  }
  10802 
  10803  auto rect = f->GetLogicalRect(wm, GetSize());
  10804  *aFrameFound = f;
  10805  *aPosIsBeforeFirstFrame = pos.I(wm) < rect.IStart(wm);
  10806  *aPosIsAfterLastFrame = pos.I(wm) > rect.IEnd(wm);
  10807  return NS_OK;
  10808 }
  10809 
  10810 void nsGridContainerFrame::TrackPlan::Initialize(TrackSizingPhase aPhase,
  10811                                                 const Tracks& aTracks) {
  10812  MOZ_ASSERT(mTrackSizes.Length() == aTracks.mSizes.Length());
  10813  auto plan = mTrackSizes.begin();
  10814  auto sz = aTracks.mSizes.begin();
  10815  for (; plan != mTrackSizes.end() && sz != aTracks.mSizes.end();
  10816       plan++, sz++) {
  10817    plan->mBase = Tracks::StartSizeInDistribution(aPhase, *sz);
  10818    MOZ_ASSERT(aPhase == TrackSizingPhase::MaxContentMaximums ||
  10819                   !(sz->mState & TrackSize::eInfinitelyGrowable),
  10820               "forgot to reset the eInfinitelyGrowable bit?");
  10821    plan->mState = sz->mState;
  10822  }
  10823 }
  10824 
  10825 // Distribute space to all flex tracks this item spans.
  10826 // https://drafts.csswg.org/css-grid-2/#algo-spanning-flex-items
  10827 nscoord nsGridContainerFrame::TrackPlan::DistributeToFlexTrackSizes(
  10828    nscoord aAvailableSpace, const nsTArray<uint32_t>& aGrowableTracks,
  10829    const TrackSizingFunctions& aFunctions,
  10830    const nsGridContainerFrame::Tracks& aTracks) {
  10831  nscoord space = aAvailableSpace;
  10832  // Measure used fraction.
  10833  double totalFr = 0.0;
  10834  // TODO alaskanemily: we should be subtracting definite-sized tracks from
  10835  // the available space below.
  10836  for (uint32_t track : aGrowableTracks) {
  10837    MOZ_ASSERT(aTracks.mSizes[track].mState & TrackSize::eFlexMaxSizing,
  10838               "Only flex-sized tracks should be growable during step 4");
  10839    totalFr += aFunctions.MaxSizingFor(track).AsFr();
  10840  }
  10841  MOZ_ASSERT(totalFr >= 0.0, "flex fractions must be non-negative.");
  10842 
  10843  double frSize = aAvailableSpace;
  10844  if (totalFr > 1.0) {
  10845    frSize /= totalFr;
  10846  }
  10847  // Distribute the space to the tracks proportionally to the fractional
  10848  // sizes.
  10849  for (uint32_t track : aGrowableTracks) {
  10850    TrackSize& sz = mTrackSizes[track];
  10851    if (sz.IsFrozen()) {
  10852      continue;
  10853    }
  10854    const double trackFr = aFunctions.MaxSizingFor(track).AsFr();
  10855    nscoord size = NSToCoordRoundWithClamp(frSize * trackFr);
  10856    // This shouldn't happen in theory, but it could happen due to a
  10857    // combination of floating-point error during the multiplication above
  10858    // and loss of precision in the cast.
  10859    if (MOZ_UNLIKELY(size > space)) {
  10860      size = space;
  10861      space = 0;
  10862    } else {
  10863      space -= size;
  10864    }
  10865    sz.mBase = std::max(sz.mBase, size);
  10866  }
  10867  return space;
  10868 }
  10869 
  10870 void nsGridContainerFrame::ItemPlan::Initialize(
  10871    TrackSizingPhase aPhase, const nsTArray<uint32_t>& aGrowableTracks,
  10872    const nsGridContainerFrame::Tracks& aTracks) {
  10873  for (uint32_t track : aGrowableTracks) {
  10874    auto& plan = mTrackSizes[track];
  10875    const TrackSize& sz = aTracks.mSizes[track];
  10876    plan.mBase = Tracks::StartSizeInDistribution(aPhase, sz);
  10877    bool unlimited = sz.mState & TrackSize::eInfinitelyGrowable;
  10878    plan.mLimit = unlimited ? NS_UNCONSTRAINEDSIZE : sz.mLimit;
  10879    plan.mState = sz.mState;
  10880  }
  10881 }
  10882 
  10883 nscoord nsGridContainerFrame::ItemPlan::GrowTracksToLimit(
  10884    nscoord aAvailableSpace, const nsTArray<uint32_t>& aGrowableTracks,
  10885    const FitContentClamper& aFitContentClamper) {
  10886  MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0);
  10887  nscoord space = aAvailableSpace;
  10888  uint32_t numGrowable = aGrowableTracks.Length();
  10889  while (true) {
  10890    nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
  10891    for (uint32_t track : aGrowableTracks) {
  10892      TrackSize& sz = mTrackSizes[track];
  10893      if (sz.IsFrozen()) {
  10894        continue;
  10895      }
  10896      nscoord newBase = sz.mBase + spacePerTrack;
  10897      nscoord limit = sz.mLimit;
  10898      if (MOZ_UNLIKELY((sz.mState & TrackSize::eApplyFitContentClamping) &&
  10899                       aFitContentClamper)) {
  10900        // Clamp the limit to the fit-content() size, for §12.5.2 step 5/6.
  10901        aFitContentClamper(track, sz.mBase, &limit);
  10902      }
  10903      if (newBase > limit) {
  10904        nscoord consumed = limit - sz.mBase;
  10905        if (consumed > 0) {
  10906          space -= consumed;
  10907          sz.mBase = limit;
  10908        }
  10909        sz.mState |= TrackSize::eFrozen;
  10910        if (--numGrowable == 0) {
  10911          return space;
  10912        }
  10913      } else {
  10914        sz.mBase = newBase;
  10915        space -= spacePerTrack;
  10916      }
  10917      MOZ_ASSERT(space >= 0);
  10918      if (space == 0) {
  10919        return 0;
  10920      }
  10921    }
  10922  }
  10923  MOZ_ASSERT_UNREACHABLE("we don't exit the loop above except by return");
  10924  return 0;
  10925 }
  10926 
  10927 uint32_t nsGridContainerFrame::ItemPlan::MarkExcludedTracks(
  10928    TrackSizingPhase aPhase, const nsTArray<uint32_t>& aGrowableTracks,
  10929    SizingConstraint aConstraint) {
  10930  uint32_t numGrowable = aGrowableTracks.Length();
  10931  if (aPhase == TrackSizingPhase::IntrinsicMaximums ||
  10932      aPhase == TrackSizingPhase::MaxContentMaximums) {
  10933    // "when handling any intrinsic growth limit: all affected tracks"
  10934    return numGrowable;
  10935  }
  10936 
  10937  TrackSize::StateBits selector = Tracks::SelectorForPhase(aPhase, aConstraint);
  10938  numGrowable = MarkExcludedTracks(
  10939      numGrowable, aGrowableTracks, TrackSize::eMaxContentMinSizing,
  10940      TrackSize::eMaxContentMaxSizing, TrackSize::eSkipGrowUnlimited1);
  10941  // Note that eMaxContentMinSizing is always included. We do those first:
  10942  if ((selector &= ~TrackSize::eMaxContentMinSizing)) {
  10943    numGrowable = MarkExcludedTracks(numGrowable, aGrowableTracks, selector,
  10944                                     TrackSize::eIntrinsicMaxSizing,
  10945                                     TrackSize::eSkipGrowUnlimited2);
  10946  }
  10947  return numGrowable;
  10948 }
  10949 
  10950 uint32_t nsGridContainerFrame::ItemPlan::MarkExcludedTracks(
  10951    uint32_t aNumGrowable, const nsTArray<uint32_t>& aGrowableTracks,
  10952    TrackSize::StateBits aMinSizingSelector,
  10953    TrackSize::StateBits aMaxSizingSelector, TrackSize::StateBits aSkipFlag) {
  10954  bool foundOneSelected = false;
  10955  bool foundOneGrowable = false;
  10956  uint32_t numGrowable = aNumGrowable;
  10957  for (uint32_t track : aGrowableTracks) {
  10958    TrackSize& sz = mTrackSizes[track];
  10959    const auto state = sz.mState;
  10960    if (state & aMinSizingSelector) {
  10961      foundOneSelected = true;
  10962      if (state & aMaxSizingSelector) {
  10963        foundOneGrowable = true;
  10964        continue;
  10965      }
  10966      sz.mState |= aSkipFlag;
  10967      MOZ_ASSERT(numGrowable != 0);
  10968      --numGrowable;
  10969    }
  10970  }
  10971  // 12.5 "if there are no such tracks, then all affected tracks"
  10972  if (foundOneSelected && !foundOneGrowable) {
  10973    for (uint32_t track : aGrowableTracks) {
  10974      mTrackSizes[track].mState &= ~aSkipFlag;
  10975    }
  10976    numGrowable = aNumGrowable;
  10977  }
  10978  return numGrowable;
  10979 }
  10980 
  10981 void nsGridContainerFrame::ItemPlan::GrowSelectedTracksUnlimited(
  10982    nscoord aAvailableSpace, const nsTArray<uint32_t>& aGrowableTracks,
  10983    uint32_t aNumGrowable, const FitContentClamper& aFitContentClamper) {
  10984  MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0 &&
  10985             aNumGrowable <= aGrowableTracks.Length());
  10986  nscoord space = aAvailableSpace;
  10987  DebugOnly<bool> didClamp = false;
  10988  while (aNumGrowable) {
  10989    nscoord spacePerTrack = std::max<nscoord>(space / aNumGrowable, 1);
  10990    for (uint32_t track : aGrowableTracks) {
  10991      TrackSize& sz = mTrackSizes[track];
  10992      if (sz.mState & TrackSize::eSkipGrowUnlimited) {
  10993        continue;  // an excluded track
  10994      }
  10995      nscoord delta = spacePerTrack;
  10996      nscoord newBase = sz.mBase + delta;
  10997      if (MOZ_UNLIKELY((sz.mState & TrackSize::eApplyFitContentClamping) &&
  10998                       aFitContentClamper)) {
  10999        // Clamp newBase to the fit-content() size, for §12.5.2 step 5/6.
  11000        if (aFitContentClamper(track, sz.mBase, &newBase)) {
  11001          didClamp = true;
  11002          delta = newBase - sz.mBase;
  11003          MOZ_ASSERT(delta >= 0, "track size shouldn't shrink");
  11004          sz.mState |= TrackSize::eSkipGrowUnlimited1;
  11005          --aNumGrowable;
  11006        }
  11007      }
  11008      sz.mBase = newBase;
  11009      space -= delta;
  11010      MOZ_ASSERT(space >= 0);
  11011      if (space == 0) {
  11012        return;
  11013      }
  11014    }
  11015  }
  11016  MOZ_ASSERT(didClamp,
  11017             "we don't exit the loop above except by return, "
  11018             "unless we clamped some track's size");
  11019 }