Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/layout/generic/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 426 kB image not shown  

Quelle  nsGridContainerFrame.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/* rendering object for CSS "display: grid | inline-grid" */

#include "nsGridContainerFrame.h"

#include <functional>
#include <stdlib.h>  // for div()
#include <type_traits>
#include "gfxContext.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Baseline.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/CSSAlignUtils.h"
#include "mozilla/dom/Grid.h"
#include "mozilla/dom/GridBinding.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/Maybe.h"
#include "mozilla/PodOperations.h"  // for PodZero
#include "mozilla/PresShell.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/StaticPrefs_layout.h"
#include "nsAbsoluteContainingBlock.h"
#include "nsCSSFrameConstructor.h"
#include "nsDisplayList.h"
#include "nsFieldSetFrame.h"
#include "nsHTMLButtonControlFrame.h"
#include "nsHashKeys.h"
#include "nsIFrameInlines.h"  // for nsIFrame::GetLogicalNormalPosition (don't remove)
#include "nsLayoutUtils.h"
#include "nsPlaceholderFrame.h"
#include "nsPresContext.h"
#include "nsReadableUtils.h"
#include "nsTableWrapperFrame.h"

using namespace mozilla;

using AbsPosReflowFlags = nsAbsoluteContainingBlock::AbsPosReflowFlags;
using AlignJustifyFlags = CSSAlignUtils::AlignJustifyFlags;
using GridItemCachedBAxisMeasurement =
    nsGridContainerFrame::CachedBAxisMeasurement;
using GridTemplate = StyleGridTemplateComponent;
using NameList = StyleOwnedSlice<StyleCustomIdent>;
using SizingConstraint = nsGridContainerFrame::SizingConstraint;
using TrackListValue =
    StyleGenericTrackListValue<LengthPercentage, StyleInteger>;
using TrackRepeat = StyleGenericTrackRepeat<LengthPercentage, StyleInteger>;
using TrackSize = nsGridContainerFrame::TrackSize;

static mozilla::LazyLogModule gGridContainerLog("GridContainer");
#define GRID_LOG(...) \
  MOZ_LOG(gGridContainerLog, LogLevel::Debug, (__VA_ARGS__));

static const int32_t kMaxLine = StyleMAX_GRID_LINE;
static const int32_t kMinLine = StyleMIN_GRID_LINE;
// The maximum line number, in the zero-based translated grid.
static const uint32_t kTranslatedMaxLine = uint32_t(kMaxLine - kMinLine);
static const uint32_t kAutoLine = kTranslatedMaxLine + 3457U;

static const nsFrameState kIsSubgridBits =
    (NS_STATE_GRID_IS_COL_SUBGRID | NS_STATE_GRID_IS_ROW_SUBGRID);

namespace mozilla {

template <>
inline Span<const StyleOwnedSlice<StyleCustomIdent>>
GridTemplate::LineNameLists(bool aIsSubgrid) const {
  if (IsTrackList()) {
    return AsTrackList()->line_names.AsSpan();
  }
  if (IsSubgrid() && aIsSubgrid) {
    // For subgrid, we need to resolve <line-name-list> from each
    // StyleGenericLineNameListValue, so return empty.
    return {};
  }
  MOZ_ASSERT(IsNone() || IsMasonry() || (IsSubgrid() && !aIsSubgrid));
  return {};
}

template <>
inline const StyleTrackBreadth& StyleTrackSize::GetMax() const {
  if (IsBreadth()) {
    return AsBreadth();
  }
  if (IsMinmax()) {
    return AsMinmax()._1;
  }
  MOZ_ASSERT(IsFitContent());
  return AsFitContent();
}

template <>
inline const StyleTrackBreadth& StyleTrackSize::GetMin() const {
  static const StyleTrackBreadth kAuto = StyleTrackBreadth::Auto();
  if (IsBreadth()) {
    // <flex> behaves like minmax(auto, <flex>)
    return AsBreadth().IsFr() ? kAuto : AsBreadth();
  }
  if (IsMinmax()) {
    return AsMinmax()._0;
  }
  MOZ_ASSERT(IsFitContent());
  return kAuto;
}

}  // namespace mozilla

static nscoord ClampToCSSMaxBSize(nscoord aSize,
                                  const ReflowInput* aReflowInput) {
  auto maxSize = aReflowInput->ComputedMaxBSize();
  if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
    MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
    aSize = std::min(aSize, maxSize);
  }
  return aSize;
}

// Same as above and set aStatus INCOMPLETE if aSize wasn't clamped.
// (If we clamp aSize it means our size is less than the break point,
// i.e. we're effectively breaking in our overflow, so we should leave
// aStatus as is (it will likely be set to OVERFLOW_INCOMPLETE later)).
static nscoord ClampToCSSMaxBSize(nscoord aSize,
                                  const ReflowInput* aReflowInput,
                                  nsReflowStatus* aStatus) {
  auto maxSize = aReflowInput->ComputedMaxBSize();
  if (MOZ_UNLIKELY(maxSize != NS_UNCONSTRAINEDSIZE)) {
    MOZ_ASSERT(aReflowInput->ComputedMinBSize() <= maxSize);
    if (aSize < maxSize) {
      aStatus->SetIncomplete();
    } else {
      aSize = maxSize;
    }
  } else {
    aStatus->SetIncomplete();
  }
  return aSize;
}

template <typename Size>
static bool IsPercentOfIndefiniteSize(const Size& aCoord,
                                      nscoord aPercentBasis) {
  return aPercentBasis == NS_UNCONSTRAINEDSIZE && aCoord.HasPercent();
}

static nscoord ResolveToDefiniteSize(const StyleTrackBreadth& aBreadth,
                                     nscoord aPercentBasis) {
  MOZ_ASSERT(aBreadth.IsBreadth());
  if (::IsPercentOfIndefiniteSize(aBreadth.AsBreadth(), aPercentBasis)) {
    return nscoord(0);
  }
  return std::max(nscoord(0), aBreadth.AsBreadth().Resolve(aPercentBasis));
}

// Synthesize a baseline from a border box.  For an alphabetical baseline
// this is the end edge of the border box.  For a central baseline it's
// the center of the border box.
// https://drafts.csswg.org/css-align-3/#synthesize-baseline
// For a 'first baseline' the measure is from the border-box start edge and
// for a 'last baseline' the measure is from the border-box end edge.
//
// The 'LogicalAxis aAxis' represents the axis (in terms of aWM) that the
// baseline corresponds to.  (Typically, baselines are a measurement in the
// block axis; e.g. for English horizontal-tb text, a traditional baseline
// would be a y-axis measurement.  But in some cases (e.g. orthogonal WMs), we
// may need to synthesize a baseline in a child's inline axis, which is when
// this function might receive an aAxis of LogicalAxis::Inline. In that case, we
// assume that the writing mode's preference for central vs. alphabetic
// baselines is irrelevant, since that's a choice about its block-axis
// baselines, and we just unconditionally use the alphabetic baseline
// (e.g. border-box bottom edge).
static nscoord SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup,
                                               WritingMode aWM,
                                               LogicalAxis aAxis,
                                               nscoord aBorderBoxSize) {
  const bool useAlphabeticBaseline =
      (aAxis == LogicalAxis::Inline) ? true : aWM.IsAlphabeticalBaseline();

  if (aGroup == BaselineSharingGroup::First) {
    return useAlphabeticBaseline ? aBorderBoxSize : aBorderBoxSize / 2;
  }
  MOZ_ASSERT(aGroup == BaselineSharingGroup::Last);
  // Round up for central baseline offset, to be consistent with eFirst.
  return useAlphabeticBaseline ? 0
                               : (aBorderBoxSize / 2) + (aBorderBoxSize % 2);
}

// The helper struct to hold the box sizing adjustment.
struct BoxSizingAdjustment {
  BoxSizingAdjustment() = delete;
  BoxSizingAdjustment(const WritingMode aWM, const ComputedStyle& aStyle)
      : mWM(aWM), mStyle(aStyle) {}

  const LogicalSize& EnsureAndGet() {
    if (mValue) {
      return mValue.ref();
    }

    if (mStyle.StylePosition()->mBoxSizing != StyleBoxSizing::Border) {
      // Use default, (0, 0).
      mValue.emplace(mWM);
      return mValue.ref();
    }

    const auto& padding = mStyle.StylePadding()->mPadding;
    LogicalMargin border(mWM, mStyle.StyleBorder()->GetComputedBorder());
    // We can use zero percentage basis since this is only called from
    // intrinsic sizing code.
    const nscoord percentageBasis = 0;
    const nscoord iBP =
        std::max(padding.GetIStart(mWM).Resolve(percentageBasis), 0) +
        std::max(padding.GetIEnd(mWM).Resolve(percentageBasis), 0) +
        border.IStartEnd(mWM);
    const nscoord bBP =
        std::max(padding.GetBStart(mWM).Resolve(percentageBasis), 0) +
        std::max(padding.GetBEnd(mWM).Resolve(percentageBasis), 0) +
        border.BStartEnd(mWM);
    mValue.emplace(mWM, iBP, bBP);
    return mValue.ref();
  }

