Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/layout/generic/   (Fast Lexical Analyzer Version 2.6©)  Datei vom 10.2.2025 mit Größe 286 kB image not shown  

Quelle  nsFlexContainerFrame.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: flex" */

#include "nsFlexContainerFrame.h"

#include <algorithm>

#include "gfxContext.h"
#include "mozilla/Baseline.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/CSSOrderAwareFrameIterator.h"
#include "mozilla/Logging.h"
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/WritingModes.h"
#include "nsBlockFrame.h"
#include "nsContentUtils.h"
#include "nsDebug.h"
#include "nsDisplayList.h"
#include "nsFieldSetFrame.h"
#include "nsIFrameInlines.h"
#include "nsLayoutUtils.h"
#include "nsPlaceholderFrame.h"
#include "nsPresContext.h"

using namespace mozilla;
using namespace mozilla::layout;

// Convenience typedefs for helper classes that we forward-declare in .h file
// (so that nsFlexContainerFrame methods can use them as parameters):
using FlexItem = nsFlexContainerFrame::FlexItem;
using FlexLine = nsFlexContainerFrame::FlexLine;
using FlexboxAxisTracker = nsFlexContainerFrame::FlexboxAxisTracker;
using StrutInfo = nsFlexContainerFrame::StrutInfo;
using CachedBAxisMeasurement = nsFlexContainerFrame::CachedBAxisMeasurement;
using CachedFlexItemData = nsFlexContainerFrame::CachedFlexItemData;

static mozilla::LazyLogModule gFlexContainerLog("FlexContainer");