 private:
  const WritingMode mWM;
  const ComputedStyle& mStyle;
  // The wrapped value we would like to use for the box sizing adjustment.
  Maybe<LogicalSize> mValue;
};

static Maybe<nscoord> GetPercentageBasisForAR(
    const LogicalAxis aRatioDeterminingAxis, const WritingMode aWM,
    const Maybe<LogicalSize>& aContainingBlockSize) {
  if (!aContainingBlockSize) {
    return Nothing();
  }

  const nscoord basis = aContainingBlockSize->Size(aRatioDeterminingAxis, aWM);
  // If the basis is unconstrained (because we are still computing the
  // containing block size), we should treat it as no basis.
  return basis == NS_UNCONSTRAINEDSIZE ? Nothing() : Some(basis);
}

template <typename Type>
static Maybe<nscoord> ComputeTransferredSize(
    const Type& aRatioDeterminingSize, const LogicalAxis aAxis,
    const WritingMode aWM, const AspectRatio& aAspectRatio,
    BoxSizingAdjustment& aBoxSizingAdjustment,
    const Maybe<LogicalSize>& aContainingBlockSize) {
  // Use GetOrthogonalAxis() to get the ratio-determining axis.
  const Maybe<nscoord> basis = GetPercentageBasisForAR(
      GetOrthogonalAxis(aAxis), aWM, aContainingBlockSize);
  nscoord rdSize = 0;
  if (aRatioDeterminingSize.ConvertsToLength()) {
    rdSize = aRatioDeterminingSize.ToLength();
  } else if (aRatioDeterminingSize.HasPercent() && basis) {
    rdSize = aRatioDeterminingSize.AsLengthPercentage().Resolve(*basis);
  } else {
    // Either we are not using LengthPercentage or there is no percentage basis.
    return Nothing();
  }
  return Some(aAspectRatio.ComputeRatioDependentSize(
      aAxis, aWM, rdSize, aBoxSizingAdjustment.EnsureAndGet()));
}

// The input sizes for calculating the number of repeat(auto-fill/fit) tracks.
// https://drafts.csswg.org/css-grid-2/#auto-repeat
struct RepeatTrackSizingInput {
  explicit RepeatTrackSizingInput(WritingMode aWM)
      : mMin(aWM, 0, 0),
        mSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
        mMax(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) {}

  RepeatTrackSizingInput(const LogicalSize& aMin, const LogicalSize& aSize,
                         const LogicalSize& aMax)
      : mMin(aMin), mSize(aSize), mMax(aMax) {}

  // This should be used in intrinsic sizing (i.e. when we can't initialize
  // the sizes directly from ReflowInput values).
  void InitFromStyle(LogicalAxis aAxis, WritingMode aWM,
                     const ComputedStyle* aStyle,
                     const AspectRatio& aAspectRatio,
                     const Maybe<LogicalSize>& aContainingBlockSize) {
    const auto& pos = aStyle->StylePosition();
    BoxSizingAdjustment boxSizingAdjustment(aWM, *aStyle);
    const nscoord cbSizeInAxis = aContainingBlockSize
                                     ? aContainingBlockSize->Size(aAxis, aWM)
                                     : NS_UNCONSTRAINEDSIZE;

    auto adjustForBoxSizing = [aWM, aAxis,
                               &boxSizingAdjustment](nscoord aSize) {
      return std::max(
          aSize - boxSizingAdjustment.EnsureAndGet().Size(aAxis, aWM), 0);
    };

    nscoord& min = mMin.Size(aAxis, aWM);
    const auto& styleMinSize = pos->MinSize(aAxis, aWM);
    if (styleMinSize.ConvertsToLength()) {
      min = adjustForBoxSizing(styleMinSize.ToLength());
    } else if (styleMinSize.HasPercent() &&
               cbSizeInAxis != NS_UNCONSTRAINEDSIZE) {
      min = adjustForBoxSizing(
          styleMinSize.AsLengthPercentage().Resolve(cbSizeInAxis));
    } else if (aAspectRatio && styleMinSize.BehavesLikeInitialValue(aAxis)) {
      // Use GetOrthogonalAxis() to get the ratio-determining axis. Same for max
      // and size below in this function.
      const auto& styleRDMinSize = pos->MinSize(GetOrthogonalAxis(aAxis), aWM);
      if (Maybe<nscoord> resolvedMinSize = ComputeTransferredSize(
              styleRDMinSize, aAxis, aWM, aAspectRatio, boxSizingAdjustment,
              aContainingBlockSize)) {
        min = *resolvedMinSize;
      }
    }

    nscoord& max = mMax.Size(aAxis, aWM);
    const auto& styleMaxSize = pos->MaxSize(aAxis, aWM);
    if (styleMaxSize.ConvertsToLength()) {
      max = std::max(min, adjustForBoxSizing(styleMaxSize.ToLength()));
    } else if (styleMaxSize.HasPercent() &&
               cbSizeInAxis != NS_UNCONSTRAINEDSIZE) {
      max = std::max(
          min, adjustForBoxSizing(
                   styleMaxSize.AsLengthPercentage().Resolve(cbSizeInAxis)));
    } else if (aAspectRatio && styleMaxSize.BehavesLikeInitialValue(aAxis)) {
      const auto& styleRDMaxSize = pos->MaxSize(GetOrthogonalAxis(aAxis), aWM);
      if (Maybe<nscoord> resolvedMaxSize = ComputeTransferredSize(
              styleRDMaxSize, aAxis, aWM, aAspectRatio, boxSizingAdjustment,
              aContainingBlockSize)) {
        max = std::max(min, *resolvedMaxSize);
      }
    }

    nscoord& size = mSize.Size(aAxis, aWM);
    // When computing the intrinsic inline size, disregard the explicit
    // inline-size property as it should not affect the final result.
    const auto& styleSize =
        aAxis == LogicalAxis::Inline ? StyleSize::Auto() : pos->BSize(aWM);
    if (styleSize.ConvertsToLength()) {
      size = std::clamp(adjustForBoxSizing(styleSize.ToLength()), min, max);
    } else if (styleSize.HasPercent() && cbSizeInAxis != NS_UNCONSTRAINEDSIZE) {
      size =
          std::clamp(adjustForBoxSizing(
                         styleSize.AsLengthPercentage().Resolve(cbSizeInAxis)),
                     min, max);
    } else if (aAspectRatio && styleSize.BehavesLikeInitialValue(aAxis)) {
      const auto& styleRDSize = pos->Size(GetOrthogonalAxis(aAxis), aWM);
      if (Maybe<nscoord> resolvedSize = ComputeTransferredSize(
              styleRDSize, aAxis, aWM, aAspectRatio, boxSizingAdjustment,
              aContainingBlockSize)) {
        size = std::clamp(*resolvedSize, min, max);
      }
    }
  }

  LogicalSize mMin;
  LogicalSize mSize;
  LogicalSize mMax;
};

enum class GridLineSide {
  BeforeGridGap,
  AfterGridGap,
};

struct nsGridContainerFrame::TrackSize {
  enum StateBits : uint16_t {
    // clang-format off
    eAutoMinSizing =              0x1,
    eMinContentMinSizing =        0x2,
    eMaxContentMinSizing =        0x4,
    eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing,
    eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing,
    eModified =                   0x8,
    eAutoMaxSizing =             0x10,
    eMinContentMaxSizing =       0x20,
    eMaxContentMaxSizing =       0x40,
    eAutoOrMaxContentMaxSizing = eAutoMaxSizing | eMaxContentMaxSizing,
    eIntrinsicMaxSizing = eAutoOrMaxContentMaxSizing | eMinContentMaxSizing,
    eFlexMaxSizing =             0x80,
    eFrozen =                   0x100,
    eSkipGrowUnlimited1 =       0x200,
    eSkipGrowUnlimited2 =       0x400,
    eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2,
    eBreakBefore =              0x800,
    eApplyFitContentClamping = 0x1000,
    eInfinitelyGrowable =      0x2000,

    // These are only used in the masonry axis.  They share the same value
    // as *MinSizing above, but that's OK because we don't use those in
    // the masonry axis.
    //
    // This track corresponds to an item margin-box size that is stretching.
    eItemStretchSize =            0x1,
    // This bit says that we should clamp that size to mLimit.
    eClampToLimit =               0x2,
    // This bit says that the corresponding item has `auto` margin(s).
    eItemHasAutoMargin =          0x4,
    // clang-format on
  };

  StateBits Initialize(nscoord aPercentageBasis, const StyleTrackSize&);
  bool IsFrozen() const { return mState & eFrozen; }
#ifdef DEBUG
  static void DumpStateBits(StateBits aState);
  void Dump() const;
#endif

  static bool IsDefiniteMaxSizing(StateBits aStateBits) {
    return (aStateBits & (eIntrinsicMaxSizing | eFlexMaxSizing)) == 0;
  }