// FLEX_LOG is a top-level general log print.
#define FLEX_LOG(message, ...) \
  MOZ_LOG(gFlexContainerLog, LogLevel::Debug, (message, ##__VA_ARGS__));

// FLEX_ITEM_LOG is a top-level log print for flex item.
#define FLEX_ITEM_LOG(item_frame, message, ...) \
  MOZ_LOG(gFlexContainerLog, LogLevel::Debug,   \
          ("Flex item %p: " message, item_frame, ##__VA_ARGS__));

// FLEX_LOGV is a verbose log print with built-in two spaces indentation. The
// convention to use FLEX_LOGV is that FLEX_LOGV statements should generally be
// preceded by one FLEX_LOG or FLEX_ITEM_LOG so that there's no need to repeat
// information presented in the preceding LOG statement. If you want extra level
// of indentation, just add two extra spaces at the start of the message string.
#define FLEX_LOGV(message, ...) \
  MOZ_LOG(gFlexContainerLog, LogLevel::Verbose, (" " message, ##__VA_ARGS__));

static const char* BoolToYesNo(bool aArg) { return aArg ? "yes" : "no"; }

// Returns true if aFlexContainer is a frame for some element that has
// display:-webkit-{inline-}box (or -moz-{inline-}box). aFlexContainer is
// expected to be an instance of nsFlexContainerFrame (enforced with an assert);
// otherwise, this function's state-bit-check here is bogus.
static bool IsLegacyBox(const nsIFrame* aFlexContainer) {
  MOZ_ASSERT(aFlexContainer->IsFlexContainerFrame(),
             "only flex containers may be passed to this function");
  return aFlexContainer->HasAnyStateBits(
      NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX);
}

// Returns the OrderState enum we should pass to CSSOrderAwareFrameIterator
// (depending on whether aFlexContainer has
// NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER state bit).
static CSSOrderAwareFrameIterator::OrderState OrderStateForIter(
    const nsFlexContainerFrame* aFlexContainer) {
  return aFlexContainer->HasAnyStateBits(
             NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
             ? CSSOrderAwareFrameIterator::OrderState::Ordered
             : CSSOrderAwareFrameIterator::OrderState::Unordered;
}

// Returns the OrderingProperty enum that we should pass to
// CSSOrderAwareFrameIterator (depending on whether it's a legacy box).
static CSSOrderAwareFrameIterator::OrderingProperty OrderingPropertyForIter(
    const nsFlexContainerFrame* aFlexContainer) {
  return IsLegacyBox(aFlexContainer)
             ? CSSOrderAwareFrameIterator::OrderingProperty::BoxOrdinalGroup
             : CSSOrderAwareFrameIterator::OrderingProperty::Order;
}

// Returns the "align-items" value that's equivalent to the legacy "box-align"
// value in the given style struct.
static StyleAlignFlags ConvertLegacyStyleToAlignItems(
    const nsStyleXUL* aStyleXUL) {
  // -[moz|webkit]-box-align corresponds to modern "align-items"
  switch (aStyleXUL->mBoxAlign) {
    case StyleBoxAlign::Stretch:
      return StyleAlignFlags::STRETCH;
    case StyleBoxAlign::Start:
      return StyleAlignFlags::FLEX_START;
    case StyleBoxAlign::Center:
      return StyleAlignFlags::CENTER;
    case StyleBoxAlign::Baseline:
      return StyleAlignFlags::BASELINE;
    case StyleBoxAlign::End:
      return StyleAlignFlags::FLEX_END;
  }

  MOZ_ASSERT_UNREACHABLE("Unrecognized mBoxAlign enum value");
  // Fall back to default value of "align-items" property:
  return StyleAlignFlags::STRETCH;
}

// Returns the "justify-content" value that's equivalent to the legacy
// "box-pack" value in the given style struct.
static StyleContentDistribution ConvertLegacyStyleToJustifyContent(
    const nsStyleXUL* aStyleXUL) {
  // -[moz|webkit]-box-pack corresponds to modern "justify-content"
  switch (aStyleXUL->mBoxPack) {
    case StyleBoxPack::Start:
      return {StyleAlignFlags::FLEX_START};
    case StyleBoxPack::Center:
      return {StyleAlignFlags::CENTER};
    case StyleBoxPack::End:
      return {StyleAlignFlags::FLEX_END};
    case StyleBoxPack::Justify:
      return {StyleAlignFlags::SPACE_BETWEEN};
  }

  MOZ_ASSERT_UNREACHABLE("Unrecognized mBoxPack enum value");
  // Fall back to default value of "justify-content" property:
  return {StyleAlignFlags::FLEX_START};
}

// Check if the size is auto or it is a keyword in the block axis.
// |aIsInline| should represent whether aSize is in the inline axis, from the
// perspective of the writing mode of the flex item that the size comes from.
//
// max-content and min-content should behave as property's initial value.
// Bug 567039: We treat -moz-fit-content and -moz-available as property's
// initial value for now.
static inline bool IsAutoOrEnumOnBSize(const StyleSize& aSize, bool aIsInline) {
  return aSize.IsAuto() || (!aIsInline && !aSize.IsLengthPercentage());
}

// Encapsulates our flex container's main & cross axes. This class is backed by
// a FlexboxAxisInfo helper member variable, and it adds some convenience APIs
// on top of what that struct offers.
class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker {
 public:
  explicit FlexboxAxisTracker(const nsFlexContainerFrame* aFlexContainer);

  // Accessors:
  LogicalAxis MainAxis() const {
    return IsRowOriented() ? LogicalAxis::Inline : LogicalAxis::Block;
  }
  LogicalAxis CrossAxis() const {
    return IsRowOriented() ? LogicalAxis::Block : LogicalAxis::Inline;
  }

  LogicalSide MainAxisStartSide() const;
  LogicalSide MainAxisEndSide() const {
    return GetOppositeSide(MainAxisStartSide());
  }

  LogicalSide CrossAxisStartSide() const;
  LogicalSide CrossAxisEndSide() const {
    return GetOppositeSide(CrossAxisStartSide());
  }

  mozilla::Side MainAxisPhysicalStartSide() const {
    return mWM.PhysicalSide(MainAxisStartSide());
  }
  mozilla::Side MainAxisPhysicalEndSide() const {
    return mWM.PhysicalSide(MainAxisEndSide());
  }

  mozilla::Side CrossAxisPhysicalStartSide() const {
    return mWM.PhysicalSide(CrossAxisStartSide());
  }
  mozilla::Side CrossAxisPhysicalEndSide() const {
    return mWM.PhysicalSide(CrossAxisEndSide());
  }

  // Returns the flex container's writing mode.
  WritingMode GetWritingMode() const { return mWM; }

  // Returns true if our main axis is in the reverse direction of our
  // writing mode's corresponding axis. (From 'flex-direction: *-reverse')
  bool IsMainAxisReversed() const { return mAxisInfo.mIsMainAxisReversed; }
  // Returns true if our cross axis is in the reverse direction of our
  // writing mode's corresponding axis. (From 'flex-wrap: *-reverse')
  bool IsCrossAxisReversed() const { return mAxisInfo.mIsCrossAxisReversed; }

  bool IsRowOriented() const { return mAxisInfo.mIsRowOriented; }
  bool IsColumnOriented() const { return !IsRowOriented(); }

  // aSize is expected to match the flex container's WritingMode.
  nscoord MainComponent(const LogicalSize& aSize) const {
    return IsRowOriented() ? aSize.ISize(mWM) : aSize.BSize(mWM);
  }
  int32_t MainComponent(const LayoutDeviceIntSize& aIntSize) const {
    return IsMainAxisHorizontal() ? aIntSize.width : aIntSize.height;
  }

  // aSize is expected to match the flex container's WritingMode.
  nscoord CrossComponent(const LogicalSize& aSize) const {
    return IsRowOriented() ? aSize.BSize(mWM) : aSize.ISize(mWM);
  }
  int32_t CrossComponent(const LayoutDeviceIntSize& aIntSize) const {
    return IsMainAxisHorizontal() ? aIntSize.height : aIntSize.width;
  }

  // NOTE: aMargin is expected to use the flex container's WritingMode.
  nscoord MarginSizeInMainAxis(const LogicalMargin& aMargin) const {
    // If we're row-oriented, our main axis is the inline axis.
    return IsRowOriented() ? aMargin.IStartEnd(mWM) : aMargin.BStartEnd(mWM);
  }
  nscoord MarginSizeInCrossAxis(const LogicalMargin& aMargin) const {
    // If we're row-oriented, our cross axis is the block axis.
    return IsRowOriented() ? aMargin.BStartEnd(mWM) : aMargin.IStartEnd(mWM);
  }

  /**
   * Converts a "flex-relative" point (a main-axis & cross-axis coordinate)
   * into a LogicalPoint, using the flex container's writing mode.
   *
   *  @arg aMainCoord  The main-axis coordinate -- i.e an offset from the
   *                   main-start edge of the flex container's content box.
   *  @arg aCrossCoord The cross-axis coordinate -- i.e an offset from the
   *                   cross-start edge of the flex container's content box.
   *  @arg aContainerMainSize  The main size of flex container's content box.
   *  @arg aContainerCrossSize The cross size of flex container's content box.
   *  @return A LogicalPoint, with the flex container's writing mode, that
   *          represents the same position. The logical coordinates are
   *          relative to the flex container's content box.
   */

  LogicalPoint LogicalPointFromFlexRelativePoint(
      nscoord aMainCoord, nscoord aCrossCoord, nscoord aContainerMainSize,
      nscoord aContainerCrossSize) const {
    nscoord logicalCoordInMainAxis =
        IsMainAxisReversed() ? aContainerMainSize - aMainCoord : aMainCoord;
    nscoord logicalCoordInCrossAxis =
        IsCrossAxisReversed() ? aContainerCrossSize - aCrossCoord : aCrossCoord;

    return IsRowOriented() ? LogicalPoint(mWM, logicalCoordInMainAxis,
                                          logicalCoordInCrossAxis)
                           : LogicalPoint(mWM, logicalCoordInCrossAxis,
                                          logicalCoordInMainAxis);
  }

  /**
   * Converts a "flex-relative" size (a main-axis & cross-axis size)
   * into a LogicalSize, using the flex container's writing mode.
   *
   *  @arg aMainSize  The main-axis size.
   *  @arg aCrossSize The cross-axis size.
   *  @return A LogicalSize, with the flex container's writing mode, that
   *          represents the same size.
   */

  LogicalSize LogicalSizeFromFlexRelativeSizes(nscoord aMainSize,
                                               nscoord aCrossSize) const {
    return IsRowOriented() ? LogicalSize(mWM, aMainSize, aCrossSize)
                           : LogicalSize(mWM, aCrossSize, aMainSize);
  }

  /**
   * Converts a "flex-relative" ascent (the distance from the flex container's
   * content-box cross-start edge to its baseline) into a logical ascent (the
   * distance from the flex container's content-box block-start edge to its
   * baseline).
   */

  nscoord LogicalAscentFromFlexRelativeAscent(
      nscoord aFlexRelativeAscent, nscoord aContentBoxCrossSize) const {
    return (IsCrossAxisReversed() ? aContentBoxCrossSize - aFlexRelativeAscent
                                  : aFlexRelativeAscent);
  }

  bool IsMainAxisHorizontal() const {
    // If we're row-oriented, and our writing mode is NOT vertical,
    // or we're column-oriented and our writing mode IS vertical,
    // then our main axis is horizontal. This handles all cases:
    return IsRowOriented() != mWM.IsVertical();
  }

  // Returns true if this flex item's inline axis in aItemWM is parallel (or
  // antiparallel) to the container's main axis. Returns false, otherwise.
  //
  // Note: this is a helper used before constructing FlexItem. Inside of flex
  // reflow code, FlexItem::IsInlineAxisMainAxis() is equivalent & more optimal.
  bool IsInlineAxisMainAxis(WritingMode aItemWM) const {
    return IsRowOriented() != GetWritingMode().IsOrthogonalTo(aItemWM);
  }

  // Maps justify-*: 'left' or 'right' to 'start' or 'end'.
  StyleAlignFlags ResolveJustifyLeftRight(const StyleAlignFlags& aFlags) const {
    MOZ_ASSERT(
        aFlags == StyleAlignFlags::LEFT || aFlags == StyleAlignFlags::RIGHT,
        "This helper accepts only 'LEFT' or 'RIGHT' flags!");

    const auto wm = GetWritingMode();
    const bool isJustifyLeft = aFlags == StyleAlignFlags::LEFT;
    if (IsColumnOriented()) {
      if (!wm.IsVertical()) {
        // Container's alignment axis (main axis) is *not* parallel to the
        // line-left <-> line-right axis or the physical left <-> physical right
        // axis, so we map both 'left' and 'right' to 'start'.
        return StyleAlignFlags::START;
      }

      MOZ_ASSERT(wm.PhysicalAxis(MainAxis()) == PhysicalAxis::Horizontal,
                 "Vertical column-oriented flex container's main axis should "
                 "be parallel to physical left <-> right axis!");
      // Map 'left' or 'right' to 'start' or 'end', depending on its block flow
      // direction.
      return isJustifyLeft == wm.IsVerticalLR() ? StyleAlignFlags::START
                                                : StyleAlignFlags::END;
    }

    MOZ_ASSERT(MainAxis() == LogicalAxis::Inline,
               "Row-oriented flex container's main axis should be parallel to "
               "line-left <-> line-right axis!");

    // If we get here, we're operating on the flex container's inline axis,
    // so we map 'left' to whichever of 'start' or 'end' corresponds to the
    // *line-relative* left side; and similar for 'right'.
    return isJustifyLeft == wm.IsBidiLTR() ? StyleAlignFlags::START
                                           : StyleAlignFlags::END;
  }

  // Delete copy-constructor & reassignment operator, to prevent accidental
  // (unnecessary) copying.
  FlexboxAxisTracker(const FlexboxAxisTracker&) = delete;
  FlexboxAxisTracker& operator=(const FlexboxAxisTracker&) = delete;

 private:
  const WritingMode mWM;  // The flex container's writing mode.
  const FlexboxAxisInfo mAxisInfo;
};

/**
 * Represents a flex item.
 * Includes the various pieces of input that the Flexbox Layout Algorithm uses
 * to resolve a flexible width.
 */

class nsFlexContainerFrame::FlexItem final {
 public:
  // Normal constructor:
  FlexItem(ReflowInput& aFlexItemReflowInput, float aFlexGrow,
           float aFlexShrink, nscoord aFlexBaseSize, nscoord aMainMinSize,
           nscoord aMainMaxSize, nscoord aTentativeCrossSize,
           nscoord aCrossMinSize, nscoord aCrossMaxSize,
           const FlexboxAxisTracker& aAxisTracker);

  // Simplified constructor, to be used only for generating "struts":
  // (NOTE: This "strut" constructor uses the *container's* writing mode, which
  // we'll use on this FlexItem instead of the child frame's real writing mode.
  // This is fine - it doesn't matter what writing mode we use for a
  // strut, since it won't render any content and we already know its size.)
  FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize, WritingMode aContainerWM,
           const FlexboxAxisTracker& aAxisTracker);

  // Clone existing FlexItem for its underlying frame's continuation.
  // @param aContinuation a continuation in our next-in-flow chain.
  FlexItem CloneFor(nsIFrame* const aContinuation) const {
    MOZ_ASSERT(Frame() == aContinuation->FirstInFlow(),
               "aContinuation should be in aItem's continuation chain!");
    FlexItem item(*this);
    item.mFrame = aContinuation;
    item.mHadMeasuringReflow = false;
    return item;
  }

  // Accessors
  nsIFrame* Frame() const { return mFrame; }
  nscoord FlexBaseSize() const { return mFlexBaseSize; }

  nscoord MainMinSize() const {
    MOZ_ASSERT(!mNeedsMinSizeAutoResolution,
               "Someone's using an unresolved 'auto' main min-size");
    return mMainMinSize;
  }
  nscoord MainMaxSize() const { return mMainMaxSize; }

  // Note: These return the main-axis position and size of our *content box*.
  nscoord MainSize() const { return mMainSize; }
  nscoord MainPosition() const { return mMainPosn; }

  nscoord CrossMinSize() const { return mCrossMinSize; }
  nscoord CrossMaxSize() const { return mCrossMaxSize; }

  // Note: These return the cross-axis position and size of our *content box*.
  nscoord CrossSize() const { return mCrossSize; }
  nscoord CrossPosition() const { return mCrossPosn; }

  // Lazy getter for mAscent or mAscentForLast.
  nscoord ResolvedAscent(bool aUseFirstBaseline) const {
    // XXX We should be using the *container's* writing-mode (mCBWM) here,
    // instead of the item's (mWM). This is essentially bug 1155322.
    nscoord& ascent = aUseFirstBaseline ? mAscent : mAscentForLast;
    if (ascent != ReflowOutput::ASK_FOR_BASELINE) {
      return ascent;
    }

    // Use GetFirstLineBaseline() or GetLastLineBaseline() as appropriate:
    bool found = aUseFirstBaseline
                     ? nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &ascent)
                     : nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &ascent);
    if (found) {
      return ascent;
    }

    // If the nsLayoutUtils getter fails, then ask the frame directly:
    auto baselineGroup = aUseFirstBaseline ? BaselineSharingGroup::First
                                           : BaselineSharingGroup::Last;
    if (auto baseline = mFrame->GetNaturalBaselineBOffset(
            mWM, baselineGroup, BaselineExportContext::Other)) {
      // Offset for last baseline from `GetNaturalBaselineBOffset` originates
      // from the frame's block end, so convert it back.
      ascent = baselineGroup == BaselineSharingGroup::First
                   ? *baseline
                   : mFrame->BSize(mWM) - *baseline;
      return ascent;
    }

    // We couldn't determine a baseline, so we synthesize one from border box:
    ascent = Baseline::SynthesizeBOffsetFromBorderBox(
        mFrame, mWM, BaselineSharingGroup::First);
    return ascent;
  }

  // Convenience methods to compute the main & cross size of our *margin-box*.
  nscoord OuterMainSize() const {
    return mMainSize + MarginBorderPaddingSizeInMainAxis();
  }

  nscoord OuterCrossSize() const {
    return mCrossSize + MarginBorderPaddingSizeInCrossAxis();
  }

  // Convenience method to return the content-box block-size.
  nscoord BSize() const {
    return IsBlockAxisMainAxis() ? MainSize() : CrossSize();
  }

  // Convenience method to return the measured content-box block-size computed
  // in nsFlexContainerFrame::MeasureBSizeForFlexItem().
  Maybe<nscoord> MeasuredBSize() const;

  // Convenience methods to synthesize a style main size or a style cross size
  // with box-size considered, to provide the size overrides when constructing
  // ReflowInput for flex items.
  StyleSize StyleMainSize() const {
    nscoord mainSize = MainSize();
    if (Frame()->StylePosition()->mBoxSizing == StyleBoxSizing::Border) {
      mainSize += BorderPaddingSizeInMainAxis();
    }
    return StyleSize::LengthPercentage(
        LengthPercentage::FromAppUnits(mainSize));
  }

  StyleSize StyleCrossSize() const {
    nscoord crossSize = CrossSize();
    if (Frame()->StylePosition()->mBoxSizing == StyleBoxSizing::Border) {
      crossSize += BorderPaddingSizeInCrossAxis();
    }
    return StyleSize::LengthPercentage(
        LengthPercentage::FromAppUnits(crossSize));
  }

  // Returns the distance between this FlexItem's baseline and the cross-start
  // edge of its margin-box. Used in baseline alignment.
  //
  // (This function needs to be told which physical start side we're measuring
  // the baseline from, so that it can look up the appropriate components from
  // margin.)
  nscoord BaselineOffsetFromOuterCrossEdge(mozilla::Side aStartSide,
                                           bool aUseFirstLineBaseline) const;

  double ShareOfWeightSoFar() const { return mShareOfWeightSoFar; }

  bool IsFrozen() const { return mIsFrozen; }

  bool HadMinViolation() const {
    MOZ_ASSERT(!mIsFrozen, "min violation has no meaning for frozen items.");
    return mHadMinViolation;
  }

  bool HadMaxViolation() const {
    MOZ_ASSERT(!mIsFrozen, "max violation has no meaning for frozen items.");
    return mHadMaxViolation;
  }

  bool WasMinClamped() const {
    MOZ_ASSERT(mIsFrozen, "min clamping has no meaning for unfrozen items.");
    return mHadMinViolation;
  }

  bool WasMaxClamped() const {
    MOZ_ASSERT(mIsFrozen, "max clamping has no meaning for unfrozen items.");
    return mHadMaxViolation;
  }

  // Indicates whether this item received a preliminary "measuring" reflow
  // before its actual reflow.
  bool HadMeasuringReflow() const { return mHadMeasuringReflow; }

  // Indicates whether this item's computed cross-size property is 'auto'.
  bool IsCrossSizeAuto() const;

  // Indicates whether the cross-size property is set to something definite,
  // for the purpose of preferred aspect ratio calculations.
  bool IsCrossSizeDefinite(const ReflowInput& aItemReflowInput) const;

  // Indicates whether this item's cross-size has been stretched (from having
  // "align-self: stretch" with an auto cross-size and no auto margins in the
  // cross axis).
  bool IsStretched() const { return mIsStretched; }

  bool IsFlexBaseSizeContentBSize() const {
    return mIsFlexBaseSizeContentBSize;
  }

  bool IsMainMinSizeContentBSize() const { return mIsMainMinSizeContentBSize; }

  // Indicates whether we need to resolve an 'auto' value for the main-axis
  // min-[width|height] property.
  bool NeedsMinSizeAutoResolution() const {
    return mNeedsMinSizeAutoResolution;
  }

  bool HasAnyAutoMargin() const { return mHasAnyAutoMargin; }

  BaselineSharingGroup ItemBaselineSharingGroup() const {
    MOZ_ASSERT(mAlignSelf == StyleAlignFlags::BASELINE ||
                   mAlignSelf == StyleAlignFlags::LAST_BASELINE,
               "mBaselineSharingGroup only gets a meaningful value "
               "for baseline-aligned items");
    return mBaselineSharingGroup;
  }

  // Indicates whether this item is a "strut" left behind by an element with
  // visibility:collapse.
  bool IsStrut() const { return mIsStrut; }

  // The main axis and cross axis are relative to mCBWM.
  LogicalAxis MainAxis() const { return mMainAxis; }
  LogicalAxis CrossAxis() const { return GetOrthogonalAxis(mMainAxis); }

  // IsInlineAxisMainAxis() returns true if this item's inline axis is parallel
  // (or antiparallel) to the container's main axis. Otherwise (i.e. if this
  // item's inline axis is orthogonal to the container's main axis), this
  // function returns false. The next 3 methods are all other ways of asking
  // the same question, and only exist for readability at callsites (depending
  // on which axes those callsites are reasoning about).
  bool IsInlineAxisMainAxis() const { return mIsInlineAxisMainAxis; }
  bool IsInlineAxisCrossAxis() const { return !mIsInlineAxisMainAxis; }
  bool IsBlockAxisMainAxis() const { return !mIsInlineAxisMainAxis; }
  bool IsBlockAxisCrossAxis() const { return mIsInlineAxisMainAxis; }

  WritingMode GetWritingMode() const { return mWM; }
  WritingMode ContainingBlockWM() const { return mCBWM; }
  StyleAlignFlags AlignSelf() const { return mAlignSelf; }
  StyleAlignFlags AlignSelfFlags() const { return mAlignSelfFlags; }

  // Returns the flex factor (flex-grow or flex-shrink), depending on
  // 'aIsUsingFlexGrow'.
  //
  // Asserts fatally if called on a frozen item (since frozen items are not
  // flexible).
  float GetFlexFactor(bool aIsUsingFlexGrow) {
    MOZ_ASSERT(!IsFrozen(), "shouldn't need flex factor after item is frozen");

    return aIsUsingFlexGrow ? mFlexGrow : mFlexShrink;
  }

  // Returns the weight that we should use in the "resolving flexible lengths"
  // algorithm.  If we're using the flex grow factor, we just return that;
  // otherwise, we return the "scaled flex shrink factor" (scaled by our flex
  // base size, so that when both large and small items are shrinking, the large
  // items shrink more).
  //
  // I'm calling this a "weight" instead of a "[scaled] flex-[grow|shrink]
  // factor", to more clearly distinguish it from the actual flex-grow &
  // flex-shrink factors.
  //
  // Asserts fatally if called on a frozen item (since frozen items are not
  // flexible).
  float GetWeight(bool aIsUsingFlexGrow) {
    MOZ_ASSERT(!IsFrozen(), "shouldn't need weight after item is frozen");

    if (aIsUsingFlexGrow) {
      return mFlexGrow;
    }

    // We're using flex-shrink --> return mFlexShrink * mFlexBaseSize
    if (mFlexBaseSize == 0) {
      // Special-case for mFlexBaseSize == 0 -- we have no room to shrink, so
      // regardless of mFlexShrink, we should just return 0.
      // (This is really a special-case for when mFlexShrink is infinity, to
      // avoid performing mFlexShrink * mFlexBaseSize = inf * 0 = undefined.)
      return 0.0f;
    }
    return mFlexShrink * mFlexBaseSize;
  }

  bool TreatBSizeAsIndefinite() const { return mTreatBSizeAsIndefinite; }

  const AspectRatio& GetAspectRatio() const { return mAspectRatio; }
  bool HasAspectRatio() const { return !!mAspectRatio; }

  // Getters for margin:
  // ===================
  LogicalMargin Margin() const { return mMargin; }
  nsMargin PhysicalMargin() const { return mMargin.GetPhysicalMargin(mCBWM); }

  // Returns the margin component for a given LogicalSide in flex container's
  // writing-mode.
  nscoord GetMarginComponentForSide(LogicalSide aSide) const {
    return mMargin.Side(aSide, mCBWM);
  }

  // Returns the total space occupied by this item's margins in the given axis
  nscoord MarginSizeInMainAxis() const {
    return mMargin.StartEnd(MainAxis(), mCBWM);
  }
  nscoord MarginSizeInCrossAxis() const {
    return mMargin.StartEnd(CrossAxis(), mCBWM);
  }

  // Getters for border/padding
  // ==========================
  // Returns the total space occupied by this item's borders and padding in
  // the given axis
  LogicalMargin BorderPadding() const { return mBorderPadding; }
  nscoord BorderPaddingSizeInMainAxis() const {
    return mBorderPadding.StartEnd(MainAxis(), mCBWM);
  }
  nscoord BorderPaddingSizeInCrossAxis() const {
    return mBorderPadding.StartEnd(CrossAxis(), mCBWM);
  }

  // Getter for combined margin/border/padding
  // =========================================
  // Returns the total space occupied by this item's margins, borders and
  // padding in the given axis
  nscoord MarginBorderPaddingSizeInMainAxis() const {
    return MarginSizeInMainAxis() + BorderPaddingSizeInMainAxis();
  }
  nscoord MarginBorderPaddingSizeInCrossAxis() const {
    return MarginSizeInCrossAxis() + BorderPaddingSizeInCrossAxis();
  }

  // Setters
  // =======
  // Helper to set the resolved value of min-[width|height]:auto for the main
  // axis. (Should only be used if NeedsMinSizeAutoResolution() returns true.)
  void UpdateMainMinSize(nscoord aNewMinSize) {
    NS_ASSERTION(aNewMinSize >= 0,
                 "How did we end up with a negative min-size?");
    MOZ_ASSERT(
        mMainMaxSize == NS_UNCONSTRAINEDSIZE || mMainMaxSize >= aNewMinSize,
        "Should only use this function for resolving min-size:auto, "
        "and main max-size should be an upper-bound for resolved val");
    MOZ_ASSERT(
        mNeedsMinSizeAutoResolution &&
            (mMainMinSize == 0 || mFrame->IsThemed(mFrame->StyleDisplay())),
        "Should only use this function for resolving min-size:auto, "
        "so we shouldn't already have a nonzero min-size established "
        "(unless it's a themed-widget-imposed minimum size)");

    if (aNewMinSize > mMainMinSize) {
      mMainMinSize = aNewMinSize;
      // Also clamp main-size to be >= new min-size:
      mMainSize = std::max(mMainSize, aNewMinSize);
    }
    mNeedsMinSizeAutoResolution = false;
  }

  // This sets our flex base size, and then sets our main size to the
  // resulting "hypothetical main size" (the base size clamped to our
  // main-axis [min,max] sizing constraints).
  void SetFlexBaseSizeAndMainSize(nscoord aNewFlexBaseSize) {
    MOZ_ASSERT(!mIsFrozen || mFlexBaseSize == NS_UNCONSTRAINEDSIZE,
               "flex base size shouldn't change after we're frozen "
               "(unless we're just resolving an intrinsic size)");
    mFlexBaseSize = aNewFlexBaseSize;

    // Before we've resolved flexible lengths, we keep mMainSize set to
    // the 'hypothetical main size', which is the flex base size, clamped
    // to the [min,max] range:
    mMainSize = CSSMinMax(mFlexBaseSize, mMainMinSize, mMainMaxSize);

    FLEX_ITEM_LOG(mFrame, "Set flex base size: %d, hypothetical main size: %d",
                  mFlexBaseSize, mMainSize);
  }

  // Setters used while we're resolving flexible lengths
  // ---------------------------------------------------

  // Sets the main-size of our flex item's content-box.
  void SetMainSize(nscoord aNewMainSize) {
    MOZ_ASSERT(!mIsFrozen, "main size shouldn't change after we're frozen");
    mMainSize = aNewMainSize;
  }

  void SetShareOfWeightSoFar(double aNewShare) {
    MOZ_ASSERT(!mIsFrozen || aNewShare == 0.0,
               "shouldn't be giving this item any share of the weight "
               "after it's frozen");
    mShareOfWeightSoFar = aNewShare;
  }

  void Freeze() {
    mIsFrozen = true;
    // Now that we are frozen, the meaning of mHadMinViolation and
    // mHadMaxViolation changes to indicate min and max clamping. Clear
    // both of the member variables so that they are ready to be set
    // as clamping state later, if necessary.
    mHadMinViolation = false;
    mHadMaxViolation = false;
  }

  void SetHadMinViolation() {
    MOZ_ASSERT(!mIsFrozen,
               "shouldn't be changing main size & having violations "
               "after we're frozen");
    mHadMinViolation = true;
  }
  void SetHadMaxViolation() {
    MOZ_ASSERT(!mIsFrozen,
               "shouldn't be changing main size & having violations "
               "after we're frozen");
    mHadMaxViolation = true;
  }
  void ClearViolationFlags() {
    MOZ_ASSERT(!mIsFrozen,
               "shouldn't be altering violation flags after we're "
               "frozen");
    mHadMinViolation = mHadMaxViolation = false;
  }

  void SetWasMinClamped() {
    MOZ_ASSERT(!mHadMinViolation && !mHadMaxViolation, "only clamp once");
    // This reuses the mHadMinViolation member variable to track clamping
    // events. This is allowable because mHadMinViolation only reflects
    // a violation up until the item is frozen.
    MOZ_ASSERT(mIsFrozen, "shouldn't set clamping state when we are unfrozen");
    mHadMinViolation = true;
  }
  void SetWasMaxClamped() {
    MOZ_ASSERT(!mHadMinViolation && !mHadMaxViolation, "only clamp once");
    // This reuses the mHadMaxViolation member variable to track clamping
    // events. This is allowable because mHadMaxViolation only reflects
    // a violation up until the item is frozen.
    MOZ_ASSERT(mIsFrozen, "shouldn't set clamping state when we are unfrozen");
    mHadMaxViolation = true;
  }

  // Setters for values that are determined after we've resolved our main size
  // -------------------------------------------------------------------------

  // Sets the main-axis position of our flex item's content-box.
  // (This is the distance between the main-start edge of the flex container
  // and the main-start edge of the flex item's content-box.)
  void SetMainPosition(nscoord aPosn) {
    MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
    mMainPosn = aPosn;
  }

  // Sets the cross-size of our flex item's content-box.
  void SetCrossSize(nscoord aCrossSize) {
    MOZ_ASSERT(!mIsStretched,
               "Cross size shouldn't be modified after it's been stretched");
    mCrossSize = aCrossSize;
  }

  // Sets the cross-axis position of our flex item's content-box.
  // (This is the distance between the cross-start edge of the flex container
  // and the cross-start edge of the flex item.)
  void SetCrossPosition(nscoord aPosn) {
    MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
    mCrossPosn = aPosn;
  }

  // After a FlexItem has had a reflow, this method can be used to cache its
  // (possibly-unresolved) ascent, in case it's needed later for
  // baseline-alignment or to establish the container's baseline.
  // (NOTE: This can be marked 'const' even though it's modifying mAscent,
  // because mAscent is mutable. It's nice for this to be 'const', because it
  // means our final reflow can iterate over const FlexItem pointers, and we
  // can be sure it's not modifying those FlexItems, except via this method.)
  void SetAscent(nscoord aAscent) const {
    mAscent = aAscent;  // NOTE: this may be ASK_FOR_BASELINE
  }

  void SetHadMeasuringReflow() { mHadMeasuringReflow = true; }

  void SetIsFlexBaseSizeContentBSize() { mIsFlexBaseSizeContentBSize = true; }

  void SetIsMainMinSizeContentBSize() { mIsMainMinSizeContentBSize = true; }

  // Setter for margin components (for resolving "auto" margins)
  void SetMarginComponentForSide(LogicalSide aSide, nscoord aLength) {
    MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
    mMargin.Side(aSide, mCBWM) = aLength;
  }

  void ResolveStretchedCrossSize(nscoord aLineCrossSize);

  // Resolves flex base size if flex-basis' used value is 'content', using this
  // item's preferred aspect ratio and cross size.
  void ResolveFlexBaseSizeFromAspectRatio(const ReflowInput& aItemReflowInput);

  uint32_t NumAutoMarginsInMainAxis() const {
    return NumAutoMarginsInAxis(MainAxis());
  };

  uint32_t NumAutoMarginsInCrossAxis() const {
    return NumAutoMarginsInAxis(CrossAxis());
  };

  // Once the main size has been resolved, should we bother doing layout to
  // establish the cross size?
  bool CanMainSizeInfluenceCrossSize() const;

  // Returns a main size, clamped by any definite min and max cross size
  // converted through the preferred aspect ratio. The caller is responsible for
  // ensuring that the flex item's preferred aspect ratio is not zero.
  nscoord ClampMainSizeViaCrossAxisConstraints(
      nscoord aMainSize, const ReflowInput& aItemReflowInput) const;

  // Indicates whether we think this flex item needs a "final" reflow
  // (after its final flexed size & final position have been determined).
  //
  // @param aParentReflowInput the flex container's reflow input.
  // @return true if such a reflow is needed, or false if we believe it can
  // simply be moved to its final position and skip the reflow.
  bool NeedsFinalReflow(const ReflowInput& aParentReflowInput) const;

  // Gets the block frame that contains the flex item's content.  This is
  // Frame() itself or one of its descendants.
  nsBlockFrame* BlockFrame() const;

 protected:
  bool IsMinSizeAutoResolutionNeeded() const;

  uint32_t NumAutoMarginsInAxis(LogicalAxis aAxis) const;

  // Values that we already know in constructor, and remain unchanged:
  // The flex item's frame.
  nsIFrame* mFrame = nullptr;
  float mFlexGrow = 0.0f;
  float mFlexShrink = 0.0f;
  AspectRatio mAspectRatio;

  // The flex item's writing mode.
  WritingMode mWM;

  // The flex container's writing mode.
  WritingMode mCBWM;

  // The flex container's main axis in flex container's writing mode.
  LogicalAxis mMainAxis;

  // Stored in flex container's writing mode.
  LogicalMargin mBorderPadding;

  // Stored in flex container's writing mode. Its value can change when we
  // resolve "auto" marigns.
  LogicalMargin mMargin;

  // These are non-const so that we can lazily update them with the item's
  // intrinsic size (obtained via a "measuring" reflow), when necessary.
  // (e.g. for "flex-basis:auto;height:auto" & "min-height:auto")
  nscoord mFlexBaseSize = 0;
  nscoord mMainMinSize = 0;
  nscoord mMainMaxSize = 0;

  // mCrossMinSize and mCrossMaxSize are not changed after constructor.
  nscoord mCrossMinSize = 0;
  nscoord mCrossMaxSize = 0;

  // Values that we compute after constructor:
  nscoord mMainSize = 0;
  nscoord mMainPosn = 0;
  nscoord mCrossSize = 0;
  nscoord mCrossPosn = 0;

  // Mutable b/c it's set & resolved lazily, sometimes via const pointer. See
  // comment above SetAscent().
  // We initialize this to ASK_FOR_BASELINE, and opportunistically fill it in
  // with a real value if we end up reflowing this flex item. (But if we don't
  // reflow this flex item, then this sentinel tells us that we don't know it
  // yet & anyone who cares will need to explicitly request it.)
  //
  // Both mAscent and mAscentForLast are distance from the frame's border-box
  // block-start edge.
  mutable nscoord mAscent = ReflowOutput::ASK_FOR_BASELINE;
  mutable nscoord mAscentForLast = ReflowOutput::ASK_FOR_BASELINE;

  // Temporary state, while we're resolving flexible widths (for our main size)
  // XXXdholbert To save space, we could use a union to make these variables
  // overlay the same memory as some other member vars that aren't touched
  // until after main-size has been resolved. In particular, these could share
  // memory with mMainPosn through mAscent, and mIsStretched.
  double mShareOfWeightSoFar = 0.0;

  bool mIsFrozen = false;
  bool mHadMinViolation = false;
  bool mHadMaxViolation = false;

  // Did this item get a preliminary reflow, to measure its desired height?
  bool mHadMeasuringReflow = false;

  // See IsStretched() documentation.
  bool mIsStretched = false;

  // Is this item a "strut" left behind by an element with visibility:collapse?
  bool mIsStrut = false;

  // See IsInlineAxisMainAxis() documentation. This is not changed after
  // constructor.
  bool mIsInlineAxisMainAxis = true;

  // Does this item need to resolve a min-[width|height]:auto (in main-axis)?
  //
  // Note: mNeedsMinSizeAutoResolution needs to be declared towards the end of
  // the member variables since it's initialized in a method that depends on
  // other members declared above such as mCBWM, mMainAxis, and
  // mIsInlineAxisMainAxis.
  bool mNeedsMinSizeAutoResolution = false;

  // Should we take care to treat this item's resolved BSize as indefinite?
  bool mTreatBSizeAsIndefinite = false;

  // Does this item have an auto margin in either main or cross axis?
  bool mHasAnyAutoMargin = false;

  // Does this item have a content-based flex base size (and is that a size in
  // its block-axis)?
  bool mIsFlexBaseSizeContentBSize = false;

  // Does this item have a content-based resolved auto min size (and is that a
  // size in its block-axis)?
  bool mIsMainMinSizeContentBSize = false;

  // If this item is {first,last}-baseline-aligned using 'align-self', which of
  // its FlexLine's baseline sharing groups does it participate in?
  BaselineSharingGroup mBaselineSharingGroup = BaselineSharingGroup::First;

  // My "align-self" computed value (with "auto" swapped out for parent"s
  // "align-items" value, in our constructor).
  StyleAlignFlags mAlignSelf{StyleAlignFlags::AUTO};

  // Flags for 'align-self' (safe/unsafe/legacy).
  StyleAlignFlags mAlignSelfFlags{0};
};

/**
 * Represents a single flex line in a flex container.
 * Manages an array of the FlexItems that are in the line.
 */

class nsFlexContainerFrame::FlexLine final {
 public:
  explicit FlexLine(nscoord aMainGapSize) : mMainGapSize(aMainGapSize) {}

  nscoord SumOfGaps() const {
    return NumItems() > 0 ? (NumItems() - 1) * mMainGapSize : 0;
  }

  // Returns the sum of our FlexItems' outer hypothetical main sizes plus the
  // sum of main axis {row,column}-gaps between items.
  // ("outer" = margin-box, and "hypothetical" = before flexing)
  AuCoord64 TotalOuterHypotheticalMainSize() const {
    return mTotalOuterHypotheticalMainSize;
  }

  // Accessors for our FlexItems & information about them:
  //
  // Note: Callers must use IsEmpty() to ensure that the FlexLine is non-empty
  // before calling accessors that return FlexItem.
  FlexItem& FirstItem() { return mItems[0]; }
  const FlexItem& FirstItem() const { return mItems[0]; }

  FlexItem& LastItem() { return mItems.LastElement(); }
  const FlexItem& LastItem() const { return mItems.LastElement(); }

  // The "startmost"/"endmost" is from the perspective of the flex container's
  // writing-mode, not from the perspective of the flex-relative main axis.
  const FlexItem& StartmostItem(const FlexboxAxisTracker& aAxisTracker) const {
    return aAxisTracker.IsMainAxisReversed() ? LastItem() : FirstItem();
  }
  const FlexItem& EndmostItem(const FlexboxAxisTracker& aAxisTracker) const {
    return aAxisTracker.IsMainAxisReversed() ? FirstItem() : LastItem();
  }

  bool IsEmpty() const { return mItems.IsEmpty(); }

  uint32_t NumItems() const { return mItems.Length(); }

  nsTArray<FlexItem>& Items() { return mItems; }
  const nsTArray<FlexItem>& Items() const { return mItems; }

  // Adds the last flex item's hypothetical outer main-size and
  // margin/border/padding to our totals. This should be called exactly once for
  // each flex item, after we've determined that this line is the correct home
  // for that item.
  void AddLastItemToMainSizeTotals() {
    const FlexItem& lastItem = Items().LastElement();

    // Update our various bookkeeping member-vars:
    if (lastItem.IsFrozen()) {
      mNumFrozenItems++;
    }

    mTotalItemMBP += lastItem.MarginBorderPaddingSizeInMainAxis();
    mTotalOuterHypotheticalMainSize += lastItem.OuterMainSize();

    // If the item added was not the first item in the line, we add in any gap
    // space as needed.
    if (NumItems() >= 2) {
      mTotalOuterHypotheticalMainSize += mMainGapSize;
    }
  }

  // Computes the cross-size and baseline position of this FlexLine, based on
  // its FlexItems.
  void ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker);

  // Returns the cross-size of this line.
  nscoord LineCrossSize() const { return mLineCrossSize; }

  // Setter for line cross-size -- needed for cases where the flex container
  // imposes a cross-size on the line. (e.g. for single-line flexbox, or for
  // multi-line flexbox with 'align-content: stretch')
  void SetLineCrossSize(nscoord aLineCrossSize) {
    mLineCrossSize = aLineCrossSize;
  }

  /**
   * Returns the offset within this line where any baseline-aligned FlexItems
   * should place their baseline. The return value represents a distance from
   * the line's cross-start edge.
   *
   * If there are no baseline-aligned FlexItems, returns nscoord_MIN.
   */

  nscoord FirstBaselineOffset() const { return mFirstBaselineOffset; }

  /**
   * Returns the offset within this line where any last baseline-aligned
   * FlexItems should place their baseline. Opposite the case of the first
   * baseline offset, this represents a distance from the line's cross-end
   * edge (since last baseline-aligned items are flush to the cross-end edge).
   *
   * If there are no last baseline-aligned FlexItems, returns nscoord_MIN.
   */

  nscoord LastBaselineOffset() const { return mLastBaselineOffset; }

  // Extract a baseline from this line, which would be suitable for use as the
  // flex container's 'aBaselineGroup' (i.e. first/last) baseline.
  // https://drafts.csswg.org/css-flexbox-1/#flex-baselines
  //
  // The return value always represents a distance from the line's cross-start
  // edge, even if we are querying last baseline. If this line has no flex items
  // in its aBaselineGroup group, this method falls back to trying the opposite
  // group. If this line has no baseline-aligned items at all, this returns
  // nscoord_MIN.
  nscoord ExtractBaselineOffset(BaselineSharingGroup aBaselineGroup) const;

  /**
   * Returns the gap size in the main axis for this line. Used for gap
   * calculations.
   */

  nscoord MainGapSize() const { return mMainGapSize; }

  // Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
  // CSS flexbox spec to distribute aFlexContainerMainSize among our flex items.
  // https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths
  void ResolveFlexibleLengths(nscoord aFlexContainerMainSize,
                              ComputedFlexLineInfo* aLineInfo);

  void PositionItemsInMainAxis(const StyleContentDistribution& aJustifyContent,
                               nscoord aContentBoxMainSize,
                               const FlexboxAxisTracker& aAxisTracker);

  void PositionItemsInCrossAxis(nscoord aLineStartPosition,
                                const FlexboxAxisTracker& aAxisTracker);

 private:
  // Helpers for ResolveFlexibleLengths():
  void FreezeItemsEarly(bool aIsUsingFlexGrow, ComputedFlexLineInfo* aLineInfo);

  void FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
                                       bool aIsFinalIteration);

  // Sum of FlexItems' outer hypothetical main sizes and all main-axis
  // {row,columnm}-gaps between items.
  // (i.e. their flex base sizes, clamped via their min/max-size properties,
  // plus their main-axis margin/border/padding, plus the sum of the gaps.)
  //
  // This variable uses a 64-bit coord type to avoid integer overflow in case
  // several of the individual items have huge hypothetical main sizes, which
  // can happen with percent-width table-layout:fixed descendants. We have to
  // avoid integer overflow in order to shrink items properly in that scenario.
  AuCoord64 mTotalOuterHypotheticalMainSize = 0;

  // Stores this line's flex items.
  nsTArray<FlexItem> mItems;

  // Number of *frozen* FlexItems in this line, based on FlexItem::IsFrozen().
  // Mostly used for optimization purposes, e.g. to bail out early from loops
  // when we can tell they have nothing left to do.
  uint32_t mNumFrozenItems = 0;

  // Sum of margin/border/padding for the FlexItems in this FlexLine.
  nscoord mTotalItemMBP = 0;

  nscoord mLineCrossSize = 0;
  nscoord mFirstBaselineOffset = nscoord_MIN;
  nscoord mLastBaselineOffset = nscoord_MIN;

  // Maintain size of each {row,column}-gap in the main axis
  const nscoord mMainGapSize;
};

// The "startmost"/"endmost" is from the perspective of the flex container's
// writing-mode, not from the perspective of the flex-relative cross axis.
const FlexLine& StartmostLine(const nsTArray<FlexLine>& aLines,
                              const FlexboxAxisTracker& aAxisTracker) {
  return aAxisTracker.IsCrossAxisReversed() ? aLines.LastElement() : aLines[0];
}
const FlexLine& EndmostLine(const nsTArray<FlexLine>& aLines,
                            const FlexboxAxisTracker& aAxisTracker) {
  return aAxisTracker.IsCrossAxisReversed() ? aLines[0] : aLines.LastElement();
}

// Information about a strut left behind by a FlexItem that's been collapsed
// using "visibility:collapse".
struct nsFlexContainerFrame::StrutInfo {
  StrutInfo(uint32_t aItemIdx, nscoord aStrutCrossSize)
      : mItemIdx(aItemIdx), mStrutCrossSize(aStrutCrossSize) {}

  uint32_t mItemIdx;        // Index in the child list.
  nscoord mStrutCrossSize;  // The cross-size of this strut.
};

// Flex data shared by the flex container frames in a continuation chain, owned
// by the first-in-flow. The data is initialized at the end of the
// first-in-flow's Reflow().
struct nsFlexContainerFrame::SharedFlexData final {
  // The flex lines generated in DoFlexLayout() by our first-in-flow.
  nsTArray<FlexLine> mLines;

  // The final content main/cross size computed by DoFlexLayout.
  nscoord mContentBoxMainSize = NS_UNCONSTRAINEDSIZE;
  nscoord mContentBoxCrossSize = NS_UNCONSTRAINEDSIZE;

  // Update this struct. Called by the first-in-flow.
  void Update(FlexLayoutResult&& aFlr) {
    mLines = std::move(aFlr.mLines);
    mContentBoxMainSize = aFlr.mContentBoxMainSize;
    mContentBoxCrossSize = aFlr.mContentBoxCrossSize;
  }

  // The frame property under which this struct is stored. Set only on the
  // first-in-flow.
  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, SharedFlexData)
};

// Flex data stored in every flex container's in-flow fragment (continuation).
//
// It's intended to prevent quadratic operations resulting from each fragment
// having to walk its full prev-in-flow chain, and also serves as an argument to
// the flex container next-in-flow's ReflowChildren(), to compute the position
// offset for each flex item.
struct nsFlexContainerFrame::PerFragmentFlexData final {
  // Suppose D is the distance from a flex container fragment's content-box
  // block-start edge to whichever is larger of either (a) the block-end edge of
  // its children, or (b) the available space's block-end edge. (Note: in case
  // (b), D is conceptually the sum of the block-size of the children, the
  // packing space before & in between them, and part of the packing space after
  // them.)
  //
  // This variable stores the sum of the D values for the current flex container
  // fragments and for all its previous fragments
  nscoord mCumulativeContentBoxBSize = 0;

  // This variable accumulates FirstLineOrFirstItemBAxisMetrics::mBEndEdgeShift,
  // for the current flex container fragment and for all its previous fragments.
  // See the comment of mBEndEdgeShift for its computation details. In short,
  // this value is the net block-end edge shift, accumulated for the children in
  // all the previous fragments. This number is non-negative.
  //
  // This value is also used to grow a flex container's block-size if the
  // container's computed block-size is unconstrained. For example: a tall item
  // may be pushed to the next page/column, which leaves some wasted area at the
  // bottom of the current flex container fragment, and causes the flex
  // container fragments to be (collectively) larger than the hypothetical
  // unfragmented size. Another example: a tall flex item may be broken into
  // multiple fragments, and those fragments may have a larger collective
  // block-size as compared to the item's original unfragmented size; the
  // container would need to increase its block-size to account for this.
  nscoord mCumulativeBEndEdgeShift = 0;

  // The frame property under which this struct is stored. Cached on every
  // in-flow fragment (continuation) at the end of the flex container's
  // Reflow().
  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, PerFragmentFlexData)
};

static void BuildStrutInfoFromCollapsedItems(const nsTArray<FlexLine>& aLines,
                                             nsTArray<StrutInfo>& aStruts) {
  MOZ_ASSERT(aStruts.IsEmpty(),
             "We should only build up StrutInfo once per reflow, so "
             "aStruts should be empty when this is called");

  uint32_t itemIdxInContainer = 0;
  for (const FlexLine& line : aLines) {
    for (const FlexItem& item : line.Items()) {
      if (item.Frame()->StyleVisibility()->IsCollapse()) {
        // Note the cross size of the line as the item's strut size.
        aStruts.AppendElement(
            StrutInfo(itemIdxInContainer, line.LineCrossSize()));
      }
      itemIdxInContainer++;
    }
  }
}

static mozilla::StyleAlignFlags SimplifyAlignOrJustifyContentForOneItem(
    const StyleContentDistribution& aAlignmentVal, bool aIsAlign) {
  // Mask away any explicit fallback, to get the main (non-fallback) part of
  // the specified value:
  StyleAlignFlags specified = aAlignmentVal.primary;

  // XXX strip off <overflow-position> bits until we implement it (bug 1311892)
  specified &= ~StyleAlignFlags::FLAG_BITS;

  // FIRST: handle a special-case for "justify-content:stretch" (or equivalent),
  // which requires that we ignore any author-provided explicit fallback value.
  if (specified == StyleAlignFlags::NORMAL) {
    // In a flex container, *-content: "'normal' behaves as 'stretch'".
    // Do that conversion early, so it benefits from our 'stretch' special-case.
    // https://drafts.csswg.org/css-align-3/#distribution-flex
    specified = StyleAlignFlags::STRETCH;
  }
  if (!aIsAlign && specified == StyleAlignFlags::STRETCH) {
    // In a flex container, in "justify-content Axis: [...] 'stretch' behaves
    // as 'flex-start' (ignoring the specified fallback alignment, if any)."
    // https://drafts.csswg.org/css-align-3/#distribution-flex
    // So, we just directly return 'flex-start', & ignore explicit fallback..
    return StyleAlignFlags::FLEX_START;
  }

  // TODO: Check for an explicit fallback value (and if it's present, use it)
  // here once we parse it, see https://github.com/w3c/csswg-drafts/issues/1002.

  // If there's no explicit fallback, use the implied fallback values for
  // space-{between,around,evenly} (since those values only make sense with
  // multiple alignment subjects), and otherwise just use the specified value:
  if (specified == StyleAlignFlags::SPACE_BETWEEN) {
    return StyleAlignFlags::FLEX_START;
  }
  if (specified == StyleAlignFlags::SPACE_AROUND ||
      specified == StyleAlignFlags::SPACE_EVENLY) {
    return StyleAlignFlags::CENTER;
  }
  return specified;
}