  // Base size of this track.
  // https://drafts.csswg.org/css-grid-2/#base-size
  nscoord mBase;

  // Growth limit of this track.
  // https://drafts.csswg.org/css-grid-2/#growth-limit
  nscoord mLimit;

  nscoord mPosition;  // zero until we apply 'align/justify-content'
  // mBaselineSubtreeSize is the size of a baseline-aligned subtree within
  // this track.  One subtree per baseline-sharing group (per track).
  PerBaseline<nscoord> mBaselineSubtreeSize;
  StateBits mState;
};

MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits)

static_assert(
    std::is_trivially_copyable<nsGridContainerFrame::TrackSize>::value,
    "Must be trivially copyable");
static_assert(
    std::is_trivially_destructible<nsGridContainerFrame::TrackSize>::value,
    "Must be trivially destructible");

TrackSize::StateBits nsGridContainerFrame::TrackSize::Initialize(
    nscoord aPercentageBasis, const StyleTrackSize& aSize) {
  using Tag = StyleTrackBreadth::Tag;

  MOZ_ASSERT(mBase == 0 && mLimit == 0 && mState == 0,
             "track size data is expected to be initialized to zero");
  mBaselineSubtreeSize[BaselineSharingGroup::First] = nscoord(0);
  mBaselineSubtreeSize[BaselineSharingGroup::Last] = nscoord(0);

  auto& min = aSize.GetMin();
  auto& max = aSize.GetMax();

  Tag minSizeTag = min.tag;
  Tag maxSizeTag = max.tag;
  if (aSize.IsFitContent()) {
    // In layout, fit-content(size) behaves as minmax(auto, max-content), with
    // 'size' as an additional upper-bound.
    if (!::IsPercentOfIndefiniteSize(aSize.AsFitContent(), aPercentageBasis)) {
      mState = eApplyFitContentClamping;
    }
    minSizeTag = Tag::Auto;
    maxSizeTag = Tag::MaxContent;
  }
  if (::IsPercentOfIndefiniteSize(min, aPercentageBasis)) {
    // https://drafts.csswg.org/css-grid-2/#valdef-grid-template-columns-length-percentage-0
    // "If the inline or block size of the grid container is indefinite,
    //  <percentage> values relative to that size are treated as 'auto'."
    minSizeTag = Tag::Auto;
  }
  if (::IsPercentOfIndefiniteSize(max, aPercentageBasis)) {
    maxSizeTag = Tag::Auto;
  }

  // https://drafts.csswg.org/css-grid-2/#algo-init
  switch (minSizeTag) {
    case Tag::Auto:
      mState |= eAutoMinSizing;
      break;
    case Tag::MinContent:
      mState |= eMinContentMinSizing;
      break;
    case Tag::MaxContent:
      mState |= eMaxContentMinSizing;
      break;
    default:
      MOZ_ASSERT(!min.IsFr(), " min-sizing is invalid as a track size");
      mBase = ::ResolveToDefiniteSize(min, aPercentageBasis);
  }
  switch (maxSizeTag) {
    case Tag::Auto:
      mState |= eAutoMaxSizing;
      mLimit = NS_UNCONSTRAINEDSIZE;
      break;
    case Tag::MinContent:
    case Tag::MaxContent:
      mState |= maxSizeTag == Tag::MinContent ? eMinContentMaxSizing
                                              : eMaxContentMaxSizing;
      mLimit = NS_UNCONSTRAINEDSIZE;
      break;
    case Tag::Fr:
      mState |= eFlexMaxSizing;
      mLimit = NS_UNCONSTRAINEDSIZE;
      break;
    default:
      mLimit = ::ResolveToDefiniteSize(max, aPercentageBasis);
      if (mLimit < mBase) {
        mLimit = mBase;
      }
  }
  return mState;
}

/**
 * A LineRange can be definite or auto - when it's definite it represents
 * a consecutive set of tracks between a starting line and an ending line.
 * Before it's definite it can also represent an auto position with a span,
 * where mStart == kAutoLine and mEnd is the (non-zero positive) span.
 * For normal-flow items, the invariant mStart < mEnd holds when both
 * lines are definite.
 *
 * For abs.pos. grid items, mStart and mEnd may both be kAutoLine, meaning
 * "attach this side to the grid container containing block edge".
 * Additionally, mStart <= mEnd holds when both are definite (non-kAutoLine),
 * i.e. the invariant is slightly relaxed compared to normal flow items.
 */

struct nsGridContainerFrame::LineRange {
  LineRange(int32_t aStart, int32_t aEnd)
      : mUntranslatedStart(aStart), mUntranslatedEnd(aEnd) {
#ifdef DEBUG
    if (!IsAutoAuto()) {
      if (IsAuto()) {
        MOZ_ASSERT(aEnd >= kMinLine && aEnd <= kMaxLine, "invalid span");
      } else {
        MOZ_ASSERT(aStart >= kMinLine && aStart <= kMaxLine,
                   "invalid start line");
        MOZ_ASSERT(aEnd == int32_t(kAutoLine) ||
                       (aEnd >= kMinLine && aEnd <= kMaxLine),
                   "invalid end line");
      }
    }
#endif
  }
  bool IsAutoAuto() const { return mStart == kAutoLine && mEnd == kAutoLine; }
  bool IsAuto() const { return mStart == kAutoLine; }
  bool IsDefinite() const { return mStart != kAutoLine; }
  uint32_t Extent() const {
    MOZ_ASSERT(mEnd != kAutoLine, "Extent is undefined for abs.pos. 'auto'");
    if (IsAuto()) {
      MOZ_ASSERT(mEnd >= 1 && mEnd < uint32_t(kMaxLine), "invalid span");
      return mEnd;
    }
    return mEnd - mStart;
  }

  /**
   * Return an object suitable for iterating this range.
   */

  auto Range() const { return IntegerRange<uint32_t>(mStart, mEnd); }

  /**
   * Resolve this auto range to start at aStart, making it definite.
   * @param aClampMaxLine the maximum allowed line number (zero-based)
   * Precondition: this range IsAuto()
   */

  void ResolveAutoPosition(uint32_t aStart, uint32_t aClampMaxLine) {
    MOZ_ASSERT(IsAuto(), "Why call me?");
    mStart = aStart;
    mEnd += aStart;
    // Clamp to aClampMaxLine, which is where kMaxLine is in the explicit
    // grid in a non-subgrid axis; this implements clamping per
    // https://drafts.csswg.org/css-grid-2/#overlarge-grids
    // In a subgrid axis it's the end of the grid in that axis.
    if (MOZ_UNLIKELY(mStart >= aClampMaxLine)) {
      mEnd = aClampMaxLine;
      mStart = mEnd - 1;
    } else if (MOZ_UNLIKELY(mEnd > aClampMaxLine)) {
      mEnd = aClampMaxLine;
    }
  }
  /**
   * Translate the lines to account for (empty) removed tracks.  This method
   * is only for grid items and should only be called after placement.
   * aNumRemovedTracks contains a count for each line in the grid how many
   * tracks were removed between the start of the grid and that line.
   */

  void AdjustForRemovedTracks(const nsTArray<uint32_t>& aNumRemovedTracks) {
    MOZ_ASSERT(mStart != kAutoLine, "invalid resolved line for a grid item");
    MOZ_ASSERT(mEnd != kAutoLine, "invalid resolved line for a grid item");
    uint32_t numRemovedTracks = aNumRemovedTracks[mStart];
    MOZ_ASSERT(numRemovedTracks == aNumRemovedTracks[mEnd],
               "tracks that a grid item spans can't be removed");
    mStart -= numRemovedTracks;
    mEnd -= numRemovedTracks;
  }
  /**
   * Translate the lines to account for (empty) removed tracks.  This method
   * is only for abs.pos. children and should only be called after placement.
   * Same as for in-flow items, but we don't touch 'auto' lines here and we
   * also need to adjust areas that span into the removed tracks.
   */

  void AdjustAbsPosForRemovedTracks(
      const nsTArray<uint32_t>& aNumRemovedTracks) {
    if (mStart != kAutoLine) {
      mStart -= aNumRemovedTracks[mStart];
    }
    if (mEnd != kAutoLine) {
      MOZ_ASSERT(mStart == kAutoLine || mEnd > mStart, "invalid line range");
      mEnd -= aNumRemovedTracks[mEnd];
    }
  }
  /**
   * Return the contribution of this line range for step 2 in
   * https://drafts.csswg.org/css-grid-2/#auto-placement-algo
   */

  uint32_t HypotheticalEnd() const { return mEnd; }
  /**
   * Given an array of track sizes, return the starting position and length
   * of the tracks in this line range.
   */

  void ToPositionAndLength(const nsTArray<TrackSize>& aTrackSizes,
                           nscoord* aPos, nscoord* aLength) const;
  /**
   * Given an array of track sizes, return the length of the tracks in this
   * line range.
   */