bool nsFlexContainerFrame::DrainSelfOverflowList() {
  return DrainAndMergeSelfOverflowList();
}

void nsFlexContainerFrame::AppendFrames(ChildListID aListID,
                                        nsFrameList&& aFrameList) {
  NoteNewChildren(aListID, aFrameList);
  nsContainerFrame::AppendFrames(aListID, std::move(aFrameList));
}

void nsFlexContainerFrame::InsertFrames(
    ChildListID aListID, nsIFrame* aPrevFrame,
    const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
  NoteNewChildren(aListID, aFrameList);
  nsContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
                                 std::move(aFrameList));
}

void nsFlexContainerFrame::RemoveFrame(DestroyContext& aContext,
                                       ChildListID aListID,
                                       nsIFrame* aOldFrame) {
  MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list");

#ifdef DEBUG
  SetDidPushItemsBitIfNeeded(aListID, aOldFrame);
#endif

  nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame);
}

StyleAlignFlags nsFlexContainerFrame::CSSAlignmentForAbsPosChild(
    const ReflowInput& aChildRI, LogicalAxis aLogicalAxis) const {
  const FlexboxAxisTracker axisTracker(this);

  // If we're row-oriented and the caller is asking about our inline axis (or
  // alternately, if we're column-oriented and the caller is asking about our
  // block axis), then the caller is really asking about our *main* axis.
  // Otherwise, the caller is asking about our cross axis.
  const bool isMainAxis =
      (axisTracker.IsRowOriented() == (aLogicalAxis == LogicalAxis::Inline));
  const nsStylePosition* containerStylePos = StylePosition();
  const bool isAxisReversed = isMainAxis ? axisTracker.IsMainAxisReversed()
                                         : axisTracker.IsCrossAxisReversed();

  StyleAlignFlags alignment{0};
  StyleAlignFlags alignmentFlags{0};
  if (isMainAxis) {
    // We're aligning in the main axis: align according to 'justify-content'.
    // (We don't care about justify-self; it has no effect on children of flex
    // containers, unless https://github.com/w3c/csswg-drafts/issues/7644
    // changes that.)
    alignment = SimplifyAlignOrJustifyContentForOneItem(
        containerStylePos->mJustifyContent,
        /*aIsAlign = */ false);
  } else {
    // We're aligning in the cross axis: align according to 'align-self'.
    // (We don't care about align-content; it has no effect on abspos flex
    // children, per https://github.com/w3c/csswg-drafts/issues/7596 )
    alignment = aChildRI.mStylePosition->UsedAlignSelf(Style())._0;
    // Extract and strip align flag bits
    alignmentFlags = alignment & StyleAlignFlags::FLAG_BITS;
    alignment &= ~StyleAlignFlags::FLAG_BITS;

    if (alignment == StyleAlignFlags::NORMAL) {
      // "the 'normal' keyword behaves as 'start' on replaced
      // absolutely-positioned boxes, and behaves as 'stretch' on all other
      // absolutely-positioned boxes."
      // https://drafts.csswg.org/css-align/#align-abspos
      alignment = aChildRI.mFrame->IsReplaced() ? StyleAlignFlags::START
                                                : StyleAlignFlags::STRETCH;
    }
  }

  if (alignment == StyleAlignFlags::STRETCH) {
    // The default fallback alignment for 'stretch' is 'flex-start'.
    alignment = StyleAlignFlags::FLEX_START;
  }

  // Resolve flex-start, flex-end, auto, left, right, baseline, last baseline;
  if (alignment == StyleAlignFlags::FLEX_START) {
    alignment = isAxisReversed ? StyleAlignFlags::END : StyleAlignFlags::START;
  } else if (alignment == StyleAlignFlags::FLEX_END) {
    alignment = isAxisReversed ? StyleAlignFlags::START : StyleAlignFlags::END;
  } else if (alignment == StyleAlignFlags::LEFT ||
             alignment == StyleAlignFlags::RIGHT) {
    MOZ_ASSERT(isMainAxis, "Only justify-* can have 'left' and 'right'!");
    alignment = axisTracker.ResolveJustifyLeftRight(alignment);
  } else if (alignment == StyleAlignFlags::BASELINE) {
    alignment = StyleAlignFlags::START;
  } else if (alignment == StyleAlignFlags::LAST_BASELINE) {
    alignment = StyleAlignFlags::END;
  }

  MOZ_ASSERT(alignment != StyleAlignFlags::STRETCH,
             "We should've converted 'stretch' to the fallback alignment!");
  MOZ_ASSERT(alignment != StyleAlignFlags::FLEX_START &&
                 alignment != StyleAlignFlags::FLEX_END,
             "nsAbsoluteContainingBlock doesn't know how to handle "
             "flex-relative axis for flex containers!");

  return (alignment | alignmentFlags);
}