  nscoord ToLength(const nsTArray<TrackSize>& aTrackSizes) const;
  /**
   * Given an array of track sizes and a grid origin coordinate, adjust the
   * abs.pos. containing block along an axis given by aPos and aLength.
   * aPos and aLength should already be initialized to the grid container
   * containing block for this axis before calling this method.
   */

  void ToPositionAndLengthForAbsPos(const Tracks& aTracks, nscoord aGridOrigin,
                                    nscoord* aPos, nscoord* aLength) const;

  void Translate(int32_t aOffset) {
    MOZ_ASSERT(IsDefinite());
    mStart += aOffset;
    mEnd += aOffset;
  }

  /** Swap the start/end sides of this range. */
  void ReverseDirection(uint32_t aGridEnd) {
    MOZ_ASSERT(IsDefinite());
    MOZ_ASSERT(aGridEnd >= mEnd);
    uint32_t newStart = aGridEnd - mEnd;
    mEnd = aGridEnd - mStart;
    mStart = newStart;
  }

  /**
   * @note We'll use the signed member while resolving definite positions
   * to line numbers (1-based), which may become negative for implicit lines
   * to the top/left of the explicit grid.  PlaceGridItems() then translates
   * the whole grid to a 0,0 origin and we'll use the unsigned member from
   * there on.
   */

  union {
    uint32_t mStart;
    int32_t mUntranslatedStart;
  };
  union {
    uint32_t mEnd;
    int32_t mUntranslatedEnd;
  };

 protected:
  LineRange() : mStart(0), mEnd(0) {}
};

/**
 * Helper class to construct a LineRange from translated lines.
 * The ctor only accepts translated definite line numbers.
 */

struct nsGridContainerFrame::TranslatedLineRange : public LineRange {
  TranslatedLineRange(uint32_t aStart, uint32_t aEnd) {
    MOZ_ASSERT(aStart < aEnd && aEnd <= kTranslatedMaxLine);
    mStart = aStart;
    mEnd = aEnd;
  }
};

/**
 * A GridArea is the area in the grid for a grid item.
 * The area is represented by two LineRanges, both of which can be auto
 * (@see LineRange) in intermediate steps while the item is being placed.
 * @see PlaceGridItems
 */

struct nsGridContainerFrame::GridArea {
  GridArea(const LineRange& aCols, const LineRange& aRows)
      : mCols(aCols), mRows(aRows) {}
  bool IsDefinite() const { return mCols.IsDefinite() && mRows.IsDefinite(); }
  LineRange& LineRangeForAxis(LogicalAxis aAxis) {
    return aAxis == LogicalAxis::Inline ? mCols : mRows;
  }
  const LineRange& LineRangeForAxis(LogicalAxis aAxis) const {
    return aAxis == LogicalAxis::Inline ? mCols : mRows;
  }
  LineRange mCols;
  LineRange mRows;
};

struct nsGridContainerFrame::GridItemInfo {
  /**
   * Item state per axis.
   */

  enum StateBits : uint16_t {
    // Does the item span a flex track?
    eIsFlexing = 0x1,

    // First or last baseline alignment preference. They are mutually exclusive.
    // This does *NOT* represent the baseline alignment group. See the member
    // variable for that.
    // <https://drafts.csswg.org/css-align-3/#baseline-alignment-preference>
    eFirstBaseline = 0x2,
    eLastBaseline = 0x4,
    eIsBaselineAligned = eFirstBaseline | eLastBaseline,

    // One of e[Self|Content]Baseline is set when eIsBaselineAligned is true
    eSelfBaseline = 0x8,  // is it *-self:[last ]baseline alignment?
    // Ditto *-content:[last ]baseline. Mutually exclusive w. eSelfBaseline.
    eContentBaseline = 0x10,

    // The baseline affects the margin or padding on the item's end side when
    // this bit is set.  In a grid-axis it's always set for eLastBaseline and
    // always unset for eFirstBaseline.  In a masonry-axis, it's set for
    // baseline groups in the EndStretch set and unset for the StartStretch set.
    eEndSideBaseline = 0x20,
    eAllBaselineBits = eIsBaselineAligned | eSelfBaseline | eContentBaseline |
                       eEndSideBaseline,

    // Should apply Automatic Minimum Size per:
    // https://drafts.csswg.org/css-grid-2/#min-size-auto
    eApplyAutoMinSize = 0x40,
    // Clamp per https://drafts.csswg.org/css-grid-2/#min-size-auto
    eClampMarginBoxMinSize = 0x80,
    eIsSubgrid = 0x100,
    // set on subgrids and items in subgrids if they are adjacent to the grid
    // start/end edge (excluding grid-aligned abs.pos. frames)
    eStartEdge = 0x200,
    eEndEdge = 0x400,
    eEdgeBits = eStartEdge | eEndEdge,
    // Set if this item was auto-placed in this axis.
    eAutoPlacement = 0x800,
    // Set if this item is the last item in its track (masonry layout only)
    eIsLastItemInMasonryTrack = 0x1000,
  };

  GridItemInfo(nsIFrame* aFrame, const GridArea& aArea);

  GridItemInfo(const GridItemInfo& aOther)
      : mFrame(aOther.mFrame), mArea(aOther.mArea) {
    mBaselineOffset = aOther.mBaselineOffset;
    mState = aOther.mState;
  }

  GridItemInfo& operator=(const GridItemInfo&) = delete;

  static bool BaselineAlignmentAffectsEndSide(StateBits state) {
    return state & StateBits::eEndSideBaseline;
  }

  /**
   * Inhibit subgrid layout unless the item is placed in the first "track" in
   * a parent masonry-axis, or has definite placement or spans all tracks in
   * the parent grid-axis.
   * TODO: this is stricter than what the Masonry proposal currently states
   *       (bug 1627581)
   */

  void MaybeInhibitSubgridInMasonry(nsGridContainerFrame* aParent,
                                    uint32_t aGridAxisTrackCount);

  /**
   * Inhibit subgridding in aAxis for this item.
   */

  void InhibitSubgrid(nsGridContainerFrame* aParent, LogicalAxis aAxis);

  /**
   * Return a copy of this item with its row/column data swapped.
   */

  GridItemInfo Transpose() const {
    GridItemInfo info(mFrame, GridArea(mArea.mRows, mArea.mCols));
    info.mState[LogicalAxis::Block] = mState[LogicalAxis::Inline];
    info.mState[LogicalAxis::Inline] = mState[LogicalAxis::Block];
    info.mBaselineOffset[LogicalAxis::Block] =
        mBaselineOffset[LogicalAxis::Inline];
    info.mBaselineOffset[LogicalAxis::Inline] =
        mBaselineOffset[LogicalAxis::Block];
    return info;
  }

  /** Swap the start/end sides in aAxis. */
  inline void ReverseDirection(LogicalAxis aAxis, uint32_t aGridEnd);

  // Is this item a subgrid in the given container axis?
  bool IsSubgrid(LogicalAxis aAxis) const {
    return mState[aAxis] & StateBits::eIsSubgrid;
  }

  // Is this item a subgrid in either axis?
  bool IsSubgrid() const {
    return IsSubgrid(LogicalAxis::Inline) || IsSubgrid(LogicalAxis::Block);
  }

  // Return the (inner) grid container frame associated with this subgrid item.
  nsGridContainerFrame* SubgridFrame() const {
    MOZ_ASSERT(IsSubgrid());
    nsGridContainerFrame* gridFrame = GetGridContainerFrame(mFrame);
    MOZ_ASSERT(gridFrame && gridFrame->IsSubgrid());
    return gridFrame;
  }

  /**
   * Adjust our grid areas to account for removed auto-fit tracks in aAxis.
   */

  void AdjustForRemovedTracks(LogicalAxis aAxis,
                              const nsTArray<uint32_t>& aNumRemovedTracks);

  /**
   * If the item is [align|justify]-self:[last ]baseline aligned in the given
   * axis then set aBaselineOffset to the baseline offset and return aAlign.
   * Otherwise, return a fallback alignment.
   */

  StyleAlignFlags GetSelfBaseline(StyleAlignFlags aAlign, LogicalAxis aAxis,
                                  nscoord* aBaselineOffset) const {
    MOZ_ASSERT(aAlign == StyleAlignFlags::BASELINE ||
               aAlign == StyleAlignFlags::LAST_BASELINE);
    if (!(mState[aAxis] & eSelfBaseline)) {
      return aAlign == StyleAlignFlags::BASELINE ? StyleAlignFlags::SELF_START
                                                 : StyleAlignFlags::SELF_END;
    }
    *aBaselineOffset = mBaselineOffset[aAxis];
    return aAlign;
  }

  // Return true if we should use MinContribution on items that do not span
  // any flex tracks to determine the minimum contribution, and if we should
  // set the eApplyAutoMinSize flag on grid items.
  //
  // In part this is determined by whether or not the minimum contribution
  // of the item is content-based.
  // https://drafts.csswg.org/css-grid-2/#min-size-auto
  //
  // @note the caller should also check that the item spans at least one track
  // that has a min track sizing function that is 'auto' before applying it.
  bool ShouldApplyAutoMinSize(WritingMode aContainerWM,
                              LogicalAxis aContainerAxis) const {
    if ((mState[aContainerAxis] & StateBits::eIsFlexing) &&
        mArea.LineRangeForAxis(aContainerAxis).Extent() > 1) {
      // If the item spans multiple tracks in a given axis, none of those
      // tracks may be flexible.
      return false;
    }
    const LogicalAxis itemAxis =
        aContainerWM.IsOrthogonalTo(mFrame->GetWritingMode())
            ? GetOrthogonalAxis(aContainerAxis)
            : aContainerAxis;
    const auto* pos =
        mFrame->IsTableWrapperFrame()
            ? mFrame->PrincipalChildList().FirstChild()->StylePosition()
            : mFrame->StylePosition();
    const auto& size = pos->Size(aContainerAxis, aContainerWM);
    // max-content and min-content should behave as initial value in block axis.
    // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
    // for block size dimension on sizing properties (e.g. height), so we
    // treat it as `auto`.
    bool isAuto = size.BehavesLikeInitialValue(itemAxis);
    // TODO alaskanemily: This probably shouldn't be a special case.
    // Although this being a percentage isn't relevant to whether or not the
    // minimum contribution is content-based or not, but this matches the
    // expectations of MinContribution().
    if (!isAuto && !size.HasPercent()) {
      return false;
    }
    const auto& minSize = pos->MinSize(aContainerAxis, aContainerWM);
    // max-content and min-content should behave as initial value in block axis.
    // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
    // for block size dimension on sizing properties (e.g. height), so we
    // treat it as `auto`.
    isAuto = minSize.BehavesLikeInitialValue(itemAxis);
    return isAuto && !mFrame->StyleDisplay()->IsScrollableOverflow();
  }

#ifdef DEBUG
  void Dump() const;
#endif

  static bool IsStartRowLessThan(const GridItemInfo* a, const GridItemInfo* b) {
    return a->mArea.mRows.mStart < b->mArea.mRows.mStart;
  }

  // Sorting functions for 'masonry-auto-flow:next'.  We sort the items that
  // were placed into the first track by the Grid placement algorithm first
  // (to honor that placement).  All other items will be placed by the Masonry
  // layout algorithm (their Grid placement in the masonry axis is irrelevant).
  static bool RowMasonryOrdered(const GridItemInfo* a, const GridItemInfo* b) {
    return a->mArea.mRows.mStart == 0 && b->mArea.mRows.mStart != 0 &&
           !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
  }
  static bool ColMasonryOrdered(const GridItemInfo* a, const GridItemInfo* b) {
    return a->mArea.mCols.mStart == 0 && b->mArea.mCols.mStart != 0 &&
           !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
  }

  // Sorting functions for 'masonry-auto-flow:definite-first'.  Similar to
  // the above, but here we also sort items with a definite item placement in
  // the grid axis in track order before 'auto'-placed items. We also sort all
  // continuations first since they use the same placement as their
  // first-in-flow (we treat them as "definite" regardless of eAutoPlacement).
  static bool RowMasonryDefiniteFirst(const GridItemInfo* a,
                                      const GridItemInfo* b) {
    bool isContinuationA = a->mFrame->GetPrevInFlow();
    bool isContinuationB = b->mFrame->GetPrevInFlow();
    if (isContinuationA != isContinuationB) {
      return isContinuationA;
    }
    auto masonryA = a->mArea.mRows.mStart;
    auto gridA = a->mState[LogicalAxis::Inline] & StateBits::eAutoPlacement;
    auto masonryB = b->mArea.mRows.mStart;
    auto gridB = b->mState[LogicalAxis::Inline] & StateBits::eAutoPlacement;
    return (masonryA == 0 ? masonryB != 0 : (masonryB != 0 && gridA < gridB)) &&
           !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
  }
  static bool ColMasonryDefiniteFirst(const GridItemInfo* a,
                                      const GridItemInfo* b) {
    MOZ_ASSERT(!a->mFrame->GetPrevInFlow() && !b->mFrame->GetPrevInFlow(),
               "fragmentation not supported in inline axis");
    auto masonryA = a->mArea.mCols.mStart;
    auto gridA = a->mState[LogicalAxis::Block] & StateBits::eAutoPlacement;
    auto masonryB = b->mArea.mCols.mStart;
    auto gridB = b->mState[LogicalAxis::Block] & StateBits::eAutoPlacement;
    return (masonryA == 0 ? masonryB != 0 : (masonryB != 0 && gridA < gridB)) &&
           !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
  }

  // Return true if this items block size is dependent on the size of the
  // container it is in.
  bool IsBSizeDependentOnContainerSize(WritingMode aContainerWM) const {
    const auto IsDependentOnContainerSize = [](const auto& size) -> bool {
      // XXXdholbert The BehavesLikeStretchOnInlineAxis usage seems like
      // maybe it should be considering block-axis instead?
      return size.HasPercent() || size.BehavesLikeStretchOnInlineAxis();
    };

    const nsStylePosition* stylePos = mFrame->StylePosition();
    bool isItemAutoSize =
        IsDependentOnContainerSize(stylePos->BSize(aContainerWM)) ||
        IsDependentOnContainerSize(stylePos->MinBSize(aContainerWM)) ||
        IsDependentOnContainerSize(stylePos->MaxBSize(aContainerWM));

    return isItemAutoSize;
  }

  nsIFrame* const mFrame;
  GridArea mArea;

  // Offset from the margin edge to the baseline (LogicalAxis index).  It's from
  // the start edge for first baseline sharing group, otherwise from the end
  // edge.
  // It's mutable since we update the value fairly late (just before reflowing
  // the item).
  mutable PerLogicalAxis<nscoord> mBaselineOffset;

  // State bits per axis.
  mutable PerLogicalAxis<StateBits> mState;
};

using GridItemInfo = nsGridContainerFrame::GridItemInfo;
using ItemState = GridItemInfo::StateBits;
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ItemState)

GridItemInfo::GridItemInfo(nsIFrame* aFrame, const GridArea& aArea)
    : mFrame(aFrame), mArea(aArea), mBaselineOffset{0, 0} {
  mState[LogicalAxis::Block] =
      StateBits(mArea.mRows.mStart == kAutoLine ? eAutoPlacement : 0);
  mState[LogicalAxis::Inline] =
      StateBits(mArea.mCols.mStart == kAutoLine ? eAutoPlacement : 0);

  if (auto* gridFrame = GetGridContainerFrame(mFrame)) {
    auto parentWM = aFrame->GetParent()->GetWritingMode();
    bool isOrthogonal = parentWM.IsOrthogonalTo(gridFrame->GetWritingMode());
    if (gridFrame->IsColSubgrid()) {
      mState[isOrthogonal ? LogicalAxis::Block : LogicalAxis::Inline] |=
          StateBits::eIsSubgrid;
    }
    if (gridFrame->IsRowSubgrid()) {
      mState[isOrthogonal ? LogicalAxis::Inline : LogicalAxis::Block] |=
          StateBits::eIsSubgrid;
    }
  }
}

void GridItemInfo::ReverseDirection(LogicalAxis aAxis, uint32_t aGridEnd) {
  mArea.LineRangeForAxis(aAxis).ReverseDirection(aGridEnd);
  ItemState& state = mState[aAxis];
  ItemState newState = state & ~ItemState::eEdgeBits;
  if (state & ItemState::eStartEdge) {
    newState |= ItemState::eEndEdge;
  }
  if (state & ItemState::eEndEdge) {
    newState |= ItemState::eStartEdge;
  }
  state = newState;
}

void GridItemInfo::InhibitSubgrid(nsGridContainerFrame* aParent,
                                  LogicalAxis aAxis) {
  MOZ_ASSERT(IsSubgrid(aAxis));
  auto bit = NS_STATE_GRID_IS_COL_SUBGRID;
  if (aParent->GetWritingMode().IsOrthogonalTo(mFrame->GetWritingMode()) !=
      (aAxis == LogicalAxis::Block)) {
    bit = NS_STATE_GRID_IS_ROW_SUBGRID;
  }
  MOZ_ASSERT(SubgridFrame()->HasAnyStateBits(bit));
  SubgridFrame()->RemoveStateBits(bit);
  mState[aAxis] &= StateBits(~StateBits::eIsSubgrid);
}

void GridItemInfo::MaybeInhibitSubgridInMasonry(nsGridContainerFrame* aParent,
                                                uint32_t aGridAxisTrackCount) {
  if (IsSubgrid(LogicalAxis::Inline) &&
      aParent->IsMasonry(LogicalAxis::Block) && mArea.mRows.mStart != 0 &&
      mArea.mCols.Extent() != aGridAxisTrackCount &&
      (mState[LogicalAxis::Inline] & eAutoPlacement)) {
    InhibitSubgrid(aParent, LogicalAxis::Inline);
    return;
  }
  if (IsSubgrid(LogicalAxis::Block) &&
      aParent->IsMasonry(LogicalAxis::Inline) && mArea.mCols.mStart != 0 &&
      mArea.mRows.Extent() != aGridAxisTrackCount &&
      (mState[LogicalAxis::Block] & eAutoPlacement)) {
    InhibitSubgrid(aParent, LogicalAxis::Block);
  }
}