std::pair<StyleAlignFlags, StyleAlignFlags>
nsFlexContainerFrame::UsedAlignSelfAndFlagsForItem(
    const nsIFrame* aFlexItem) const {
  MOZ_ASSERT(aFlexItem->IsFlexItem());

  if (IsLegacyBox(this)) {
    // For -webkit-{inline-}box and -moz-{inline-}box, we need to:
    // (1) Use prefixed "box-align" instead of "align-items" to determine the
    //     container's cross-axis alignment behavior.
    // (2) Suppress the ability for flex items to override that with their own
    //     cross-axis alignment. (The legacy box model doesn't support this.)
    // So, each FlexItem simply copies the container's converted "align-items"
    // value and disregards their own "align-self" property.
    const StyleAlignFlags alignSelf =
        ConvertLegacyStyleToAlignItems(StyleXUL());
    const StyleAlignFlags flags = {0};
    return {alignSelf, flags};
  }

  // Note: we don't need to call nsLayoutUtils::GetStyleFrame(aFlexItem) because
  // the table wrapper frame inherits 'align-self' property from the table
  // frame.
  StyleAlignSelf usedAlignSelf =
      aFlexItem->StylePosition()->UsedAlignSelf(Style());
  if (MOZ_LIKELY(usedAlignSelf._0 == StyleAlignFlags::NORMAL)) {
    // For flex items, 'align-self:normal' behaves as 'align-self:stretch'.
    // https://drafts.csswg.org/css-align-3/#align-flex
    usedAlignSelf = {StyleAlignFlags::STRETCH};
  }

  // Store the <overflow-position> bits in flags, and strip the bits from the
  // used align-self value.
  const StyleAlignFlags flags = usedAlignSelf._0 & StyleAlignFlags::FLAG_BITS;
  const StyleAlignFlags alignSelf =
      usedAlignSelf._0 & ~StyleAlignFlags::FLAG_BITS;
  return {alignSelf, flags};
}

void nsFlexContainerFrame::GenerateFlexItemForChild(
    FlexLine& aLine, nsIFrame* aChildFrame,
    const ReflowInput& aParentReflowInput,
    const FlexboxAxisTracker& aAxisTracker,
    const nscoord aTentativeContentBoxCrossSize) {
  const auto flexWM = aAxisTracker.GetWritingMode();
  const auto childWM = aChildFrame->GetWritingMode();

  // Note: we use GetStyleFrame() to access the sizing & flex properties here.
  // This lets us correctly handle table wrapper frames as flex items since
  // their inline-size and block-size properties are always 'auto'. In order for
  // 'flex-basis:auto' to actually resolve to the author's specified inline-size
  // or block-size, we need to dig through to the inner table.
  const auto* stylePos =
      nsLayoutUtils::GetStyleFrame(aChildFrame)->StylePosition();

  // Construct a StyleSizeOverrides for this flex item so that its ReflowInput
  // below will use and resolve its flex base size rather than its corresponding
  // preferred main size property (only for modern CSS flexbox).
  StyleSizeOverrides sizeOverrides;
  if (!IsLegacyBox(this)) {
    Maybe<StyleSize> styleFlexBaseSize;

    // When resolving flex base size, flex items use their 'flex-basis' property
    // in place of their preferred main size (e.g. 'width') for sizing purposes,
    // *unless* they have 'flex-basis:auto' in which case they use their
    // preferred main size after all.
    const auto& flexBasis = stylePos->mFlexBasis;
    const auto& styleMainSize = stylePos->Size(aAxisTracker.MainAxis(), flexWM);
    if (IsUsedFlexBasisContent(flexBasis, styleMainSize)) {
      // If we get here, we're resolving the flex base size for a flex item, and
      // we fall into the flexbox spec section 9.2 step 3, substep C (if we have
      // a definite cross size) or E (if not).
      styleFlexBaseSize.emplace(StyleSize::MaxContent());
    } else if (flexBasis.IsSize() && !flexBasis.IsAuto()) {
      // For all other non-'auto' flex-basis values, we just swap in the
      // flex-basis itself for the preferred main-size property.
      styleFlexBaseSize.emplace(flexBasis.AsSize());
    } else {
      // else: flex-basis is 'auto', which is deferring to some explicit value
      // in the preferred main size.
      MOZ_ASSERT(flexBasis.IsAuto());
      styleFlexBaseSize.emplace(styleMainSize);
    }

    MOZ_ASSERT(styleFlexBaseSize, "We should've emplace styleFlexBaseSize!");

    // Provide the size override for the preferred main size property.
    if (aAxisTracker.IsInlineAxisMainAxis(childWM)) {
      sizeOverrides.mStyleISize = std::move(styleFlexBaseSize);
    } else {
      sizeOverrides.mStyleBSize = std::move(styleFlexBaseSize);
    }

    // 'flex-basis' should works on the inner table frame for a table flex item,
    // just like how 'height' works on a table element.
    sizeOverrides.mApplyOverridesVerbatim = true;
  }

  // Create temporary reflow input just for sizing -- to get hypothetical
  // main-size and the computed values of min / max main-size property.
  // (This reflow input will _not_ be used for reflow.)
  ReflowInput childRI(PresContext(), aParentReflowInput, aChildFrame,
                      aParentReflowInput.ComputedSize(childWM), Nothing(), {},
                      sizeOverrides, {ComputeSizeFlag::ShrinkWrap});

  // FLEX GROW & SHRINK WEIGHTS
  // --------------------------
  float flexGrow, flexShrink;
  if (IsLegacyBox(this)) {
    flexGrow = flexShrink = aChildFrame->StyleXUL()->mBoxFlex;
  } else {
    flexGrow = stylePos->mFlexGrow;
    flexShrink = stylePos->mFlexShrink;
  }

  // MAIN SIZES (flex base size, min/max size)
  // -----------------------------------------
  const LogicalSize computedSizeInFlexWM = childRI.ComputedSize(flexWM);
  const LogicalSize computedMinSizeInFlexWM = childRI.ComputedMinSize(flexWM);
  const LogicalSize computedMaxSizeInFlexWM = childRI.ComputedMaxSize(flexWM);

  const nscoord flexBaseSize = aAxisTracker.MainComponent(computedSizeInFlexWM);
  const nscoord mainMinSize =
      aAxisTracker.MainComponent(computedMinSizeInFlexWM);
  const nscoord mainMaxSize =
      aAxisTracker.MainComponent(computedMaxSizeInFlexWM);

  // This is enforced by the ReflowInput where these values come from:
  MOZ_ASSERT(mainMinSize <= mainMaxSize, "min size is larger than max size");

  // CROSS SIZES (tentative cross size, min/max cross size)
  // ------------------------------------------------------
  // Grab the cross size from the reflow input. This might be the right value,
  // or we might resolve it to something else in SizeItemInCrossAxis(); hence,
  // it's tentative. See comment under "Cross Size Determination" for more.
  const nscoord tentativeCrossSize =
      aAxisTracker.CrossComponent(computedSizeInFlexWM);
  const nscoord crossMinSize =
      aAxisTracker.CrossComponent(computedMinSizeInFlexWM);
  const nscoord crossMaxSize =
      aAxisTracker.CrossComponent(computedMaxSizeInFlexWM);

  // Construct the flex item!
  FlexItem& item = *aLine.Items().EmplaceBack(
      childRI, flexGrow, flexShrink, flexBaseSize, mainMinSize, mainMaxSize,
      tentativeCrossSize, crossMinSize, crossMaxSize, aAxisTracker);

  // We may be about to do computations based on our item's cross-size
  // (e.g. using it as a constraint when measuring our content in the
  // main axis, or using it with the preferred aspect ratio to obtain a main
  // size). BEFORE WE DO THAT, we need let the item "pre-stretch" its cross size
  // (if it's got 'align-self:stretch'), for a certain case where the spec says
--> --------------------

--> maximum size reached

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

Messung V0.5
C=88 H=96 G=91

¤ Dauer der Verarbeitung: 0.31 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.