// Each subgrid stores this data about its items etc on a frame property.
struct nsGridContainerFrame::Subgrid {
  Subgrid(const GridArea& aArea, bool aIsOrthogonal, WritingMode aCBWM)
      : mArea(aArea),
        mGridColEnd(0),
        mGridRowEnd(0),
        mMarginBorderPadding(aCBWM),
        mIsOrthogonal(aIsOrthogonal) {}

  // Return the relevant line range for the subgrid column axis.
  const LineRange& SubgridCols() const {
    return mIsOrthogonal ? mArea.mRows : mArea.mCols;
  }
  // Return the relevant line range for the subgrid row axis.
  const LineRange& SubgridRows() const {
    return mIsOrthogonal ? mArea.mCols : mArea.mRows;
  }

  // The subgrid's items.
  nsTArray<GridItemInfo> mGridItems;
  // The subgrid's abs.pos. items.
  nsTArray<GridItemInfo> mAbsPosItems;
  // The subgrid's area as a grid item, i.e. in its parent's grid space.
  GridArea mArea;
  // The (inner) grid size for the subgrid, zero-based.
  uint32_t mGridColEnd;
  uint32_t mGridRowEnd;
  // The margin+border+padding for the subgrid box in its parent grid's WM.
  // (This also includes the size of any scrollbars.)
  LogicalMargin mMarginBorderPadding;
  // Does the subgrid frame have orthogonal writing-mode to its parent grid
  // container?
  bool mIsOrthogonal;

  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, Subgrid)
};
using Subgrid = nsGridContainerFrame::Subgrid;

void GridItemInfo::AdjustForRemovedTracks(
    LogicalAxis aAxis, const nsTArray<uint32_t>& aNumRemovedTracks) {
  const bool abspos = mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
  auto& lines = mArea.LineRangeForAxis(aAxis);
  if (abspos) {
    lines.AdjustAbsPosForRemovedTracks(aNumRemovedTracks);
  } else {
    lines.AdjustForRemovedTracks(aNumRemovedTracks);
  }
  if (IsSubgrid()) {
    auto* subgrid = SubgridFrame()->GetProperty(Subgrid::Prop());
    if (subgrid) {
      auto& lines = subgrid->mArea.LineRangeForAxis(aAxis);
      if (abspos) {
        lines.AdjustAbsPosForRemovedTracks(aNumRemovedTracks);
      } else {
        lines.AdjustForRemovedTracks(aNumRemovedTracks);
      }
    }
  }
}

/**
 * Track size data for use by subgrids (which don't do sizing of their own
 * in a subgridded axis).  A non-subgrid container stores its resolved sizes,
 * but only if it has any subgrid children.  A subgrid always stores one.
 * In a subgridded axis, we copy the parent's sizes (see CopyUsedTrackSizes).
 *
 * This struct us stored on a frame property, which may be null before the track
 * sizing step for the given container.  A null property is semantically
 * equivalent to mCanResolveLineRangeSize being false in both axes.
 * @note the axis used to access this data is in the grid container's own
 * writing-mode, same as in other track-sizing functions.
 */

struct nsGridContainerFrame::UsedTrackSizes {
  UsedTrackSizes() : mCanResolveLineRangeSize{falsefalse} {}

  /**
   * Setup mSizes by copying track sizes from aFrame's grid container
   * parent when aAxis is subgridded (and recurse if the parent is a subgrid
   * that doesn't have sizes yet), or by running the Track Sizing Algo when
   * the axis is not subgridded (for a subgrid).
   * Set mCanResolveLineRangeSize[aAxis] to true once we have obtained
   * sizes for an axis (if it's already true then this method is a NOP).
   */

  void ResolveTrackSizesForAxis(nsGridContainerFrame* aFrame, LogicalAxis aAxis,
                                gfxContext& aRC);

  /** Helper function for the above method */
  void ResolveSubgridTrackSizesForAxis(nsGridContainerFrame* aFrame,
                                       LogicalAxis aAxis, Subgrid* aSubgrid,
                                       gfxContext& aRC,
                                       nscoord aContentBoxSize);

  // This only has valid sizes when mCanResolveLineRangeSize is true in
  // the same axis.  It may have zero tracks (a grid with only abs.pos.
  // subgrids/items may have zero tracks).
  PerLogicalAxis<nsTArray<TrackSize>> mSizes;
  // True if mSizes can be used to resolve line range sizes in an axis.
  PerLogicalAxis<bool> mCanResolveLineRangeSize;

  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, UsedTrackSizes)
};
using UsedTrackSizes = nsGridContainerFrame::UsedTrackSizes;

#ifdef DEBUG
void nsGridContainerFrame::GridItemInfo::Dump() const {
  auto Dump1 = [this](const char* aMsg, LogicalAxis aAxis) {
    auto state = mState[aAxis];
    if (!state) {
      return;
    }
    printf("%s", aMsg);
    if (state & ItemState::eEdgeBits) {
      printf("subgrid-adjacent-edges(");
      if (state & ItemState::eStartEdge) {
        printf("start ");
      }
      if (state & ItemState::eEndEdge) {
        printf("end");
      }
      printf(") ");
    }
    if (state & ItemState::eAutoPlacement) {
      printf("masonry-auto ");
    }
    if (state & ItemState::eIsSubgrid) {
      printf("subgrid ");
    }
    if (state & ItemState::eIsFlexing) {
      printf("flexing ");
    }
    if (state & ItemState::eApplyAutoMinSize) {
      printf("auto-min-size ");
    }
    if (state & ItemState::eClampMarginBoxMinSize) {
      printf("clamp ");
    }
    if (state & ItemState::eIsLastItemInMasonryTrack) {
      printf("last-in-track ");
    }
    if (state & ItemState::eFirstBaseline) {
      printf("first baseline %s-alignment ",
             (state & ItemState::eSelfBaseline) ? "self" : "content");
    }
    if (state & ItemState::eLastBaseline) {
      printf("last baseline %s-alignment ",
             (state & ItemState::eSelfBaseline) ? "self" : "content");
    }
    if (state & ItemState::eIsBaselineAligned) {
      printf("%.2fpx", NSAppUnitsToFloatPixels(mBaselineOffset[aAxis],
                                               AppUnitsPerCSSPixel()));
    }
    printf("\n");
  };
  printf("grid-row: %d %d\n", mArea.mRows.mStart, mArea.mRows.mEnd);
  Dump1(" grid block-axis: ", LogicalAxis::Block);
  printf("grid-column: %d %d\n", mArea.mCols.mStart, mArea.mCols.mEnd);
  Dump1(" grid inline-axis: ", LogicalAxis::Inline);
}
#endif

/**
 * Encapsulates CSS track-sizing functions.
 */

struct nsGridContainerFrame::TrackSizingFunctions {
 private:
  TrackSizingFunctions(const GridTemplate& aTemplate,
                       const StyleImplicitGridTracks& aAutoSizing,
                       const Maybe<size_t>& aRepeatAutoIndex, bool aIsSubgrid)
      : mTemplate(aTemplate),
        mTrackListValues(aTemplate.TrackListValues()),
        mAutoSizing(aAutoSizing),
        mExplicitGridOffset(0),
        mRepeatAutoStart(aRepeatAutoIndex.valueOr(0)),
        mRepeatAutoEnd(mRepeatAutoStart),
        mHasRepeatAuto(aRepeatAutoIndex.isSome()) {
    MOZ_ASSERT(!mHasRepeatAuto || !aIsSubgrid,
               "a track-list for a subgrid can't have an track");
    if (!aIsSubgrid) {
      ExpandNonRepeatAutoTracks();
    }

#ifdef DEBUG
    if (mHasRepeatAuto) {
      MOZ_ASSERT(mExpandedTracks.Length() >= 1);
      const unsigned maxTrack = kMaxLine - 1;
      // If the exanded tracks are out of range of the maximum track, we
      // can't compare the repeat-auto start. It will be removed later during
      // grid item placement in that situation.
      if (mExpandedTracks.Length() < maxTrack) {
        MOZ_ASSERT(mRepeatAutoStart < mExpandedTracks.Length());
      }
    }
#endif
  }

 public:
  TrackSizingFunctions(const GridTemplate& aGridTemplate,
                       const StyleImplicitGridTracks& aAutoSizing,
                       bool aIsSubgrid)
      : TrackSizingFunctions(aGridTemplate, aAutoSizing,
                             aGridTemplate.RepeatAutoIndex(), aIsSubgrid) {}

 private:
  enum { ForSubgridFallbackTag };
  TrackSizingFunctions(const GridTemplate& aGridTemplate,
                       const StyleImplicitGridTracks& aAutoSizing,
                       decltype(ForSubgridFallbackTag))
      : TrackSizingFunctions(aGridTemplate, aAutoSizing, Nothing(),
                             /* aIsSubgrid */ true) {}

 public:
  /**
   * This is used in a subgridded axis to resolve sizes before its parent's
   * sizes are known for intrinsic sizing purposes.  It copies the slice of
   * the nearest non-subgridded axis' track sizing functions spanned by
   * the subgrid.
   *
   * FIXME: this was written before there was a spec... the spec now says:
   * "If calculating the layout of a grid item in this step depends on
   *  the available space in the block axis, assume the available space
   *  that it would have if any row with a definite max track sizing
   *  function had that size and all other rows were infinite."
   * https://drafts.csswg.org/css-grid-2/#subgrid-sizing
   */

  static TrackSizingFunctions ForSubgridFallback(
      nsGridContainerFrame* aSubgridFrame, const Subgrid* aSubgrid,
      nsGridContainerFrame* aParentGridContainer, LogicalAxis aParentAxis) {
    MOZ_ASSERT(aSubgrid);
    MOZ_ASSERT(aSubgridFrame->IsSubgrid(aSubgrid->mIsOrthogonal
                                            ? GetOrthogonalAxis(aParentAxis)
                                            : aParentAxis));
    nsGridContainerFrame* parent = aParentGridContainer;
    auto parentAxis = aParentAxis;
    LineRange range = aSubgrid->mArea.LineRangeForAxis(parentAxis);
    // Find our nearest non-subgridded axis and use its track sizing functions.
    while (parent->IsSubgrid(parentAxis)) {
      const auto* parentSubgrid = parent->GetProperty(Subgrid::Prop());
      auto* grandParent = parent->ParentGridContainerForSubgrid();
      auto grandParentWM = grandParent->GetWritingMode();
      bool isSameDirInAxis =
          parent->GetWritingMode().ParallelAxisStartsOnSameSide(parentAxis,
                                                                grandParentWM);
      if (MOZ_UNLIKELY(!isSameDirInAxis)) {
        auto end = parentAxis == LogicalAxis::Block
                       ? parentSubgrid->mGridRowEnd
                       : parentSubgrid->mGridColEnd;
        range.ReverseDirection(end);
        // range is now in the same direction as the grand-parent's axis
      }
      auto grandParentAxis = parentSubgrid->mIsOrthogonal
                                 ? GetOrthogonalAxis(parentAxis)
                                 : parentAxis;
      const auto& parentRange =
          parentSubgrid->mArea.LineRangeForAxis(grandParentAxis);
      range.Translate(parentRange.mStart);
      // range is now in the grand-parent's coordinates
      parentAxis = grandParentAxis;
      parent = grandParent;
    }
    const auto* pos = parent->StylePosition();
    const auto isInlineAxis = parentAxis == LogicalAxis::Inline;
    const auto& szf =
        isInlineAxis ? pos->mGridTemplateRows : pos->mGridTemplateColumns;
    const auto& autoSizing =
        isInlineAxis ? pos->mGridAutoColumns : pos->mGridAutoRows;
    return TrackSizingFunctions(szf, autoSizing, ForSubgridFallbackTag);
  }

  /**
   * Initialize the number of auto-fill/fit tracks to use.
   * This can be zero if no auto-fill/fit track was specified, or if the repeat
   * begins after the maximum allowed track.
   */

  void InitRepeatTracks(const NonNegativeLengthPercentageOrNormal& aGridGap,
                        nscoord aMinSize, nscoord aSize, nscoord aMaxSize) {
    const uint32_t maxTrack = kMaxLine - 1;
    // Check for a repeat after the maximum allowed track.
    if (MOZ_UNLIKELY(mRepeatAutoStart >= maxTrack)) {
      mHasRepeatAuto = false;
      mRepeatAutoStart = 0;
      mRepeatAutoEnd = 0;
      return;
    }
    uint32_t repeatTracks =
        CalculateRepeatFillCount(aGridGap, aMinSize, aSize, aMaxSize) *
        NumRepeatTracks();
    // Clamp the number of repeat tracks to the maximum possible track.
    repeatTracks = std::min(repeatTracks, maxTrack - mRepeatAutoStart);
    SetNumRepeatTracks(repeatTracks);
    // Blank out the removed flags for each of these tracks.
    mRemovedRepeatTracks.SetLength(repeatTracks);
    for (auto& track : mRemovedRepeatTracks) {
      track = false;
    }
  }

  uint32_t CalculateRepeatFillCount(
      const NonNegativeLengthPercentageOrNormal& aGridGap, nscoord aMinSize,
      nscoord aSize, nscoord aMaxSize) const {
    if (!mHasRepeatAuto) {
      return 0;
    }
    // At this point no tracks will have been collapsed, so the RepeatEndDelta
    // should not be negative.
    MOZ_ASSERT(RepeatEndDelta() >= 0);
    // Note that this uses NumRepeatTracks and mRepeatAutoStart/End, although
    // the result of this method is used to change those values to a fully
    // expanded value. Spec quotes are from
    // https://drafts.csswg.org/css-grid-2/#repeat-notation
    const uint32_t numTracks = mExpandedTracks.Length() + RepeatEndDelta();
    MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track");
    if (MOZ_UNLIKELY(numTracks >= kMaxLine)) {
      // The fixed tracks plus an entire repetition is either larger or as
      // large as the maximum track, so we do not need to measure how many
      // repetitions will fit. This also avoids needing to check for if
      // kMaxLine - numTracks would underflow at the end where we clamp the
      // result.
      return 1;
    }
    nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize;
    if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) {
      // "Otherwise, the specified track list repeats only once."
      return 1;
    }
    nscoord repeatTrackSum = 0;
    // Note that one repeat() track size is included in |sum| in this loop.
    nscoord sum = 0;
    const nscoord percentBasis = aSize;
    for (uint32_t i = 0; i < numTracks; ++i) {
      // "treating each track as its max track sizing function if that is
      // definite or as its minimum track sizing function otherwise"
      // https://drafts.csswg.org/css-grid-2/#valdef-repeat-auto-fill
      nscoord trackSize;
      {
        const auto& sizingFunction = SizingFor(i);
        const auto& maxCoord = sizingFunction.GetMax();
        const auto& minCoord = sizingFunction.GetMin();
        if (maxCoord.IsBreadth() && minCoord.IsBreadth()) {
          // If the max is less than the min, then the max will be floored by
          // the min (essentially yielding minmax(min, min))
          // https://drafts.csswg.org/css-grid-2/#funcdef-grid-template-columns-minmax
          const nscoord minSize =
              ::ResolveToDefiniteSize(minCoord, percentBasis);
          const nscoord maxSize =
              ::ResolveToDefiniteSize(maxCoord, percentBasis);
          trackSize = std::max(maxSize, minSize);
        } else {
          const auto* coord = &maxCoord;
          if (!coord->IsBreadth()) {
            coord = &minCoord;
            if (!coord->IsBreadth()) {
              return 1;
            }
          }
          trackSize = ::ResolveToDefiniteSize(*coord, percentBasis);
        }
      }

      if (i >= mRepeatAutoStart && i < mRepeatAutoEnd) {
        // Use a minimum 1px for the repeat() track-size.
        if (trackSize < AppUnitsPerCSSPixel()) {
          trackSize = AppUnitsPerCSSPixel();
        }
        repeatTrackSum += trackSize;
      }
      sum += trackSize;
    }
    nscoord gridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aSize);
    if (numTracks > 1) {
      // Add grid-gaps for all the tracks including the repeat() track.
      sum += gridGap * (numTracks - 1);
    }
    // Calculate the max number of tracks that fits without overflow.
    nscoord available = maxFill != NS_UNCONSTRAINEDSIZE ? maxFill : aMinSize;
    nscoord spaceToFill = available - sum;
    if (spaceToFill <= 0) {
      // "if any number of repetitions would overflow, then 1 repetition"
      return 1;
    }
    // Calculate the max number of tracks that fits without overflow.
    // Since we already have one repetition in sum, we can simply add one grid
    // gap for each element in the repeat.
    div_t q = div(spaceToFill, repeatTrackSum + gridGap * NumRepeatTracks());
    // The +1 here is for the one repeat track we already accounted for above.
    uint32_t numRepeatTracks = q.quot + 1;
    if (q.rem != 0 && maxFill == NS_UNCONSTRAINEDSIZE) {
      // "Otherwise, if the grid container has a definite min size in
      // the relevant axis, the number of repetitions is the largest possible
      // positive integer that fulfills that minimum requirement."
      ++numRepeatTracks;  // one more to ensure the grid is at least min-size
    }
    // Clamp the number of repeat tracks so that the last line <= kMaxLine.
    // (note that |numTracks| already includes one repeat() track)
    MOZ_ASSERT(numTracks >= NumRepeatTracks());
    const uint32_t maxRepeatTrackCount = kMaxLine - numTracks;
    const uint32_t maxRepetitions = maxRepeatTrackCount / NumRepeatTracks();
    return std::min(numRepeatTracks, maxRepetitions);
  }

  /**
   * Compute the explicit grid end line number (in a zero-based grid).
   * @param aGridTemplateAreasEnd 'grid-template-areas' end line in this axis
   */

  uint32_t ComputeExplicitGridEnd(uint32_t aGridTemplateAreasEnd) {
    uint32_t end = NumExplicitTracks() + 1;
    end = std::max(end, aGridTemplateAreasEnd);
    end = std::min(end, uint32_t(kMaxLine));
    return end;
  }
  const StyleTrackSize& SizingFor(uint32_t aTrackIndex) const {
    static const StyleTrackSize kAutoTrackSize =
        StyleTrackSize::Breadth(StyleTrackBreadth::Auto());
    // |aIndex| is the relative index to mAutoSizing. A negative value means it
    // is the last Nth element.
    auto getImplicitSize = [this](int32_t aIndex) -> const StyleTrackSize& {
      MOZ_ASSERT(!(mAutoSizing.Length() == 1 &&
                   mAutoSizing.AsSpan()[0] == kAutoTrackSize),
                 "It's impossible to have one track with auto value because we "
                 "filter out this case during parsing");

      if (mAutoSizing.IsEmpty()) {
        return kAutoTrackSize;
      }

      // If multiple track sizes are given, the pattern is repeated as necessary
      // to find the size of the implicit tracks.
      int32_t i = aIndex % int32_t(mAutoSizing.Length());
      if (i < 0) {
        i += mAutoSizing.Length();
      }
      return mAutoSizing.AsSpan()[i];
    };

    if (MOZ_UNLIKELY(aTrackIndex < mExplicitGridOffset)) {
      // The last implicit grid track before the explicit grid receives the
      // last specified size, and so on backwards. Therefore we pass the
      // negative relative index to imply that we should get the implicit size
      // from the last Nth specified grid auto size.
      return getImplicitSize(int32_t(aTrackIndex) -
                             int32_t(mExplicitGridOffset));
    }
    uint32_t index = aTrackIndex - mExplicitGridOffset;
    MOZ_ASSERT(mRepeatAutoStart <= mRepeatAutoEnd);

    if (index >= mRepeatAutoStart) {
      if (index < mRepeatAutoEnd) {
        // Expand the repeat tracks.
        const auto& indices = mExpandedTracks[mRepeatAutoStart];
        const TrackListValue& value = mTrackListValues[indices.first];

        // We expect the default to be used for all track repeats.
        MOZ_ASSERT(indices.second == 0);

        const auto& repeatTracks = value.AsTrackRepeat().track_sizes.AsSpan();

        // Find the repeat track to use, skipping over any collapsed tracks.
        const uint32_t finalRepeatIndex = (index - mRepeatAutoStart);
        uint32_t repeatWithCollapsed = 0;
        // NOTE: We need SizingFor before the final collapsed tracks are known.
        // We know that it's invalid to have empty mRemovedRepeatTracks when
        // there are any repeat tracks, so we can detect that situation here.
        if (mRemovedRepeatTracks.IsEmpty()) {
          repeatWithCollapsed = finalRepeatIndex;
        } else {
          // Count up through the repeat tracks, until we have seen
          // finalRepeatIndex number of non-collapsed tracks.
          for (uint32_t repeatNoCollapsed = 0;
               repeatNoCollapsed < finalRepeatIndex; repeatWithCollapsed++) {
            if (!mRemovedRepeatTracks[repeatWithCollapsed]) {
              repeatNoCollapsed++;
            }
          }
          // If we stopped iterating on a collapsed track, continue to the next
          // non-collapsed track.
          while (mRemovedRepeatTracks[repeatWithCollapsed]) {
            repeatWithCollapsed++;
          }
        }
        return repeatTracks[repeatWithCollapsed % repeatTracks.Length()];
      } else {
        // The index is after the repeat auto range, adjust it to skip over the
        // repeat value. This will have no effect if there is no auto repeat,
        // since then RepeatEndDelta will return zero.
        index -= RepeatEndDelta();
      }
    }
    if (index >= mExpandedTracks.Length()) {
      return getImplicitSize(index - mExpandedTracks.Length());
    }
    auto& indices = mExpandedTracks[index];
    const TrackListValue& value = mTrackListValues[indices.first];
    if (value.IsTrackSize()) {
      MOZ_ASSERT(indices.second == 0);
      return value.AsTrackSize();
    }
    return value.AsTrackRepeat().track_sizes.AsSpan()[indices.second];
  }
  const StyleTrackBreadth& MaxSizingFor(uint32_t aTrackIndex) const {
    return SizingFor(aTrackIndex).GetMax();
  }
  const StyleTrackBreadth& MinSizingFor(uint32_t aTrackIndex) const {
    return SizingFor(aTrackIndex).GetMin();
  }
  uint32_t NumExplicitTracks() const {
    return mExpandedTracks.Length() + RepeatEndDelta();
  }
  uint32_t NumRepeatTracks() const { return mRepeatAutoEnd - mRepeatAutoStart; }
  // The difference between mExplicitGridEnd and mSizingFunctions.Length().
  int32_t RepeatEndDelta() const {
    return mHasRepeatAuto ? int32_t(NumRepeatTracks()) - 1 : 0;
  }
  void SetNumRepeatTracks(uint32_t aNumRepeatTracks) {
    MOZ_ASSERT(mHasRepeatAuto || aNumRepeatTracks == 0);
    mRepeatAutoEnd = mRepeatAutoStart + aNumRepeatTracks;
  }

  // Store mTrackListValues into mExpandedTracks with `repeat(INTEGER, ...)`
  // tracks expanded.
  void ExpandNonRepeatAutoTracks() {
    for (size_t i = 0; i < mTrackListValues.Length(); ++i) {
      auto& value = mTrackListValues[i];
      if (value.IsTrackSize()) {
        mExpandedTracks.EmplaceBack(i, 0);
        continue;
      }
      auto& repeat = value.AsTrackRepeat();
      if (!repeat.count.IsNumber()) {
        MOZ_ASSERT(i == mRepeatAutoStart);
        mRepeatAutoStart = mExpandedTracks.Length();
        mRepeatAutoEnd = mRepeatAutoStart + repeat.track_sizes.Length();
        mExpandedTracks.EmplaceBack(i, 0);
        continue;
      }
      for (auto j : IntegerRange(repeat.count.AsNumber())) {
        Unused << j;
        size_t trackSizesCount = repeat.track_sizes.Length();
        for (auto k : IntegerRange(trackSizesCount)) {
          mExpandedTracks.EmplaceBack(i, k);
        }
      }
    }
    if (MOZ_UNLIKELY(mExpandedTracks.Length() > kMaxLine - 1)) {
      mExpandedTracks.TruncateLength(kMaxLine - 1);
      if (mHasRepeatAuto && mRepeatAutoStart > kMaxLine - 1) {
        // The `repeat(auto-fill/fit)` track is outside the clamped grid.
        mHasRepeatAuto = false;
      }
    }
  }

  // Some style data references, for easy access.
  const GridTemplate& mTemplate;
  const Span<const TrackListValue> mTrackListValues;
  const StyleImplicitGridTracks& mAutoSizing;
  // An array from expanded track sizes (without expanding auto-repeat, which is
  // included just once at `mRepeatAutoStart`).
  //
  // Each entry contains two indices, the first into mTrackListValues, and a
  // second one inside mTrackListValues' repeat value, if any, or zero
  // otherwise.
  nsTArray<std::pair<size_t, size_t>> mExpandedTracks;
  // Offset from the start of the implicit grid to the first explicit track.
  uint32_t mExplicitGridOffset;
  // The index of the repeat(auto-fill/fit) track, or zero if there is none.
  // Relative to mExplicitGridOffset (repeat tracks are explicit by definition).
  uint32_t mRepeatAutoStart;
  // The (hypothetical) index of the last such repeat() track.
  uint32_t mRepeatAutoEnd;
  // True if there is a specified repeat(auto-fill/fit) track.
  bool mHasRepeatAuto;
  // True if this track (relative to mRepeatAutoStart) is a removed auto-fit.
  // Indexed relative to mExplicitGridOffset + mRepeatAutoStart.
  nsTArray<bool> mRemovedRepeatTracks;
};

/**
 * Utility class to find line names.  It provides an interface to lookup line
 * names with a dynamic number of repeat(auto-fill/fit) tracks taken into
 * account.
 */

class MOZ_STACK_CLASS nsGridContainerFrame::LineNameMap {
 public:
  /**
   * Create a LineNameMap.
   * @param aStylePosition the style for the grid container
   * @param aImplicitNamedAreas the implicit areas for the grid container
   * @param aGridTemplate is the grid-template-rows/columns data for this axis
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=85 H=96 G=90

¤ Dauer der Verarbeitung: 0.21 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.