tor-browser

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

nsFlexContainerFrame.cpp (293902B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* rendering object for CSS "display: flex" */
      8 
      9 #include "nsFlexContainerFrame.h"
     10 
     11 #include <algorithm>
     12 
     13 #include "gfxContext.h"
     14 #include "mozilla/Baseline.h"
     15 #include "mozilla/CSSOrderAwareFrameIterator.h"
     16 #include "mozilla/ComputedStyle.h"
     17 #include "mozilla/Logging.h"
     18 #include "mozilla/PresShell.h"
     19 #include "mozilla/StaticPrefs_layout.h"
     20 #include "mozilla/WritingModes.h"
     21 #include "nsBlockFrame.h"
     22 #include "nsContentUtils.h"
     23 #include "nsDebug.h"
     24 #include "nsDisplayList.h"
     25 #include "nsFieldSetFrame.h"
     26 #include "nsIFrameInlines.h"
     27 #include "nsLayoutUtils.h"
     28 #include "nsPlaceholderFrame.h"
     29 #include "nsPresContext.h"
     30 
     31 using namespace mozilla;
     32 using namespace mozilla::layout;
     33 
     34 // Convenience typedefs for helper classes that we forward-declare in .h file
     35 // (so that nsFlexContainerFrame methods can use them as parameters):
     36 using FlexItem = nsFlexContainerFrame::FlexItem;
     37 using FlexLine = nsFlexContainerFrame::FlexLine;
     38 using FlexboxAxisTracker = nsFlexContainerFrame::FlexboxAxisTracker;
     39 using StrutInfo = nsFlexContainerFrame::StrutInfo;
     40 using CachedBAxisMeasurement = nsFlexContainerFrame::CachedBAxisMeasurement;
     41 using CachedFlexItemData = nsFlexContainerFrame::CachedFlexItemData;
     42 
     43 static mozilla::LazyLogModule gFlexContainerLog("FlexContainer");
     44 
     45 // FLEX_LOG is a top-level general log print.
     46 #define FLEX_LOG(message, ...) \
     47  MOZ_LOG(gFlexContainerLog, LogLevel::Debug, (message, ##__VA_ARGS__));
     48 
     49 // FLEX_ITEM_LOG is a top-level log print for flex item.
     50 #define FLEX_ITEM_LOG(item_frame, message, ...) \
     51  MOZ_LOG(gFlexContainerLog, LogLevel::Debug,   \
     52          ("Flex item %p: " message, item_frame, ##__VA_ARGS__));
     53 
     54 // FLEX_LOGV is a verbose log print with built-in two spaces indentation. The
     55 // convention to use FLEX_LOGV is that FLEX_LOGV statements should generally be
     56 // preceded by one FLEX_LOG or FLEX_ITEM_LOG so that there's no need to repeat
     57 // information presented in the preceding LOG statement. If you want extra level
     58 // of indentation, just add two extra spaces at the start of the message string.
     59 #define FLEX_LOGV(message, ...) \
     60  MOZ_LOG(gFlexContainerLog, LogLevel::Verbose, ("  " message, ##__VA_ARGS__));
     61 
     62 // Returns the OrderState enum we should pass to CSSOrderAwareFrameIterator
     63 // (depending on whether aFlexContainer has
     64 // NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER state bit).
     65 static CSSOrderAwareFrameIterator::OrderState OrderStateForIter(
     66    const nsFlexContainerFrame* aFlexContainer) {
     67  return aFlexContainer->HasAnyStateBits(
     68             NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
     69             ? CSSOrderAwareFrameIterator::OrderState::Ordered
     70             : CSSOrderAwareFrameIterator::OrderState::Unordered;
     71 }
     72 
     73 // Returns the OrderingProperty enum that we should pass to
     74 // CSSOrderAwareFrameIterator (depending on whether it's a legacy box).
     75 static CSSOrderAwareFrameIterator::OrderingProperty OrderingPropertyForIter(
     76    const nsFlexContainerFrame* aFlexContainer) {
     77  return aFlexContainer->IsLegacyWebkitBox()
     78             ? CSSOrderAwareFrameIterator::OrderingProperty::BoxOrdinalGroup
     79             : CSSOrderAwareFrameIterator::OrderingProperty::Order;
     80 }
     81 
     82 // Returns the "align-items" value that's equivalent to the legacy "box-align"
     83 // value in the given style struct.
     84 static StyleAlignFlags ConvertLegacyStyleToAlignItems(
     85    const nsStyleXUL* aStyleXUL) {
     86  // -[moz|webkit]-box-align corresponds to modern "align-items"
     87  switch (aStyleXUL->mBoxAlign) {
     88    case StyleBoxAlign::Stretch:
     89      return StyleAlignFlags::STRETCH;
     90    case StyleBoxAlign::Start:
     91      return StyleAlignFlags::FLEX_START;
     92    case StyleBoxAlign::Center:
     93      return StyleAlignFlags::CENTER;
     94    case StyleBoxAlign::Baseline:
     95      return StyleAlignFlags::BASELINE;
     96    case StyleBoxAlign::End:
     97      return StyleAlignFlags::FLEX_END;
     98  }
     99 
    100  MOZ_ASSERT_UNREACHABLE("Unrecognized mBoxAlign enum value");
    101  // Fall back to default value of "align-items" property:
    102  return StyleAlignFlags::STRETCH;
    103 }
    104 
    105 // Returns the "justify-content" value that's equivalent to the legacy
    106 // "box-pack" value in the given style struct.
    107 static StyleContentDistribution ConvertLegacyStyleToJustifyContent(
    108    const nsStyleXUL* aStyleXUL) {
    109  // -[moz|webkit]-box-pack corresponds to modern "justify-content"
    110  switch (aStyleXUL->mBoxPack) {
    111    case StyleBoxPack::Start:
    112      return {StyleAlignFlags::FLEX_START};
    113    case StyleBoxPack::Center:
    114      return {StyleAlignFlags::CENTER};
    115    case StyleBoxPack::End:
    116      return {StyleAlignFlags::FLEX_END};
    117    case StyleBoxPack::Justify:
    118      return {StyleAlignFlags::SPACE_BETWEEN};
    119  }
    120 
    121  MOZ_ASSERT_UNREACHABLE("Unrecognized mBoxPack enum value");
    122  // Fall back to default value of "justify-content" property:
    123  return {StyleAlignFlags::FLEX_START};
    124 }
    125 
    126 // Check if the size is auto or it is a keyword in the block axis.
    127 // |aIsInline| should represent whether aSize is in the inline axis, from the
    128 // perspective of the writing mode of the flex item that the size comes from.
    129 //
    130 // max-content and min-content should behave as property's initial value.
    131 // Bug 567039: We treat -moz-fit-content and -moz-available as property's
    132 // initial value for now.
    133 static inline bool IsAutoOrEnumOnBSize(const StyleSize& aSize, bool aIsInline) {
    134  return aSize.IsAuto() || (!aIsInline && !aSize.IsLengthPercentage());
    135 }
    136 
    137 // Returns true if the flex container should be treated as a single-line
    138 // container.
    139 static bool IsSingleLine(const nsIFrame* aFlexContainer,
    140                         const nsStylePosition* aStylePos) {
    141  MOZ_ASSERT(aFlexContainer->IsFlexContainerFrame());
    142 
    143  if (aFlexContainer->IsLegacyWebkitBox()) {
    144    // For legacy -webkit-{inline-}box, ignore the flex-wrap property.
    145    // These containers are always treated as single-line.
    146    return true;
    147  }
    148  return aStylePos->mFlexWrap == StyleFlexWrap::Nowrap;
    149 }
    150 
    151 // Encapsulates our flex container's main & cross axes. This class is backed by
    152 // a FlexboxAxisInfo helper member variable, and it adds some convenience APIs
    153 // on top of what that struct offers.
    154 class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker {
    155 public:
    156  explicit FlexboxAxisTracker(const nsFlexContainerFrame* aFlexContainer);
    157 
    158  // Accessors:
    159  LogicalAxis MainAxis() const {
    160    return IsRowOriented() ? LogicalAxis::Inline : LogicalAxis::Block;
    161  }
    162  LogicalAxis CrossAxis() const {
    163    return IsRowOriented() ? LogicalAxis::Block : LogicalAxis::Inline;
    164  }
    165 
    166  LogicalSide MainAxisStartSide() const;
    167  LogicalSide MainAxisEndSide() const {
    168    return GetOppositeSide(MainAxisStartSide());
    169  }
    170 
    171  LogicalSide CrossAxisStartSide() const;
    172  LogicalSide CrossAxisEndSide() const {
    173    return GetOppositeSide(CrossAxisStartSide());
    174  }
    175 
    176  mozilla::Side MainAxisPhysicalStartSide() const {
    177    return mWM.PhysicalSide(MainAxisStartSide());
    178  }
    179  mozilla::Side MainAxisPhysicalEndSide() const {
    180    return mWM.PhysicalSide(MainAxisEndSide());
    181  }
    182 
    183  mozilla::Side CrossAxisPhysicalStartSide() const {
    184    return mWM.PhysicalSide(CrossAxisStartSide());
    185  }
    186  mozilla::Side CrossAxisPhysicalEndSide() const {
    187    return mWM.PhysicalSide(CrossAxisEndSide());
    188  }
    189 
    190  // Returns the flex container's writing mode.
    191  WritingMode GetWritingMode() const { return mWM; }
    192 
    193  // Returns true if our main axis is in the reverse direction of our
    194  // writing mode's corresponding axis. (From 'flex-direction: *-reverse')
    195  bool IsMainAxisReversed() const { return mAxisInfo.mIsMainAxisReversed; }
    196  // Returns true if our cross axis is in the reverse direction of our
    197  // writing mode's corresponding axis. (From 'flex-wrap: *-reverse')
    198  bool IsCrossAxisReversed() const { return mAxisInfo.mIsCrossAxisReversed; }
    199 
    200  bool IsRowOriented() const { return mAxisInfo.mIsRowOriented; }
    201  bool IsColumnOriented() const { return !IsRowOriented(); }
    202 
    203  // aSize is expected to match the flex container's WritingMode.
    204  nscoord MainComponent(const LogicalSize& aSize) const {
    205    return IsRowOriented() ? aSize.ISize(mWM) : aSize.BSize(mWM);
    206  }
    207  int32_t MainComponent(const LayoutDeviceIntSize& aIntSize) const {
    208    return IsMainAxisHorizontal() ? aIntSize.width : aIntSize.height;
    209  }
    210 
    211  // aSize is expected to match the flex container's WritingMode.
    212  nscoord CrossComponent(const LogicalSize& aSize) const {
    213    return IsRowOriented() ? aSize.BSize(mWM) : aSize.ISize(mWM);
    214  }
    215  int32_t CrossComponent(const LayoutDeviceIntSize& aIntSize) const {
    216    return IsMainAxisHorizontal() ? aIntSize.height : aIntSize.width;
    217  }
    218 
    219  // NOTE: aMargin is expected to use the flex container's WritingMode.
    220  nscoord MarginSizeInMainAxis(const LogicalMargin& aMargin) const {
    221    // If we're row-oriented, our main axis is the inline axis.
    222    return IsRowOriented() ? aMargin.IStartEnd(mWM) : aMargin.BStartEnd(mWM);
    223  }
    224  nscoord MarginSizeInCrossAxis(const LogicalMargin& aMargin) const {
    225    // If we're row-oriented, our cross axis is the block axis.
    226    return IsRowOriented() ? aMargin.BStartEnd(mWM) : aMargin.IStartEnd(mWM);
    227  }
    228 
    229  /**
    230   * Converts a "flex-relative" point (a main-axis & cross-axis coordinate)
    231   * into a LogicalPoint, using the flex container's writing mode.
    232   *
    233   *  @arg aMainCoord  The main-axis coordinate -- i.e an offset from the
    234   *                   main-start edge of the flex container's content box.
    235   *  @arg aCrossCoord The cross-axis coordinate -- i.e an offset from the
    236   *                   cross-start edge of the flex container's content box.
    237   *  @arg aContainerMainSize  The main size of flex container's content box.
    238   *  @arg aContainerCrossSize The cross size of flex container's content box.
    239   *  @return A LogicalPoint, with the flex container's writing mode, that
    240   *          represents the same position. The logical coordinates are
    241   *          relative to the flex container's content box.
    242   */
    243  LogicalPoint LogicalPointFromFlexRelativePoint(
    244      nscoord aMainCoord, nscoord aCrossCoord, nscoord aContainerMainSize,
    245      nscoord aContainerCrossSize) const {
    246    nscoord logicalCoordInMainAxis =
    247        IsMainAxisReversed() ? aContainerMainSize - aMainCoord : aMainCoord;
    248    nscoord logicalCoordInCrossAxis =
    249        IsCrossAxisReversed() ? aContainerCrossSize - aCrossCoord : aCrossCoord;
    250 
    251    return IsRowOriented() ? LogicalPoint(mWM, logicalCoordInMainAxis,
    252                                          logicalCoordInCrossAxis)
    253                           : LogicalPoint(mWM, logicalCoordInCrossAxis,
    254                                          logicalCoordInMainAxis);
    255  }
    256 
    257  /**
    258   * Converts a "flex-relative" size (a main-axis & cross-axis size)
    259   * into a LogicalSize, using the flex container's writing mode.
    260   *
    261   *  @arg aMainSize  The main-axis size.
    262   *  @arg aCrossSize The cross-axis size.
    263   *  @return A LogicalSize, with the flex container's writing mode, that
    264   *          represents the same size.
    265   */
    266  LogicalSize LogicalSizeFromFlexRelativeSizes(nscoord aMainSize,
    267                                               nscoord aCrossSize) const {
    268    return IsRowOriented() ? LogicalSize(mWM, aMainSize, aCrossSize)
    269                           : LogicalSize(mWM, aCrossSize, aMainSize);
    270  }
    271 
    272  /**
    273   * Converts a "flex-relative" ascent (the distance from the flex container's
    274   * content-box cross-start edge to its baseline) into a logical ascent (the
    275   * distance from the flex container's content-box block-start edge to its
    276   * baseline).
    277   */
    278  nscoord LogicalAscentFromFlexRelativeAscent(
    279      nscoord aFlexRelativeAscent, nscoord aContentBoxCrossSize) const {
    280    return (IsCrossAxisReversed() ? aContentBoxCrossSize - aFlexRelativeAscent
    281                                  : aFlexRelativeAscent);
    282  }
    283 
    284  bool IsMainAxisHorizontal() const {
    285    // If we're row-oriented, and our writing mode is NOT vertical,
    286    // or we're column-oriented and our writing mode IS vertical,
    287    // then our main axis is horizontal. This handles all cases:
    288    return IsRowOriented() != mWM.IsVertical();
    289  }
    290 
    291  // Returns true if this flex item's inline axis in aItemWM is parallel (or
    292  // antiparallel) to the container's main axis. Returns false, otherwise.
    293  //
    294  // Note: this is a helper used before constructing FlexItem. Inside of flex
    295  // reflow code, FlexItem::IsInlineAxisMainAxis() is equivalent & more optimal.
    296  bool IsInlineAxisMainAxis(WritingMode aItemWM) const {
    297    return IsRowOriented() != GetWritingMode().IsOrthogonalTo(aItemWM);
    298  }
    299 
    300  // Maps justify-*: 'left' or 'right' to 'start' or 'end'.
    301  StyleAlignFlags ResolveJustifyLeftRight(const StyleAlignFlags& aFlags) const {
    302    MOZ_ASSERT(
    303        aFlags == StyleAlignFlags::LEFT || aFlags == StyleAlignFlags::RIGHT,
    304        "This helper accepts only 'LEFT' or 'RIGHT' flags!");
    305 
    306    const auto wm = GetWritingMode();
    307    const bool isJustifyLeft = aFlags == StyleAlignFlags::LEFT;
    308    if (IsColumnOriented()) {
    309      if (!wm.IsVertical()) {
    310        // Container's alignment axis (main axis) is *not* parallel to the
    311        // line-left <-> line-right axis or the physical left <-> physical right
    312        // axis, so we map both 'left' and 'right' to 'start'.
    313        return StyleAlignFlags::START;
    314      }
    315 
    316      MOZ_ASSERT(wm.PhysicalAxis(MainAxis()) == PhysicalAxis::Horizontal,
    317                 "Vertical column-oriented flex container's main axis should "
    318                 "be parallel to physical left <-> right axis!");
    319      // Map 'left' or 'right' to 'start' or 'end', depending on its block flow
    320      // direction.
    321      return isJustifyLeft == wm.IsVerticalLR() ? StyleAlignFlags::START
    322                                                : StyleAlignFlags::END;
    323    }
    324 
    325    MOZ_ASSERT(MainAxis() == LogicalAxis::Inline,
    326               "Row-oriented flex container's main axis should be parallel to "
    327               "line-left <-> line-right axis!");
    328 
    329    // If we get here, we're operating on the flex container's inline axis,
    330    // so we map 'left' to whichever of 'start' or 'end' corresponds to the
    331    // *line-relative* left side; and similar for 'right'.
    332    return isJustifyLeft == wm.IsBidiLTR() ? StyleAlignFlags::START
    333                                           : StyleAlignFlags::END;
    334  }
    335 
    336  // Delete copy-constructor & reassignment operator, to prevent accidental
    337  // (unnecessary) copying.
    338  FlexboxAxisTracker(const FlexboxAxisTracker&) = delete;
    339  FlexboxAxisTracker& operator=(const FlexboxAxisTracker&) = delete;
    340 
    341 private:
    342  const WritingMode mWM;  // The flex container's writing mode.
    343  const FlexboxAxisInfo mAxisInfo;
    344 };
    345 
    346 /**
    347 * Represents a flex item.
    348 * Includes the various pieces of input that the Flexbox Layout Algorithm uses
    349 * to resolve a flexible width.
    350 */
    351 class nsFlexContainerFrame::FlexItem final {
    352 public:
    353  // Normal constructor:
    354  FlexItem(ReflowInput& aFlexItemReflowInput, float aFlexGrow,
    355           float aFlexShrink, nscoord aFlexBaseSize, nscoord aMainMinSize,
    356           nscoord aMainMaxSize, nscoord aTentativeCrossSize,
    357           nscoord aCrossMinSize, nscoord aCrossMaxSize,
    358           const FlexboxAxisTracker& aAxisTracker);
    359 
    360  // Simplified constructor, to be used only for generating "struts":
    361  // (NOTE: This "strut" constructor uses the *container's* writing mode, which
    362  // we'll use on this FlexItem instead of the child frame's real writing mode.
    363  // This is fine - it doesn't matter what writing mode we use for a
    364  // strut, since it won't render any content and we already know its size.)
    365  FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize, WritingMode aContainerWM,
    366           const FlexboxAxisTracker& aAxisTracker);
    367 
    368  // Clone existing FlexItem for its underlying frame's continuation.
    369  // @param aContinuation a continuation in our next-in-flow chain.
    370  FlexItem CloneFor(nsIFrame* const aContinuation) const {
    371    MOZ_ASSERT(Frame() == aContinuation->FirstInFlow(),
    372               "aContinuation should be in aItem's continuation chain!");
    373    FlexItem item(*this);
    374    item.mFrame = aContinuation;
    375    item.mHadMeasuringReflow = false;
    376    return item;
    377  }
    378 
    379  // Accessors
    380  nsIFrame* Frame() const { return mFrame; }
    381  nscoord FlexBaseSize() const { return mFlexBaseSize; }
    382 
    383  nscoord MainMinSize() const {
    384    MOZ_ASSERT(!mNeedsMinSizeAutoResolution,
    385               "Someone's using an unresolved 'auto' main min-size");
    386    return mMainMinSize;
    387  }
    388  nscoord MainMaxSize() const { return mMainMaxSize; }
    389 
    390  // Note: These return the main-axis position and size of our *content box*.
    391  nscoord MainSize() const { return mMainSize; }
    392  nscoord MainPosition() const { return mMainPosn; }
    393 
    394  nscoord CrossMinSize() const { return mCrossMinSize; }
    395  nscoord CrossMaxSize() const { return mCrossMaxSize; }
    396 
    397  // Note: These return the cross-axis position and size of our *content box*.
    398  nscoord CrossSize() const { return mCrossSize; }
    399  nscoord CrossPosition() const { return mCrossPosn; }
    400 
    401  // Lazy getter for mAscent or mAscentForLast.
    402  nscoord ResolvedAscent(bool aUseFirstBaseline) const {
    403    // XXX We should be using the *container's* writing-mode (mCBWM) here,
    404    // instead of the item's (mWM). This is essentially bug 1155322.
    405    nscoord& ascent = aUseFirstBaseline ? mAscent : mAscentForLast;
    406    if (ascent != ReflowOutput::ASK_FOR_BASELINE) {
    407      return ascent;
    408    }
    409 
    410    // Use GetFirstLineBaseline() or GetLastLineBaseline() as appropriate:
    411    bool found = aUseFirstBaseline
    412                     ? nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &ascent)
    413                     : nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &ascent);
    414    if (found) {
    415      return ascent;
    416    }
    417 
    418    // If the nsLayoutUtils getter fails, then ask the frame directly:
    419    auto baselineGroup = aUseFirstBaseline ? BaselineSharingGroup::First
    420                                           : BaselineSharingGroup::Last;
    421    if (auto baseline = mFrame->GetNaturalBaselineBOffset(
    422            mWM, baselineGroup, BaselineExportContext::Other)) {
    423      // Offset for last baseline from `GetNaturalBaselineBOffset` originates
    424      // from the frame's block end, so convert it back.
    425      ascent = baselineGroup == BaselineSharingGroup::First
    426                   ? *baseline
    427                   : mFrame->BSize(mWM) - *baseline;
    428      return ascent;
    429    }
    430 
    431    // We couldn't determine a baseline, so we synthesize one from border box:
    432    ascent = Baseline::SynthesizeBOffsetFromBorderBox(
    433        mFrame, mWM, BaselineSharingGroup::First);
    434    return ascent;
    435  }
    436 
    437  // Convenience methods to compute the main & cross size of our *margin-box*.
    438  nscoord OuterMainSize() const {
    439    return mMainSize + MarginBorderPaddingSizeInMainAxis();
    440  }
    441 
    442  nscoord OuterCrossSize() const {
    443    return mCrossSize + MarginBorderPaddingSizeInCrossAxis();
    444  }
    445 
    446  // Convenience method to return the content-box block-size.
    447  nscoord BSize() const {
    448    return IsBlockAxisMainAxis() ? MainSize() : CrossSize();
    449  }
    450 
    451  // Convenience method to return the measured content-box block-size computed
    452  // in nsFlexContainerFrame::MeasureBSizeForFlexItem().
    453  Maybe<nscoord> MeasuredBSize() const;
    454 
    455  // Convenience methods to synthesize a style main size or a style cross size
    456  // with box-size considered, to provide the size overrides when constructing
    457  // ReflowInput for flex items.
    458  StyleSize StyleMainSize() const {
    459    nscoord mainSize = MainSize();
    460    if (Frame()->StylePosition()->mBoxSizing == StyleBoxSizing::Border) {
    461      mainSize += BorderPaddingSizeInMainAxis();
    462    }
    463    return StyleSize::LengthPercentage(
    464        LengthPercentage::FromAppUnits(mainSize));
    465  }
    466 
    467  StyleSize StyleCrossSize() const {
    468    nscoord crossSize = CrossSize();
    469    if (Frame()->StylePosition()->mBoxSizing == StyleBoxSizing::Border) {
    470      crossSize += BorderPaddingSizeInCrossAxis();
    471    }
    472    return StyleSize::LengthPercentage(
    473        LengthPercentage::FromAppUnits(crossSize));
    474  }
    475 
    476  // Returns the distance between this FlexItem's baseline and the cross-start
    477  // edge of its margin-box. Used in baseline alignment.
    478  //
    479  // (This function needs to be told which physical start side we're measuring
    480  // the baseline from, so that it can look up the appropriate components from
    481  // margin.)
    482  nscoord BaselineOffsetFromOuterCrossEdge(mozilla::Side aStartSide,
    483                                           bool aUseFirstLineBaseline) const;
    484 
    485  double ShareOfWeightSoFar() const { return mShareOfWeightSoFar; }
    486 
    487  bool IsFrozen() const { return mIsFrozen; }
    488 
    489  bool HadMinViolation() const {
    490    MOZ_ASSERT(!mIsFrozen, "min violation has no meaning for frozen items.");
    491    return mHadMinViolation;
    492  }
    493 
    494  bool HadMaxViolation() const {
    495    MOZ_ASSERT(!mIsFrozen, "max violation has no meaning for frozen items.");
    496    return mHadMaxViolation;
    497  }
    498 
    499  bool WasMinClamped() const {
    500    MOZ_ASSERT(mIsFrozen, "min clamping has no meaning for unfrozen items.");
    501    return mHadMinViolation;
    502  }
    503 
    504  bool WasMaxClamped() const {
    505    MOZ_ASSERT(mIsFrozen, "max clamping has no meaning for unfrozen items.");
    506    return mHadMaxViolation;
    507  }
    508 
    509  // Indicates whether this item received a preliminary "measuring" reflow
    510  // before its actual reflow.
    511  bool HadMeasuringReflow() const { return mHadMeasuringReflow; }
    512 
    513  // Indicates whether this item's computed cross-size property is 'auto'.
    514  bool IsCrossSizeAuto() const;
    515 
    516  // Indicates whether the cross-size property is set to something definite,
    517  // for the purpose of preferred aspect ratio calculations.
    518  bool IsCrossSizeDefinite(const ReflowInput& aItemReflowInput) const;
    519 
    520  // Indicates whether this item's cross-size has been stretched (from having
    521  // "align-self: stretch" with an auto cross-size and no auto margins in the
    522  // cross axis).
    523  bool IsStretched() const { return mIsStretched; }
    524 
    525  bool IsFlexBaseSizeContentBSize() const {
    526    return mIsFlexBaseSizeContentBSize;
    527  }
    528 
    529  bool IsMainMinSizeContentBSize() const { return mIsMainMinSizeContentBSize; }
    530 
    531  // Indicates whether we need to resolve an 'auto' value for the main-axis
    532  // min-[width|height] property.
    533  bool NeedsMinSizeAutoResolution() const {
    534    return mNeedsMinSizeAutoResolution;
    535  }
    536 
    537  bool HasAnyAutoMargin() const { return mHasAnyAutoMargin; }
    538 
    539  BaselineSharingGroup ItemBaselineSharingGroup() const {
    540    MOZ_ASSERT(mAlignSelf == StyleAlignFlags::BASELINE ||
    541                   mAlignSelf == StyleAlignFlags::LAST_BASELINE,
    542               "mBaselineSharingGroup only gets a meaningful value "
    543               "for baseline-aligned items");
    544    return mBaselineSharingGroup;
    545  }
    546 
    547  // Indicates whether this item is a "strut" left behind by an element with
    548  // visibility:collapse.
    549  bool IsStrut() const { return mIsStrut; }
    550 
    551  // The main axis and cross axis are relative to mCBWM.
    552  LogicalAxis MainAxis() const { return mMainAxis; }
    553  LogicalAxis CrossAxis() const { return GetOrthogonalAxis(mMainAxis); }
    554 
    555  // IsInlineAxisMainAxis() returns true if this item's inline axis is parallel
    556  // (or antiparallel) to the container's main axis. Otherwise (i.e. if this
    557  // item's inline axis is orthogonal to the container's main axis), this
    558  // function returns false. The next 3 methods are all other ways of asking
    559  // the same question, and only exist for readability at callsites (depending
    560  // on which axes those callsites are reasoning about).
    561  bool IsInlineAxisMainAxis() const { return mIsInlineAxisMainAxis; }
    562  bool IsInlineAxisCrossAxis() const { return !mIsInlineAxisMainAxis; }
    563  bool IsBlockAxisMainAxis() const { return !mIsInlineAxisMainAxis; }
    564  bool IsBlockAxisCrossAxis() const { return mIsInlineAxisMainAxis; }
    565 
    566  WritingMode GetWritingMode() const { return mWM; }
    567  WritingMode ContainingBlockWM() const { return mCBWM; }
    568  StyleAlignFlags AlignSelf() const { return mAlignSelf; }
    569  StyleAlignFlags AlignSelfFlags() const { return mAlignSelfFlags; }
    570 
    571  // Returns the flex factor (flex-grow or flex-shrink), depending on
    572  // 'aIsUsingFlexGrow'.
    573  //
    574  // Asserts fatally if called on a frozen item (since frozen items are not
    575  // flexible).
    576  float GetFlexFactor(bool aIsUsingFlexGrow) {
    577    MOZ_ASSERT(!IsFrozen(), "shouldn't need flex factor after item is frozen");
    578 
    579    return aIsUsingFlexGrow ? mFlexGrow : mFlexShrink;
    580  }
    581 
    582  // Returns the weight that we should use in the "resolving flexible lengths"
    583  // algorithm.  If we're using the flex grow factor, we just return that;
    584  // otherwise, we return the "scaled flex shrink factor" (scaled by our flex
    585  // base size, so that when both large and small items are shrinking, the large
    586  // items shrink more).
    587  //
    588  // I'm calling this a "weight" instead of a "[scaled] flex-[grow|shrink]
    589  // factor", to more clearly distinguish it from the actual flex-grow &
    590  // flex-shrink factors.
    591  //
    592  // Asserts fatally if called on a frozen item (since frozen items are not
    593  // flexible).
    594  float GetWeight(bool aIsUsingFlexGrow) {
    595    MOZ_ASSERT(!IsFrozen(), "shouldn't need weight after item is frozen");
    596 
    597    if (aIsUsingFlexGrow) {
    598      return mFlexGrow;
    599    }
    600 
    601    // We're using flex-shrink --> return mFlexShrink * mFlexBaseSize
    602    if (mFlexBaseSize == 0) {
    603      // Special-case for mFlexBaseSize == 0 -- we have no room to shrink, so
    604      // regardless of mFlexShrink, we should just return 0.
    605      // (This is really a special-case for when mFlexShrink is infinity, to
    606      // avoid performing mFlexShrink * mFlexBaseSize = inf * 0 = undefined.)
    607      return 0.0f;
    608    }
    609    return mFlexShrink * mFlexBaseSize;
    610  }
    611 
    612  bool TreatBSizeAsIndefinite() const { return mTreatBSizeAsIndefinite; }
    613 
    614  const AspectRatio& GetAspectRatio() const { return mAspectRatio; }
    615  bool HasAspectRatio() const { return !!mAspectRatio; }
    616 
    617  // Getters for margin:
    618  // ===================
    619  LogicalMargin Margin() const { return mMargin; }
    620  nsMargin PhysicalMargin() const { return mMargin.GetPhysicalMargin(mCBWM); }
    621 
    622  // Returns the margin component for a given LogicalSide in flex container's
    623  // writing-mode.
    624  nscoord GetMarginComponentForSide(LogicalSide aSide) const {
    625    return mMargin.Side(aSide, mCBWM);
    626  }
    627 
    628  // Returns the total space occupied by this item's margins in the given axis
    629  nscoord MarginSizeInMainAxis() const {
    630    return mMargin.StartEnd(MainAxis(), mCBWM);
    631  }
    632  nscoord MarginSizeInCrossAxis() const {
    633    return mMargin.StartEnd(CrossAxis(), mCBWM);
    634  }
    635 
    636  // Getters for border/padding
    637  // ==========================
    638  // Returns the total space occupied by this item's borders and padding in
    639  // the given axis
    640  LogicalMargin BorderPadding() const { return mBorderPadding; }
    641  nscoord BorderPaddingSizeInMainAxis() const {
    642    return mBorderPadding.StartEnd(MainAxis(), mCBWM);
    643  }
    644  nscoord BorderPaddingSizeInCrossAxis() const {
    645    return mBorderPadding.StartEnd(CrossAxis(), mCBWM);
    646  }
    647 
    648  // Getter for combined margin/border/padding
    649  // =========================================
    650  // Returns the total space occupied by this item's margins, borders and
    651  // padding in the given axis
    652  nscoord MarginBorderPaddingSizeInMainAxis() const {
    653    return MarginSizeInMainAxis() + BorderPaddingSizeInMainAxis();
    654  }
    655  nscoord MarginBorderPaddingSizeInCrossAxis() const {
    656    return MarginSizeInCrossAxis() + BorderPaddingSizeInCrossAxis();
    657  }
    658 
    659  // Setters
    660  // =======
    661  // Helper to set the resolved value of min-[width|height]:auto for the main
    662  // axis. (Should only be used if NeedsMinSizeAutoResolution() returns true.)
    663  void UpdateMainMinSize(nscoord aNewMinSize) {
    664    NS_ASSERTION(aNewMinSize >= 0,
    665                 "How did we end up with a negative min-size?");
    666    MOZ_ASSERT(
    667        mMainMaxSize == NS_UNCONSTRAINEDSIZE || mMainMaxSize >= aNewMinSize,
    668        "Should only use this function for resolving min-size:auto, "
    669        "and main max-size should be an upper-bound for resolved val");
    670    MOZ_ASSERT(
    671        mNeedsMinSizeAutoResolution &&
    672            (mMainMinSize == 0 || mFrame->IsThemed(mFrame->StyleDisplay())),
    673        "Should only use this function for resolving min-size:auto, "
    674        "so we shouldn't already have a nonzero min-size established "
    675        "(unless it's a themed-widget-imposed minimum size)");
    676 
    677    if (aNewMinSize > mMainMinSize) {
    678      mMainMinSize = aNewMinSize;
    679      // Also clamp main-size to be >= new min-size:
    680      mMainSize = std::max(mMainSize, aNewMinSize);
    681    }
    682    mNeedsMinSizeAutoResolution = false;
    683  }
    684 
    685  // This sets our flex base size, and then sets our main size to the
    686  // resulting "hypothetical main size" (the base size clamped to our
    687  // main-axis [min,max] sizing constraints).
    688  void SetFlexBaseSizeAndMainSize(nscoord aNewFlexBaseSize) {
    689    MOZ_ASSERT(!mIsFrozen || mFlexBaseSize == NS_UNCONSTRAINEDSIZE,
    690               "flex base size shouldn't change after we're frozen "
    691               "(unless we're just resolving an intrinsic size)");
    692    mFlexBaseSize = aNewFlexBaseSize;
    693 
    694    // Before we've resolved flexible lengths, we keep mMainSize set to
    695    // the 'hypothetical main size', which is the flex base size, clamped
    696    // to the [min,max] range:
    697    mMainSize = CSSMinMax(mFlexBaseSize, mMainMinSize, mMainMaxSize);
    698 
    699    FLEX_ITEM_LOG(mFrame, "Set flex base size: %d, hypothetical main size: %d",
    700                  mFlexBaseSize, mMainSize);
    701  }
    702 
    703  // Setters used while we're resolving flexible lengths
    704  // ---------------------------------------------------
    705 
    706  // Sets the main-size of our flex item's content-box.
    707  void SetMainSize(nscoord aNewMainSize) {
    708    MOZ_ASSERT(!mIsFrozen, "main size shouldn't change after we're frozen");
    709    mMainSize = aNewMainSize;
    710  }
    711 
    712  void SetShareOfWeightSoFar(double aNewShare) {
    713    MOZ_ASSERT(!mIsFrozen || aNewShare == 0.0,
    714               "shouldn't be giving this item any share of the weight "
    715               "after it's frozen");
    716    mShareOfWeightSoFar = aNewShare;
    717  }
    718 
    719  void Freeze() {
    720    mIsFrozen = true;
    721    // Now that we are frozen, the meaning of mHadMinViolation and
    722    // mHadMaxViolation changes to indicate min and max clamping. Clear
    723    // both of the member variables so that they are ready to be set
    724    // as clamping state later, if necessary.
    725    mHadMinViolation = false;
    726    mHadMaxViolation = false;
    727  }
    728 
    729  void SetHadMinViolation() {
    730    MOZ_ASSERT(!mIsFrozen,
    731               "shouldn't be changing main size & having violations "
    732               "after we're frozen");
    733    mHadMinViolation = true;
    734  }
    735  void SetHadMaxViolation() {
    736    MOZ_ASSERT(!mIsFrozen,
    737               "shouldn't be changing main size & having violations "
    738               "after we're frozen");
    739    mHadMaxViolation = true;
    740  }
    741  void ClearViolationFlags() {
    742    MOZ_ASSERT(!mIsFrozen,
    743               "shouldn't be altering violation flags after we're "
    744               "frozen");
    745    mHadMinViolation = mHadMaxViolation = false;
    746  }
    747 
    748  void SetWasMinClamped() {
    749    MOZ_ASSERT(!mHadMinViolation && !mHadMaxViolation, "only clamp once");
    750    // This reuses the mHadMinViolation member variable to track clamping
    751    // events. This is allowable because mHadMinViolation only reflects
    752    // a violation up until the item is frozen.
    753    MOZ_ASSERT(mIsFrozen, "shouldn't set clamping state when we are unfrozen");
    754    mHadMinViolation = true;
    755  }
    756  void SetWasMaxClamped() {
    757    MOZ_ASSERT(!mHadMinViolation && !mHadMaxViolation, "only clamp once");
    758    // This reuses the mHadMaxViolation member variable to track clamping
    759    // events. This is allowable because mHadMaxViolation only reflects
    760    // a violation up until the item is frozen.
    761    MOZ_ASSERT(mIsFrozen, "shouldn't set clamping state when we are unfrozen");
    762    mHadMaxViolation = true;
    763  }
    764 
    765  // Setters for values that are determined after we've resolved our main size
    766  // -------------------------------------------------------------------------
    767 
    768  // Sets the main-axis position of our flex item's content-box.
    769  // (This is the distance between the main-start edge of the flex container
    770  // and the main-start edge of the flex item's content-box.)
    771  void SetMainPosition(nscoord aPosn) {
    772    MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
    773    mMainPosn = aPosn;
    774  }
    775 
    776  // Sets the cross-size of our flex item's content-box.
    777  void SetCrossSize(nscoord aCrossSize) {
    778    MOZ_ASSERT(!mIsStretched,
    779               "Cross size shouldn't be modified after it's been stretched");
    780    mCrossSize = aCrossSize;
    781  }
    782 
    783  // Sets the cross-axis position of our flex item's content-box.
    784  // (This is the distance between the cross-start edge of the flex container
    785  // and the cross-start edge of the flex item.)
    786  void SetCrossPosition(nscoord aPosn) {
    787    MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
    788    mCrossPosn = aPosn;
    789  }
    790 
    791  // After a FlexItem has had a reflow, this method can be used to cache its
    792  // (possibly-unresolved) ascent, in case it's needed later for
    793  // baseline-alignment or to establish the container's baseline.
    794  // (NOTE: This can be marked 'const' even though it's modifying mAscent,
    795  // because mAscent is mutable. It's nice for this to be 'const', because it
    796  // means our final reflow can iterate over const FlexItem pointers, and we
    797  // can be sure it's not modifying those FlexItems, except via this method.)
    798  void SetAscent(nscoord aAscent) const {
    799    mAscent = aAscent;  // NOTE: this may be ASK_FOR_BASELINE
    800  }
    801 
    802  void SetHadMeasuringReflow() { mHadMeasuringReflow = true; }
    803 
    804  void SetIsFlexBaseSizeContentBSize() { mIsFlexBaseSizeContentBSize = true; }
    805 
    806  void SetIsMainMinSizeContentBSize() { mIsMainMinSizeContentBSize = true; }
    807 
    808  // Setter for margin components (for resolving "auto" margins)
    809  void SetMarginComponentForSide(LogicalSide aSide, nscoord aLength) {
    810    MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
    811    mMargin.Side(aSide, mCBWM) = aLength;
    812  }
    813 
    814  void ResolveStretchedCrossSize(nscoord aLineCrossSize);
    815 
    816  // Resolves flex base size if flex-basis' used value is 'content', using this
    817  // item's preferred aspect ratio and cross size.
    818  void ResolveFlexBaseSizeFromAspectRatio(const ReflowInput& aItemReflowInput);
    819 
    820  uint32_t NumAutoMarginsInMainAxis() const {
    821    return NumAutoMarginsInAxis(MainAxis());
    822  };
    823 
    824  uint32_t NumAutoMarginsInCrossAxis() const {
    825    return NumAutoMarginsInAxis(CrossAxis());
    826  };
    827 
    828  // Once the main size has been resolved, should we bother doing layout to
    829  // establish the cross size?
    830  bool CanMainSizeInfluenceCrossSize() const;
    831 
    832  // Returns a main size, clamped by any definite min and max cross size
    833  // converted through the preferred aspect ratio. The caller is responsible for
    834  // ensuring that the flex item's preferred aspect ratio is not zero.
    835  nscoord ClampMainSizeViaCrossAxisConstraints(
    836      nscoord aMainSize, const ReflowInput& aItemReflowInput) const;
    837 
    838  // Indicates whether we think this flex item needs a "final" reflow
    839  // (after its final flexed size & final position have been determined).
    840  //
    841  // @param aParentReflowInput the flex container's reflow input.
    842  // @return true if such a reflow is needed, or false if we believe it can
    843  // simply be moved to its final position and skip the reflow.
    844  bool NeedsFinalReflow(const ReflowInput& aParentReflowInput) const;
    845 
    846  // Gets the block frame that contains the flex item's content.  This is
    847  // Frame() itself or one of its descendants.
    848  nsBlockFrame* BlockFrame() const;
    849 
    850 protected:
    851  bool IsMinSizeAutoResolutionNeeded() const;
    852 
    853  uint32_t NumAutoMarginsInAxis(LogicalAxis aAxis) const;
    854 
    855  // Values that we already know in constructor, and remain unchanged:
    856  // The flex item's frame.
    857  nsIFrame* mFrame = nullptr;
    858  float mFlexGrow = 0.0f;
    859  float mFlexShrink = 0.0f;
    860  AspectRatio mAspectRatio;
    861 
    862  // The flex item's writing mode.
    863  WritingMode mWM;
    864 
    865  // The flex container's writing mode.
    866  WritingMode mCBWM;
    867 
    868  // The flex container's main axis in flex container's writing mode.
    869  LogicalAxis mMainAxis;
    870 
    871  // Stored in flex container's writing mode.
    872  LogicalMargin mBorderPadding;
    873 
    874  // Stored in flex container's writing mode. Its value can change when we
    875  // resolve "auto" marigns.
    876  LogicalMargin mMargin;
    877 
    878  // These are non-const so that we can lazily update them with the item's
    879  // intrinsic size (obtained via a "measuring" reflow), when necessary.
    880  // (e.g. for "flex-basis:auto;height:auto" & "min-height:auto")
    881  nscoord mFlexBaseSize = 0;
    882  nscoord mMainMinSize = 0;
    883  nscoord mMainMaxSize = 0;
    884 
    885  // mCrossMinSize and mCrossMaxSize are not changed after constructor.
    886  nscoord mCrossMinSize = 0;
    887  nscoord mCrossMaxSize = 0;
    888 
    889  // Values that we compute after constructor:
    890  nscoord mMainSize = 0;
    891  nscoord mMainPosn = 0;
    892  nscoord mCrossSize = 0;
    893  nscoord mCrossPosn = 0;
    894 
    895  // Mutable b/c it's set & resolved lazily, sometimes via const pointer. See
    896  // comment above SetAscent().
    897  // We initialize this to ASK_FOR_BASELINE, and opportunistically fill it in
    898  // with a real value if we end up reflowing this flex item. (But if we don't
    899  // reflow this flex item, then this sentinel tells us that we don't know it
    900  // yet & anyone who cares will need to explicitly request it.)
    901  //
    902  // Both mAscent and mAscentForLast are distance from the frame's border-box
    903  // block-start edge.
    904  mutable nscoord mAscent = ReflowOutput::ASK_FOR_BASELINE;
    905  mutable nscoord mAscentForLast = ReflowOutput::ASK_FOR_BASELINE;
    906 
    907  // Temporary state, while we're resolving flexible widths (for our main size)
    908  // XXXdholbert To save space, we could use a union to make these variables
    909  // overlay the same memory as some other member vars that aren't touched
    910  // until after main-size has been resolved. In particular, these could share
    911  // memory with mMainPosn through mAscent, and mIsStretched.
    912  double mShareOfWeightSoFar = 0.0;
    913 
    914  bool mIsFrozen = false;
    915  bool mHadMinViolation = false;
    916  bool mHadMaxViolation = false;
    917 
    918  // Did this item get a preliminary reflow, to measure its desired height?
    919  bool mHadMeasuringReflow = false;
    920 
    921  // See IsStretched() documentation.
    922  bool mIsStretched = false;
    923 
    924  // Is this item a "strut" left behind by an element with visibility:collapse?
    925  bool mIsStrut = false;
    926 
    927  // See IsInlineAxisMainAxis() documentation. This is not changed after
    928  // constructor.
    929  bool mIsInlineAxisMainAxis = true;
    930 
    931  // Does this item need to resolve a min-[width|height]:auto (in main-axis)?
    932  //
    933  // Note: mNeedsMinSizeAutoResolution needs to be declared towards the end of
    934  // the member variables since it's initialized in a method that depends on
    935  // other members declared above such as mCBWM, mMainAxis, and
    936  // mIsInlineAxisMainAxis.
    937  bool mNeedsMinSizeAutoResolution = false;
    938 
    939  // Should we take care to treat this item's resolved BSize as indefinite?
    940  bool mTreatBSizeAsIndefinite = false;
    941 
    942  // Does this item have an auto margin in either main or cross axis?
    943  bool mHasAnyAutoMargin = false;
    944 
    945  // Does this item have a content-based flex base size (and is that a size in
    946  // its block-axis)?
    947  bool mIsFlexBaseSizeContentBSize = false;
    948 
    949  // Does this item have a content-based resolved auto min size (and is that a
    950  // size in its block-axis)?
    951  bool mIsMainMinSizeContentBSize = false;
    952 
    953  // If this item is {first,last}-baseline-aligned using 'align-self', which of
    954  // its FlexLine's baseline sharing groups does it participate in?
    955  BaselineSharingGroup mBaselineSharingGroup = BaselineSharingGroup::First;
    956 
    957  // My "align-self" computed value (with "auto" swapped out for parent"s
    958  // "align-items" value, in our constructor).
    959  StyleAlignFlags mAlignSelf{StyleAlignFlags::AUTO};
    960 
    961  // Flags for 'align-self' (safe/unsafe/legacy).
    962  StyleAlignFlags mAlignSelfFlags{0};
    963 };
    964 
    965 /**
    966 * Represents a single flex line in a flex container.
    967 * Manages an array of the FlexItems that are in the line.
    968 */
    969 class nsFlexContainerFrame::FlexLine final {
    970 public:
    971  explicit FlexLine(nscoord aMainGapSize) : mMainGapSize(aMainGapSize) {}
    972 
    973  nscoord SumOfGaps() const {
    974    return NumItems() > 0 ? (NumItems() - 1) * mMainGapSize : 0;
    975  }
    976 
    977  // Returns the sum of our FlexItems' outer hypothetical main sizes plus the
    978  // sum of main axis {row,column}-gaps between items.
    979  // ("outer" = margin-box, and "hypothetical" = before flexing)
    980  AuCoord64 TotalOuterHypotheticalMainSize() const {
    981    return mTotalOuterHypotheticalMainSize;
    982  }
    983 
    984  // Accessors for our FlexItems & information about them:
    985  //
    986  // Note: Callers must use IsEmpty() to ensure that the FlexLine is non-empty
    987  // before calling accessors that return FlexItem.
    988  FlexItem& FirstItem() { return mItems[0]; }
    989  const FlexItem& FirstItem() const { return mItems[0]; }
    990 
    991  FlexItem& LastItem() { return mItems.LastElement(); }
    992  const FlexItem& LastItem() const { return mItems.LastElement(); }
    993 
    994  // The "startmost"/"endmost" is from the perspective of the flex container's
    995  // writing-mode, not from the perspective of the flex-relative main axis.
    996  const FlexItem& StartmostItem(const FlexboxAxisTracker& aAxisTracker) const {
    997    return aAxisTracker.IsMainAxisReversed() ? LastItem() : FirstItem();
    998  }
    999  const FlexItem& EndmostItem(const FlexboxAxisTracker& aAxisTracker) const {
   1000    return aAxisTracker.IsMainAxisReversed() ? FirstItem() : LastItem();
   1001  }
   1002 
   1003  bool IsEmpty() const { return mItems.IsEmpty(); }
   1004 
   1005  uint32_t NumItems() const { return mItems.Length(); }
   1006 
   1007  nsTArray<FlexItem>& Items() { return mItems; }
   1008  const nsTArray<FlexItem>& Items() const { return mItems; }
   1009 
   1010  // Adds the last flex item's hypothetical outer main-size and
   1011  // margin/border/padding to our totals. This should be called exactly once for
   1012  // each flex item, after we've determined that this line is the correct home
   1013  // for that item.
   1014  void AddLastItemToMainSizeTotals() {
   1015    const FlexItem& lastItem = Items().LastElement();
   1016 
   1017    // Update our various bookkeeping member-vars:
   1018    if (lastItem.IsFrozen()) {
   1019      mNumFrozenItems++;
   1020    }
   1021 
   1022    mTotalItemMBP += lastItem.MarginBorderPaddingSizeInMainAxis();
   1023    mTotalOuterHypotheticalMainSize += lastItem.OuterMainSize();
   1024 
   1025    // If the item added was not the first item in the line, we add in any gap
   1026    // space as needed.
   1027    if (NumItems() >= 2) {
   1028      mTotalOuterHypotheticalMainSize += mMainGapSize;
   1029    }
   1030  }
   1031 
   1032  // Computes the cross-size and baseline position of this FlexLine, based on
   1033  // its FlexItems.
   1034  void ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker);
   1035 
   1036  // Returns the cross-size of this line.
   1037  nscoord LineCrossSize() const { return mLineCrossSize; }
   1038 
   1039  // Setter for line cross-size -- needed for cases where the flex container
   1040  // imposes a cross-size on the line. (e.g. for single-line flexbox, or for
   1041  // multi-line flexbox with 'align-content: stretch')
   1042  void SetLineCrossSize(nscoord aLineCrossSize) {
   1043    mLineCrossSize = aLineCrossSize;
   1044  }
   1045 
   1046  /**
   1047   * Returns the offset within this line where any baseline-aligned FlexItems
   1048   * should place their baseline. The return value represents a distance from
   1049   * the line's cross-start edge.
   1050   *
   1051   * If there are no baseline-aligned FlexItems, returns nscoord_MIN.
   1052   */
   1053  nscoord FirstBaselineOffset() const { return mFirstBaselineOffset; }
   1054 
   1055  /**
   1056   * Returns the offset within this line where any last baseline-aligned
   1057   * FlexItems should place their baseline. Opposite the case of the first
   1058   * baseline offset, this represents a distance from the line's cross-end
   1059   * edge (since last baseline-aligned items are flush to the cross-end edge).
   1060   *
   1061   * If there are no last baseline-aligned FlexItems, returns nscoord_MIN.
   1062   */
   1063  nscoord LastBaselineOffset() const { return mLastBaselineOffset; }
   1064 
   1065  // Extract a baseline from this line, which would be suitable for use as the
   1066  // flex container's 'aBaselineGroup' (i.e. first/last) baseline.
   1067  // https://drafts.csswg.org/css-flexbox-1/#flex-baselines
   1068  //
   1069  // The return value always represents a distance from the line's cross-start
   1070  // edge, even if we are querying last baseline. If this line has no flex items
   1071  // in its aBaselineGroup group, this method falls back to trying the opposite
   1072  // group. If this line has no baseline-aligned items at all, this returns
   1073  // nscoord_MIN.
   1074  nscoord ExtractBaselineOffset(BaselineSharingGroup aBaselineGroup) const;
   1075 
   1076  /**
   1077   * Returns the gap size in the main axis for this line. Used for gap
   1078   * calculations.
   1079   */
   1080  nscoord MainGapSize() const { return mMainGapSize; }
   1081 
   1082  // Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
   1083  // CSS flexbox spec to distribute aFlexContainerMainSize among our flex items.
   1084  // https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths
   1085  void ResolveFlexibleLengths(nscoord aFlexContainerMainSize,
   1086                              ComputedFlexLineInfo* aLineInfo);
   1087 
   1088  void PositionItemsInMainAxis(const StyleContentDistribution& aJustifyContent,
   1089                               nscoord aContentBoxMainSize,
   1090                               const FlexboxAxisTracker& aAxisTracker);
   1091 
   1092  void PositionItemsInCrossAxis(nscoord aLineStartPosition,
   1093                                const FlexboxAxisTracker& aAxisTracker);
   1094 
   1095 private:
   1096  // Helpers for ResolveFlexibleLengths():
   1097  void FreezeItemsEarly(bool aIsUsingFlexGrow, ComputedFlexLineInfo* aLineInfo);
   1098 
   1099  void FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
   1100                                       bool aIsFinalIteration);
   1101 
   1102  // Sum of FlexItems' outer hypothetical main sizes and all main-axis
   1103  // {row,columnm}-gaps between items.
   1104  // (i.e. their flex base sizes, clamped via their min/max-size properties,
   1105  // plus their main-axis margin/border/padding, plus the sum of the gaps.)
   1106  //
   1107  // This variable uses a 64-bit coord type to avoid integer overflow in case
   1108  // several of the individual items have huge hypothetical main sizes, which
   1109  // can happen with percent-width table-layout:fixed descendants. We have to
   1110  // avoid integer overflow in order to shrink items properly in that scenario.
   1111  AuCoord64 mTotalOuterHypotheticalMainSize = 0;
   1112 
   1113  // Stores this line's flex items.
   1114  nsTArray<FlexItem> mItems;
   1115 
   1116  // Number of *frozen* FlexItems in this line, based on FlexItem::IsFrozen().
   1117  // Mostly used for optimization purposes, e.g. to bail out early from loops
   1118  // when we can tell they have nothing left to do.
   1119  uint32_t mNumFrozenItems = 0;
   1120 
   1121  // Sum of margin/border/padding for the FlexItems in this FlexLine.
   1122  nscoord mTotalItemMBP = 0;
   1123 
   1124  nscoord mLineCrossSize = 0;
   1125  nscoord mFirstBaselineOffset = nscoord_MIN;
   1126  nscoord mLastBaselineOffset = nscoord_MIN;
   1127 
   1128  // Maintain size of each {row,column}-gap in the main axis
   1129  const nscoord mMainGapSize;
   1130 };
   1131 
   1132 // The "startmost"/"endmost" is from the perspective of the flex container's
   1133 // writing-mode, not from the perspective of the flex-relative cross axis.
   1134 const FlexLine& StartmostLine(const nsTArray<FlexLine>& aLines,
   1135                              const FlexboxAxisTracker& aAxisTracker) {
   1136  return aAxisTracker.IsCrossAxisReversed() ? aLines.LastElement() : aLines[0];
   1137 }
   1138 const FlexLine& EndmostLine(const nsTArray<FlexLine>& aLines,
   1139                            const FlexboxAxisTracker& aAxisTracker) {
   1140  return aAxisTracker.IsCrossAxisReversed() ? aLines[0] : aLines.LastElement();
   1141 }
   1142 
   1143 // Information about a strut left behind by a FlexItem that's been collapsed
   1144 // using "visibility:collapse".
   1145 struct nsFlexContainerFrame::StrutInfo {
   1146  StrutInfo(uint32_t aItemIdx, nscoord aStrutCrossSize)
   1147      : mItemIdx(aItemIdx), mStrutCrossSize(aStrutCrossSize) {}
   1148 
   1149  uint32_t mItemIdx;        // Index in the child list.
   1150  nscoord mStrutCrossSize;  // The cross-size of this strut.
   1151 };
   1152 
   1153 // Flex data shared by the flex container frames in a continuation chain, owned
   1154 // by the first-in-flow. The data is initialized at the end of the
   1155 // first-in-flow's Reflow().
   1156 struct nsFlexContainerFrame::SharedFlexData final {
   1157  // The flex lines generated in DoFlexLayout() by our first-in-flow.
   1158  nsTArray<FlexLine> mLines;
   1159 
   1160  // The final content main/cross size computed by DoFlexLayout.
   1161  nscoord mContentBoxMainSize = NS_UNCONSTRAINEDSIZE;
   1162  nscoord mContentBoxCrossSize = NS_UNCONSTRAINEDSIZE;
   1163 
   1164  // Update this struct. Called by the first-in-flow.
   1165  void Update(FlexLayoutResult&& aFlr) {
   1166    mLines = std::move(aFlr.mLines);
   1167    mContentBoxMainSize = aFlr.mContentBoxMainSize;
   1168    mContentBoxCrossSize = aFlr.mContentBoxCrossSize;
   1169  }
   1170 
   1171  // The frame property under which this struct is stored. Set only on the
   1172  // first-in-flow.
   1173  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, SharedFlexData)
   1174 };
   1175 
   1176 // Flex data stored in every flex container's in-flow fragment (continuation).
   1177 //
   1178 // It's intended to prevent quadratic operations resulting from each fragment
   1179 // having to walk its full prev-in-flow chain, and also serves as an argument to
   1180 // the flex container next-in-flow's ReflowChildren(), to compute the position
   1181 // offset for each flex item.
   1182 struct nsFlexContainerFrame::PerFragmentFlexData final {
   1183  // Suppose D is the distance from a flex container fragment's content-box
   1184  // block-start edge to whichever is larger of either (a) the block-end edge of
   1185  // its children, or (b) the available space's block-end edge. (Note: in case
   1186  // (b), D is conceptually the sum of the block-size of the children, the
   1187  // packing space before & in between them, and part of the packing space after
   1188  // them.)
   1189  //
   1190  // This variable stores the sum of the D values for the current flex container
   1191  // fragments and for all its previous fragments
   1192  nscoord mCumulativeContentBoxBSize = 0;
   1193 
   1194  // This variable accumulates FirstLineOrFirstItemBAxisMetrics::mBEndEdgeShift,
   1195  // for the current flex container fragment and for all its previous fragments.
   1196  // See the comment of mBEndEdgeShift for its computation details. In short,
   1197  // this value is the net block-end edge shift, accumulated for the children in
   1198  // all the previous fragments. This number is non-negative.
   1199  //
   1200  // This value is also used to grow a flex container's block-size if the
   1201  // container's computed block-size is unconstrained. For example: a tall item
   1202  // may be pushed to the next page/column, which leaves some wasted area at the
   1203  // bottom of the current flex container fragment, and causes the flex
   1204  // container fragments to be (collectively) larger than the hypothetical
   1205  // unfragmented size. Another example: a tall flex item may be broken into
   1206  // multiple fragments, and those fragments may have a larger collective
   1207  // block-size as compared to the item's original unfragmented size; the
   1208  // container would need to increase its block-size to account for this.
   1209  nscoord mCumulativeBEndEdgeShift = 0;
   1210 
   1211  // The frame property under which this struct is stored. Cached on every
   1212  // in-flow fragment (continuation) at the end of the flex container's
   1213  // Reflow().
   1214  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, PerFragmentFlexData)
   1215 };
   1216 
   1217 static void BuildStrutInfoFromCollapsedItems(const nsTArray<FlexLine>& aLines,
   1218                                             nsTArray<StrutInfo>& aStruts) {
   1219  MOZ_ASSERT(aStruts.IsEmpty(),
   1220             "We should only build up StrutInfo once per reflow, so "
   1221             "aStruts should be empty when this is called");
   1222 
   1223  uint32_t itemIdxInContainer = 0;
   1224  for (const FlexLine& line : aLines) {
   1225    for (const FlexItem& item : line.Items()) {
   1226      if (item.Frame()->StyleVisibility()->IsCollapse()) {
   1227        // Note the cross size of the line as the item's strut size.
   1228        aStruts.AppendElement(
   1229            StrutInfo(itemIdxInContainer, line.LineCrossSize()));
   1230      }
   1231      itemIdxInContainer++;
   1232    }
   1233  }
   1234 }
   1235 
   1236 static mozilla::StyleAlignFlags SimplifyAlignOrJustifyContentForOneItem(
   1237    const StyleContentDistribution& aAlignmentVal, bool aIsAlign) {
   1238  // Mask away any explicit fallback, to get the main (non-fallback) part of
   1239  // the specified value:
   1240  StyleAlignFlags specified = aAlignmentVal.primary;
   1241 
   1242  // XXX strip off <overflow-position> bits until we implement it (bug 1311892)
   1243  specified &= ~StyleAlignFlags::FLAG_BITS;
   1244 
   1245  // FIRST: handle a special-case for "justify-content:stretch" (or equivalent),
   1246  // which requires that we ignore any author-provided explicit fallback value.
   1247  if (specified == StyleAlignFlags::NORMAL) {
   1248    // In a flex container, *-content: "'normal' behaves as 'stretch'".
   1249    // Do that conversion early, so it benefits from our 'stretch' special-case.
   1250    // https://drafts.csswg.org/css-align-3/#distribution-flex
   1251    specified = StyleAlignFlags::STRETCH;
   1252  }
   1253  if (!aIsAlign && specified == StyleAlignFlags::STRETCH) {
   1254    // In a flex container, in "justify-content Axis: [...] 'stretch' behaves
   1255    // as 'flex-start' (ignoring the specified fallback alignment, if any)."
   1256    // https://drafts.csswg.org/css-align-3/#distribution-flex
   1257    // So, we just directly return 'flex-start', & ignore explicit fallback..
   1258    return StyleAlignFlags::FLEX_START;
   1259  }
   1260 
   1261  // TODO: Check for an explicit fallback value (and if it's present, use it)
   1262  // here once we parse it, see https://github.com/w3c/csswg-drafts/issues/1002.
   1263 
   1264  // If there's no explicit fallback, use the implied fallback values for
   1265  // space-{between,around,evenly} (since those values only make sense with
   1266  // multiple alignment subjects), and otherwise just use the specified value:
   1267  if (specified == StyleAlignFlags::SPACE_BETWEEN) {
   1268    return StyleAlignFlags::FLEX_START;
   1269  }
   1270  if (specified == StyleAlignFlags::SPACE_AROUND ||
   1271      specified == StyleAlignFlags::SPACE_EVENLY) {
   1272    return StyleAlignFlags::CENTER;
   1273  }
   1274  return specified;
   1275 }
   1276 
   1277 bool nsFlexContainerFrame::DrainSelfOverflowList() {
   1278  return DrainAndMergeSelfOverflowList();
   1279 }
   1280 
   1281 void nsFlexContainerFrame::AppendFrames(ChildListID aListID,
   1282                                        nsFrameList&& aFrameList) {
   1283  NoteNewChildren(aListID, aFrameList);
   1284  nsContainerFrame::AppendFrames(aListID, std::move(aFrameList));
   1285 }
   1286 
   1287 void nsFlexContainerFrame::InsertFrames(
   1288    ChildListID aListID, nsIFrame* aPrevFrame,
   1289    const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
   1290  NoteNewChildren(aListID, aFrameList);
   1291  nsContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
   1292                                 std::move(aFrameList));
   1293 }
   1294 
   1295 void nsFlexContainerFrame::RemoveFrame(DestroyContext& aContext,
   1296                                       ChildListID aListID,
   1297                                       nsIFrame* aOldFrame) {
   1298  MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list");
   1299 
   1300 #ifdef DEBUG
   1301  SetDidPushItemsBitIfNeeded(aListID, aOldFrame);
   1302 #endif
   1303 
   1304  nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame);
   1305 }
   1306 
   1307 StyleAlignFlags nsFlexContainerFrame::CSSAlignmentForAbsPosChild(
   1308    const ReflowInput& aChildRI, LogicalAxis aLogicalAxis) const {
   1309  const FlexboxAxisTracker axisTracker(this);
   1310 
   1311  // If we're row-oriented and the caller is asking about our inline axis (or
   1312  // alternately, if we're column-oriented and the caller is asking about our
   1313  // block axis), then the caller is really asking about our *main* axis.
   1314  // Otherwise, the caller is asking about our cross axis.
   1315  const bool isMainAxis =
   1316      (axisTracker.IsRowOriented() == (aLogicalAxis == LogicalAxis::Inline));
   1317  const nsStylePosition* containerStylePos = StylePosition();
   1318  const bool isAxisReversed = isMainAxis ? axisTracker.IsMainAxisReversed()
   1319                                         : axisTracker.IsCrossAxisReversed();
   1320 
   1321  StyleAlignFlags alignment{0};
   1322  StyleAlignFlags alignmentFlags{0};
   1323  if (isMainAxis) {
   1324    // We're aligning in the main axis: align according to 'justify-content'.
   1325    // (We don't care about justify-self; it has no effect on children of flex
   1326    // containers, unless https://github.com/w3c/csswg-drafts/issues/7644
   1327    // changes that.)
   1328    alignment = SimplifyAlignOrJustifyContentForOneItem(
   1329        containerStylePos->mJustifyContent,
   1330        /*aIsAlign = */ false);
   1331  } else {
   1332    // We're aligning in the cross axis: align according to 'align-self'.
   1333    // (We don't care about align-content; it has no effect on abspos flex
   1334    // children, per https://github.com/w3c/csswg-drafts/issues/7596 )
   1335    alignment = aChildRI.mStylePosition->UsedAlignSelf(Style())._0;
   1336    // Extract and strip align flag bits
   1337    alignmentFlags = alignment & StyleAlignFlags::FLAG_BITS;
   1338    alignment &= ~StyleAlignFlags::FLAG_BITS;
   1339 
   1340    if (alignment == StyleAlignFlags::NORMAL) {
   1341      // "the 'normal' keyword behaves as 'start' on replaced
   1342      // absolutely-positioned boxes, and behaves as 'stretch' on all other
   1343      // absolutely-positioned boxes."
   1344      // https://drafts.csswg.org/css-align/#align-abspos
   1345      alignment = aChildRI.mFrame->IsReplaced() ? StyleAlignFlags::START
   1346                                                : StyleAlignFlags::STRETCH;
   1347    }
   1348  }
   1349 
   1350  if (alignment == StyleAlignFlags::STRETCH) {
   1351    // The default fallback alignment for 'stretch' is 'flex-start'.
   1352    alignment = StyleAlignFlags::FLEX_START;
   1353  }
   1354 
   1355  // Resolve flex-start, flex-end, auto, left, right, baseline, last baseline;
   1356  if (alignment == StyleAlignFlags::FLEX_START) {
   1357    alignment = isAxisReversed ? StyleAlignFlags::END : StyleAlignFlags::START;
   1358  } else if (alignment == StyleAlignFlags::FLEX_END) {
   1359    alignment = isAxisReversed ? StyleAlignFlags::START : StyleAlignFlags::END;
   1360  } else if (alignment == StyleAlignFlags::LEFT ||
   1361             alignment == StyleAlignFlags::RIGHT) {
   1362    MOZ_ASSERT(isMainAxis, "Only justify-* can have 'left' and 'right'!");
   1363    alignment = axisTracker.ResolveJustifyLeftRight(alignment);
   1364  } else if (alignment == StyleAlignFlags::BASELINE) {
   1365    alignment = StyleAlignFlags::START;
   1366  } else if (alignment == StyleAlignFlags::LAST_BASELINE) {
   1367    alignment = StyleAlignFlags::END;
   1368  }
   1369 
   1370  MOZ_ASSERT(alignment != StyleAlignFlags::STRETCH,
   1371             "We should've converted 'stretch' to the fallback alignment!");
   1372  MOZ_ASSERT(alignment != StyleAlignFlags::FLEX_START &&
   1373                 alignment != StyleAlignFlags::FLEX_END,
   1374             "AbsoluteContainingBlock doesn't know how to handle "
   1375             "flex-relative axis for flex containers!");
   1376 
   1377  return (alignment | alignmentFlags);
   1378 }
   1379 
   1380 std::pair<StyleAlignFlags, StyleAlignFlags>
   1381 nsFlexContainerFrame::UsedAlignSelfAndFlagsForItem(
   1382    const nsIFrame* aFlexItem) const {
   1383  MOZ_ASSERT(aFlexItem->IsFlexItem());
   1384 
   1385  if (IsLegacyWebkitBox()) {
   1386    // For -webkit-{inline-}box, we need to:
   1387    // (1) Use prefixed "box-align" instead of "align-items" to determine the
   1388    //     container's cross-axis alignment behavior.
   1389    // (2) Suppress the ability for flex items to override that with their own
   1390    //     cross-axis alignment. (The legacy box model doesn't support this.)
   1391    // So, each FlexItem simply copies the container's converted "align-items"
   1392    // value and disregards their own "align-self" property.
   1393    const StyleAlignFlags alignSelf =
   1394        ConvertLegacyStyleToAlignItems(StyleXUL());
   1395    const StyleAlignFlags flags = {0};
   1396    return {alignSelf, flags};
   1397  }
   1398 
   1399  // Note: we don't need to call nsLayoutUtils::GetStyleFrame(aFlexItem) because
   1400  // the table wrapper frame inherits 'align-self' property from the table
   1401  // frame.
   1402  StyleSelfAlignment usedAlignSelf =
   1403      aFlexItem->StylePosition()->UsedAlignSelf(Style());
   1404  if (MOZ_LIKELY(usedAlignSelf._0 == StyleAlignFlags::NORMAL)) {
   1405    // For flex items, 'align-self:normal' behaves as 'align-self:stretch'.
   1406    // https://drafts.csswg.org/css-align-3/#align-flex
   1407    usedAlignSelf = {StyleAlignFlags::STRETCH};
   1408  }
   1409 
   1410  // Store the <overflow-position> bits in flags, and strip the bits from the
   1411  // used align-self value.
   1412  const StyleAlignFlags flags = usedAlignSelf._0 & StyleAlignFlags::FLAG_BITS;
   1413  const StyleAlignFlags alignSelf =
   1414      usedAlignSelf._0 & ~StyleAlignFlags::FLAG_BITS;
   1415  return {alignSelf, flags};
   1416 }
   1417 
   1418 void nsFlexContainerFrame::GenerateFlexItemForChild(
   1419    FlexLine& aLine, nsIFrame* aChildFrame,
   1420    const ReflowInput& aParentReflowInput,
   1421    const FlexboxAxisTracker& aAxisTracker,
   1422    const nscoord aTentativeContentBoxCrossSize) {
   1423  const auto flexWM = aAxisTracker.GetWritingMode();
   1424  const auto childWM = aChildFrame->GetWritingMode();
   1425 
   1426  // Note: we use GetStyleFrame() to access the sizing & flex properties here.
   1427  // This lets us correctly handle table wrapper frames as flex items since
   1428  // their inline-size and block-size properties are always 'auto'. In order for
   1429  // 'flex-basis:auto' to actually resolve to the author's specified inline-size
   1430  // or block-size, we need to dig through to the inner table.
   1431  const auto* styleFrame = nsLayoutUtils::GetStyleFrame(aChildFrame);
   1432  const auto* stylePos = styleFrame->StylePosition();
   1433  const auto anchorResolutionParams =
   1434      AnchorPosResolutionParams::From(styleFrame);
   1435 
   1436  // Construct a StyleSizeOverrides for this flex item so that its ReflowInput
   1437  // below will use and resolve its flex base size rather than its corresponding
   1438  // preferred main size property (only for modern CSS flexbox).
   1439  StyleSizeOverrides sizeOverrides;
   1440  if (!IsLegacyWebkitBox()) {
   1441    Maybe<StyleSize> styleFlexBaseSize;
   1442 
   1443    // When resolving flex base size, flex items use their 'flex-basis' property
   1444    // in place of their preferred main size (e.g. 'width') for sizing purposes,
   1445    // *unless* they have 'flex-basis:auto' in which case they use their
   1446    // preferred main size after all.
   1447    const auto& flexBasis = stylePos->mFlexBasis;
   1448    const auto styleMainSize =
   1449        stylePos->Size(aAxisTracker.MainAxis(), flexWM, anchorResolutionParams);
   1450    if (IsUsedFlexBasisContent(flexBasis, *styleMainSize)) {
   1451      // If we get here, we're resolving the flex base size for a flex item, and
   1452      // we fall into the flexbox spec section 9.2 step 3, substep C (if we have
   1453      // a definite cross size) or E (if not).
   1454      styleFlexBaseSize.emplace(StyleSize::MaxContent());
   1455    } else if (flexBasis.IsSize() && !flexBasis.IsAuto()) {
   1456      // For all other non-'auto' flex-basis values, we just swap in the
   1457      // flex-basis itself for the preferred main-size property.
   1458      styleFlexBaseSize.emplace(flexBasis.AsSize());
   1459    } else {
   1460      // else: flex-basis is 'auto', which is deferring to some explicit value
   1461      // in the preferred main size.
   1462      MOZ_ASSERT(flexBasis.IsAuto());
   1463      styleFlexBaseSize.emplace(*styleMainSize);
   1464    }
   1465 
   1466    MOZ_ASSERT(styleFlexBaseSize, "We should've emplace styleFlexBaseSize!");
   1467 
   1468    // Provide the size override for the preferred main size property.
   1469    if (aAxisTracker.IsInlineAxisMainAxis(childWM)) {
   1470      sizeOverrides.mStyleISize = std::move(styleFlexBaseSize);
   1471    } else {
   1472      sizeOverrides.mStyleBSize = std::move(styleFlexBaseSize);
   1473    }
   1474 
   1475    // 'flex-basis' should works on the inner table frame for a table flex item,
   1476    // just like how 'height' works on a table element.
   1477    sizeOverrides.mApplyOverridesVerbatim = true;
   1478  }
   1479 
   1480  // Create temporary reflow input just for sizing -- to get hypothetical
   1481  // main-size and the computed values of min / max main-size property.
   1482  // (This reflow input will _not_ be used for reflow.)
   1483  ReflowInput childRI(PresContext(), aParentReflowInput, aChildFrame,
   1484                      aParentReflowInput.ComputedSize(childWM), Nothing(), {},
   1485                      sizeOverrides, {ComputeSizeFlag::ShrinkWrap});
   1486 
   1487  // FLEX GROW & SHRINK WEIGHTS
   1488  // --------------------------
   1489  float flexGrow, flexShrink;
   1490  if (IsLegacyWebkitBox()) {
   1491    flexGrow = flexShrink = aChildFrame->StyleXUL()->mBoxFlex;
   1492  } else {
   1493    flexGrow = stylePos->mFlexGrow;
   1494    flexShrink = stylePos->mFlexShrink;
   1495  }
   1496 
   1497  // MAIN SIZES (flex base size, min/max size)
   1498  // -----------------------------------------
   1499  const LogicalSize computedSizeInFlexWM = childRI.ComputedSize(flexWM);
   1500  const LogicalSize computedMinSizeInFlexWM = childRI.ComputedMinSize(flexWM);
   1501  const LogicalSize computedMaxSizeInFlexWM = childRI.ComputedMaxSize(flexWM);
   1502 
   1503  const nscoord flexBaseSize = aAxisTracker.MainComponent(computedSizeInFlexWM);
   1504  const nscoord mainMinSize =
   1505      aAxisTracker.MainComponent(computedMinSizeInFlexWM);
   1506  const nscoord mainMaxSize =
   1507      aAxisTracker.MainComponent(computedMaxSizeInFlexWM);
   1508 
   1509  // This is enforced by the ReflowInput where these values come from:
   1510  MOZ_ASSERT(mainMinSize <= mainMaxSize, "min size is larger than max size");
   1511 
   1512  // CROSS SIZES (tentative cross size, min/max cross size)
   1513  // ------------------------------------------------------
   1514  // Grab the cross size from the reflow input. This might be the right value,
   1515  // or we might resolve it to something else in SizeItemInCrossAxis(); hence,
   1516  // it's tentative. See comment under "Cross Size Determination" for more.
   1517  const nscoord tentativeCrossSize =
   1518      aAxisTracker.CrossComponent(computedSizeInFlexWM);
   1519  const nscoord crossMinSize =
   1520      aAxisTracker.CrossComponent(computedMinSizeInFlexWM);
   1521  const nscoord crossMaxSize =
   1522      aAxisTracker.CrossComponent(computedMaxSizeInFlexWM);
   1523 
   1524  // Construct the flex item!
   1525  FlexItem& item = *aLine.Items().EmplaceBack(
   1526      childRI, flexGrow, flexShrink, flexBaseSize, mainMinSize, mainMaxSize,
   1527      tentativeCrossSize, crossMinSize, crossMaxSize, aAxisTracker);
   1528 
   1529  // We may be about to do computations based on our item's cross-size
   1530  // (e.g. using it as a constraint when measuring our content in the
   1531  // main axis, or using it with the preferred aspect ratio to obtain a main
   1532  // size). BEFORE WE DO THAT, we need let the item "pre-stretch" its cross size
   1533  // (if it's got 'align-self:stretch'), for a certain case where the spec says
   1534  // the stretched cross size is considered "definite". That case is if we
   1535  // have a single-line (nowrap) flex container which itself has a definite
   1536  // cross-size.  Otherwise, we'll wait to do stretching, since (in other
   1537  // cases) we don't know how much the item should stretch yet.
   1538  if (IsSingleLine(aParentReflowInput.mFrame,
   1539                   aParentReflowInput.mStylePosition)) {
   1540    // Is container's cross size "definite"?
   1541    // - If it's column-oriented, then "yes", because its cross size is its
   1542    // inline-size which is always definite from its descendants' perspective.
   1543    // - Otherwise (if it's row-oriented), then we check the actual size
   1544    // and call it definite if it's not NS_UNCONSTRAINEDSIZE.
   1545    if (aAxisTracker.IsColumnOriented() ||
   1546        aTentativeContentBoxCrossSize != NS_UNCONSTRAINEDSIZE) {
   1547      // Container's cross size is "definite", so we can resolve the item's
   1548      // stretched cross size using that.
   1549      item.ResolveStretchedCrossSize(aTentativeContentBoxCrossSize);
   1550    }
   1551  }
   1552 
   1553  // Before thinking about freezing the item at its base size, we need to give
   1554  // it a chance to recalculate the base size from its cross size and aspect
   1555  // ratio (since its cross size might've *just* now become definite due to
   1556  // 'stretch' above)
   1557  item.ResolveFlexBaseSizeFromAspectRatio(childRI);
   1558 
   1559  // If we're inflexible, we can just freeze to our hypothetical main-size
   1560  // up-front.
   1561  if (flexGrow == 0.0f && flexShrink == 0.0f) {
   1562    item.Freeze();
   1563    if (flexBaseSize < mainMinSize) {
   1564      item.SetWasMinClamped();
   1565    } else if (flexBaseSize > mainMaxSize) {
   1566      item.SetWasMaxClamped();
   1567    }
   1568  }
   1569 
   1570  // Resolve "flex-basis:auto" and/or "min-[width|height]:auto" (which might
   1571  // require us to reflow the item to measure content height)
   1572  ResolveAutoFlexBasisAndMinSize(item, childRI, aAxisTracker);
   1573 }
   1574 
   1575 nscoord nsFlexContainerFrame::PartiallyResolveAutoMinSize(
   1576    const FlexItem& aFlexItem, const ReflowInput& aItemReflowInput,
   1577    const FlexboxAxisTracker& aAxisTracker) const {
   1578  MOZ_ASSERT(aFlexItem.NeedsMinSizeAutoResolution(),
   1579             "only call for FlexItems that need min-size auto resolution");
   1580 
   1581  const auto itemWM = aFlexItem.GetWritingMode();
   1582  const auto cbWM = aAxisTracker.GetWritingMode();
   1583  const auto anchorResolutionParams =
   1584      AnchorPosResolutionParams::From(&aItemReflowInput);
   1585  const auto mainStyleSize = aItemReflowInput.mStylePosition->Size(
   1586      aAxisTracker.MainAxis(), cbWM, anchorResolutionParams);
   1587  const auto maxMainStyleSize = aItemReflowInput.mStylePosition->MaxSize(
   1588      aAxisTracker.MainAxis(), cbWM, anchorResolutionParams);
   1589  const auto boxSizingAdjust =
   1590      aItemReflowInput.mStylePosition->mBoxSizing == StyleBoxSizing::Border
   1591          ? aFlexItem.BorderPadding().Size(cbWM)
   1592          : LogicalSize(cbWM);
   1593 
   1594  // Return the percentage basis in cbWM for computing the specified size
   1595  // suggestion.
   1596  auto PercentageBasisForItem = [&]() {
   1597    // If this flex item is a compressible replaced element, according to the
   1598    // list in CSS Sizing 3 §5.2.2, then CSS Sizing 3 §5.2.1c requires us to
   1599    // resolve the percentage part of the preferred main size property against
   1600    // zero, yielding a definite specified size suggestion. Here we can use a
   1601    // zero percentage basis to fulfill this requirement.
   1602    if (aFlexItem.Frame()->IsPercentageResolvedAgainstZero(*mainStyleSize,
   1603                                                           *maxMainStyleSize)) {
   1604      return LogicalSize(cbWM, 0, 0);
   1605    }
   1606    return aItemReflowInput.mContainingBlockSize.ConvertTo(cbWM, itemWM);
   1607  };
   1608 
   1609  // Compute the specified size suggestion, which is the main-size property if
   1610  // it's definite.
   1611  nscoord specifiedSizeSuggestion = nscoord_MAX;
   1612 
   1613  if (aAxisTracker.IsRowOriented()) {
   1614    // TODO(dholbert): We need to handle 'stretch' (and its prefixed aliases)
   1615    // here; that's tracked in bug 1936942.  (Note that we do handle 'stretch'
   1616    // in our column-oriented "else" clause below, via the call to
   1617    // ComputeBSizeValueHandlingStretch.)
   1618    if (mainStyleSize->IsLengthPercentage()) {
   1619      // NOTE: We ignore extremum inline-size. This is OK because the caller is
   1620      // responsible for computing the min-content inline-size and min()'ing it
   1621      // with the value we return.
   1622      specifiedSizeSuggestion = aFlexItem.Frame()->ComputeISizeValue(
   1623          cbWM, PercentageBasisForItem(), boxSizingAdjust,
   1624          mainStyleSize->AsLengthPercentage());
   1625    }
   1626  } else {
   1627    // NOTE: We ignore specified block-sizes that behave as 'auto', as
   1628    // identified by IsAutoBSize(); that's OK because the caller is responsible
   1629    // for computing the content-based block-size and and min()'ing it with the
   1630    // value we return.
   1631    const auto percentageBasisBSize = PercentageBasisForItem().BSize(cbWM);
   1632    if (!nsLayoutUtils::IsAutoBSize(*mainStyleSize, percentageBasisBSize)) {
   1633      specifiedSizeSuggestion = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
   1634          percentageBasisBSize, aFlexItem.MarginSizeInMainAxis(),
   1635          aFlexItem.BorderPaddingSizeInMainAxis(), boxSizingAdjust.BSize(cbWM),
   1636          *mainStyleSize);
   1637    }
   1638  }
   1639 
   1640  if (specifiedSizeSuggestion != nscoord_MAX) {
   1641    // We have the specified size suggestion. Return it now since we don't need
   1642    // to consider transferred size suggestion.
   1643    FLEX_LOGV("Specified size suggestion: %d", specifiedSizeSuggestion);
   1644    return specifiedSizeSuggestion;
   1645  }
   1646 
   1647  // Compute the transferred size suggestion, which is the cross size converted
   1648  // through the aspect ratio (if the item is replaced, and it has an aspect
   1649  // ratio and a definite cross size).
   1650  if (const auto& aspectRatio = aFlexItem.GetAspectRatio();
   1651      aFlexItem.Frame()->IsReplaced() && aspectRatio &&
   1652      aFlexItem.IsCrossSizeDefinite(aItemReflowInput)) {
   1653    // We have a usable aspect ratio. (not going to divide by 0)
   1654    nscoord transferredSizeSuggestion = aspectRatio.ComputeRatioDependentSize(
   1655        aFlexItem.MainAxis(), cbWM, aFlexItem.CrossSize(), boxSizingAdjust);
   1656 
   1657    // Clamp the transferred size suggestion by any definite min and max
   1658    // cross size converted through the aspect ratio.
   1659    transferredSizeSuggestion = aFlexItem.ClampMainSizeViaCrossAxisConstraints(
   1660        transferredSizeSuggestion, aItemReflowInput);
   1661 
   1662    FLEX_LOGV("Transferred size suggestion: %d", transferredSizeSuggestion);
   1663    return transferredSizeSuggestion;
   1664  }
   1665 
   1666  return nscoord_MAX;
   1667 }
   1668 
   1669 // Note: If & when we handle "min-height: min-content" for flex items,
   1670 // we may want to resolve that in this function, too.
   1671 void nsFlexContainerFrame::ResolveAutoFlexBasisAndMinSize(
   1672    FlexItem& aFlexItem, const ReflowInput& aItemReflowInput,
   1673    const FlexboxAxisTracker& aAxisTracker) {
   1674  // (Note: We can guarantee that the flex-basis will have already been
   1675  // resolved if the main axis is the same as the item's inline
   1676  // axis. Inline-axis values should always be resolvable without reflow.)
   1677  const bool isMainSizeAuto =
   1678      (!aFlexItem.IsInlineAxisMainAxis() &&
   1679       NS_UNCONSTRAINEDSIZE == aFlexItem.FlexBaseSize());
   1680 
   1681  const bool isMainMinSizeAuto = aFlexItem.NeedsMinSizeAutoResolution();
   1682 
   1683  if (!isMainSizeAuto && !isMainMinSizeAuto) {
   1684    // Nothing to do; this function is only needed for flex items
   1685    // with a used flex-basis of "auto" or a min-main-size of "auto".
   1686    return;
   1687  }
   1688 
   1689  FLEX_ITEM_LOG(
   1690      aFlexItem.Frame(),
   1691      "Resolving auto main size? %s; resolving auto min main size? %s",
   1692      YesOrNo(isMainSizeAuto), YesOrNo(isMainMinSizeAuto));
   1693 
   1694  nscoord resolvedMinSize;  // (only set/used if isMainMinSizeAuto==true)
   1695  bool minSizeNeedsToMeasureContent = false;  // assume the best
   1696  if (isMainMinSizeAuto) {
   1697    if (IsLegacyWebkitBox()) {
   1698      // Allow flex items in a legacy flex container to shrink below their
   1699      // automatic minimum size by setting the resolved minimum size to zero.
   1700      // This behavior is not in the spec, but it aligns with blink and webkit's
   1701      // implementation.
   1702      resolvedMinSize = 0;
   1703    } else {
   1704      // Resolve the min-size, except for considering the min-content size.
   1705      // (We'll consider that later, if we need to.)
   1706      resolvedMinSize = PartiallyResolveAutoMinSize(aFlexItem, aItemReflowInput,
   1707                                                    aAxisTracker);
   1708    }
   1709    if (resolvedMinSize > 0) {
   1710      // If resolvedMinSize were already at 0, we could skip calculating content
   1711      // size suggestion because it can't go any lower.
   1712      minSizeNeedsToMeasureContent = true;
   1713    }
   1714  }
   1715 
   1716  const bool flexBasisNeedsToMeasureContent = isMainSizeAuto;
   1717 
   1718  // Measure content, if needed (w/ intrinsic-width method or a reflow)
   1719  if (minSizeNeedsToMeasureContent || flexBasisNeedsToMeasureContent) {
   1720    // Compute the content size suggestion, which is the min-content size in the
   1721    // main axis.
   1722    nscoord contentSizeSuggestion = nscoord_MAX;
   1723 
   1724    if (aFlexItem.IsInlineAxisMainAxis()) {
   1725      if (minSizeNeedsToMeasureContent) {
   1726        // Compute the flex item's content size suggestion, which is the
   1727        // 'min-content' size on the main axis.
   1728        // https://drafts.csswg.org/css-flexbox-1/#content-size-suggestion
   1729        const auto cbWM = aAxisTracker.GetWritingMode();
   1730        const auto itemWM = aFlexItem.GetWritingMode();
   1731        const nscoord availISize = 0;  // for min-content size
   1732        StyleSizeOverrides sizeOverrides;
   1733        sizeOverrides.mStyleISize.emplace(StyleSize::Auto());
   1734        if (aFlexItem.IsStretched()) {
   1735          sizeOverrides.mStyleBSize.emplace(aFlexItem.StyleCrossSize());
   1736        }
   1737        const auto sizeInItemWM = aFlexItem.Frame()->ComputeSize(
   1738            aItemReflowInput, itemWM, aItemReflowInput.mContainingBlockSize,
   1739            availISize,
   1740            aItemReflowInput.ComputedLogicalMargin(itemWM).Size(itemWM),
   1741            aItemReflowInput.ComputedLogicalBorderPadding(itemWM).Size(itemWM),
   1742            sizeOverrides, {ComputeSizeFlag::ShrinkWrap});
   1743 
   1744        contentSizeSuggestion = aAxisTracker.MainComponent(
   1745            sizeInItemWM.mLogicalSize.ConvertTo(cbWM, itemWM));
   1746      }
   1747      NS_ASSERTION(!flexBasisNeedsToMeasureContent,
   1748                   "flex-basis:auto should have been resolved in the "
   1749                   "reflow input, for horizontal flexbox. It shouldn't need "
   1750                   "special handling here");
   1751    } else {
   1752      // If this item is flexible (in its block axis)...
   1753      // OR if we're measuring its 'auto' min-BSize, with its main-size (in its
   1754      // block axis) being something non-"auto"...
   1755      // THEN: we assume that the computed BSize that we're reflowing with now
   1756      // could be different from the one we'll use for this flex item's
   1757      // "actual" reflow later on.  In that case, we need to be sure the flex
   1758      // item treats this as a block-axis resize (regardless of whether there
   1759      // are actually any ancestors being resized in that axis).
   1760      // (Note: We don't have to do this for the inline axis, because
   1761      // InitResizeFlags will always turn on mIsIResize on when it sees that
   1762      // the computed ISize is different from current ISize, and that's all we
   1763      // need.)
   1764      bool forceBResizeForMeasuringReflow =
   1765          !aFlexItem.IsFrozen() ||          // Is the item flexible?
   1766          !flexBasisNeedsToMeasureContent;  // Are we *only* measuring it for
   1767                                            // 'min-block-size:auto'?
   1768 
   1769      const ReflowInput& flexContainerRI = *aItemReflowInput.mParentReflowInput;
   1770      nscoord contentBSize = MeasureFlexItemContentBSize(
   1771          aFlexItem, forceBResizeForMeasuringReflow, flexContainerRI);
   1772      if (minSizeNeedsToMeasureContent) {
   1773        contentSizeSuggestion = contentBSize;
   1774      }
   1775      if (flexBasisNeedsToMeasureContent) {
   1776        aFlexItem.SetFlexBaseSizeAndMainSize(contentBSize);
   1777        aFlexItem.SetIsFlexBaseSizeContentBSize();
   1778      }
   1779    }
   1780 
   1781    if (minSizeNeedsToMeasureContent) {
   1782      // Clamp the content size suggestion by any definite min and max cross
   1783      // size converted through the aspect ratio.
   1784      if (aFlexItem.HasAspectRatio()) {
   1785        contentSizeSuggestion = aFlexItem.ClampMainSizeViaCrossAxisConstraints(
   1786            contentSizeSuggestion, aItemReflowInput);
   1787      }
   1788 
   1789      FLEX_LOGV("Content size suggestion: %d", contentSizeSuggestion);
   1790      resolvedMinSize = std::min(resolvedMinSize, contentSizeSuggestion);
   1791 
   1792      // Clamp the resolved min main size by the max main size if it's definite.
   1793      if (aFlexItem.MainMaxSize() != NS_UNCONSTRAINEDSIZE) {
   1794        resolvedMinSize = std::min(resolvedMinSize, aFlexItem.MainMaxSize());
   1795      } else if (MOZ_UNLIKELY(resolvedMinSize > nscoord_MAX)) {
   1796        NS_WARNING("Bogus resolved auto min main size!");
   1797        // Our resolved min-size is bogus, probably due to some huge sizes in
   1798        // the content. Clamp it to the valid nscoord range, so that we can at
   1799        // least depend on it being <= the max-size (which is also the
   1800        // nscoord_MAX sentinel value if we reach this point).
   1801        resolvedMinSize = nscoord_MAX;
   1802      }
   1803      FLEX_LOGV("Resolved auto min main size: %d", resolvedMinSize);
   1804 
   1805      if (resolvedMinSize == contentSizeSuggestion) {
   1806        // When we are here, we've measured the item's content-based size, and
   1807        // we used it as the resolved auto min main size. Record the fact so
   1808        // that we can use it to determine whether we allow a flex item to grow
   1809        // its block-size in ReflowFlexItem().
   1810        aFlexItem.SetIsMainMinSizeContentBSize();
   1811      }
   1812    }
   1813  }
   1814 
   1815  if (isMainMinSizeAuto) {
   1816    aFlexItem.UpdateMainMinSize(resolvedMinSize);
   1817  }
   1818 }
   1819 
   1820 /**
   1821 * A cached result for a flex item's block-axis measuring reflow. This cache
   1822 * prevents us from doing exponential reflows in cases of deeply nested flex
   1823 * and scroll frames.
   1824 *
   1825 * We store the cached value in the flex item's frame property table, for
   1826 * simplicity.
   1827 *
   1828 * Right now, we cache the following as a "key", from the item's ReflowInput:
   1829 *   - its ComputedSize
   1830 *   - its min/max block size (in case its ComputedBSize is unconstrained)
   1831 *   - its AvailableBSize
   1832 * ...and we cache the following as the "value", from the item's ReflowOutput:
   1833 *   - its final content-box BSize
   1834 *
   1835 * The assumption here is that a given flex item measurement from our "value"
   1836 * won't change unless one of the pieces of the "key" change, or the flex
   1837 * item's intrinsic size is marked as dirty (due to a style or DOM change).
   1838 * (The latter will cause the cached value to be discarded, in
   1839 * nsIFrame::MarkIntrinsicISizesDirty.)
   1840 *
   1841 * Note that the components of "Key" (mComputed{MinB,MaxB,}Size and
   1842 * mAvailableBSize) are sufficient to catch any changes to the flex container's
   1843 * size that the item may care about for its measuring reflow. Specifically:
   1844 *  - If the item cares about the container's size (e.g. if it has a percent
   1845 *    height and the container's height changes, in a horizontal-WM container)
   1846 *    then that'll be detectable via the item's ReflowInput's "ComputedSize()"
   1847 *    differing from the value in our Key.  And the same applies for the
   1848 *    inline axis.
   1849 *  - If the item is fragmentable (pending bug 939897) and its measured BSize
   1850 *    depends on where it gets fragmented, then that sort of change can be
   1851 *    detected due to the item's ReflowInput's "AvailableBSize()" differing
   1852 *    from the value in our Key.
   1853 *
   1854 * One particular case to consider (& need to be sure not to break when
   1855 * changing this class): the flex item's computed BSize may change between
   1856 * measuring reflows due to how the mIsFlexContainerMeasuringBSize flag affects
   1857 * size computation (see bug 1336708). This is one reason we need to use the
   1858 * computed BSize as part of the key.
   1859 */
   1860 class nsFlexContainerFrame::CachedBAxisMeasurement {
   1861  struct Key {
   1862    const LogicalSize mComputedSize;
   1863    const nscoord mComputedMinBSize;
   1864    const nscoord mComputedMaxBSize;
   1865    const nscoord mAvailableBSize;
   1866 
   1867    explicit Key(const ReflowInput& aRI)
   1868        : mComputedSize(aRI.ComputedSize()),
   1869          mComputedMinBSize(aRI.ComputedMinBSize()),
   1870          mComputedMaxBSize(aRI.ComputedMaxBSize()),
   1871          mAvailableBSize(aRI.AvailableBSize()) {}
   1872 
   1873    bool operator==(const Key& aOther) const = default;
   1874  };
   1875 
   1876  const Key mKey;
   1877 
   1878  // This could/should be const, but it's non-const for now just because it's
   1879  // assigned via a series of steps in the constructor body:
   1880  nscoord mBSize;
   1881 
   1882 public:
   1883  CachedBAxisMeasurement(const ReflowInput& aReflowInput,
   1884                         const ReflowOutput& aReflowOutput)
   1885      : mKey(aReflowInput) {
   1886    // To get content-box bsize, we have to subtract off border & padding
   1887    // (and floor at 0 in case the border/padding are too large):
   1888    WritingMode itemWM = aReflowInput.GetWritingMode();
   1889    nscoord borderBoxBSize = aReflowOutput.BSize(itemWM);
   1890    mBSize =
   1891        borderBoxBSize -
   1892        aReflowInput.ComputedLogicalBorderPadding(itemWM).BStartEnd(itemWM);
   1893    mBSize = std::max(0, mBSize);
   1894  }
   1895 
   1896  /**
   1897   * Returns true if this cached flex item measurement is valid for (i.e. can
   1898   * be expected to match the output of) a measuring reflow whose input
   1899   * parameters are given via aReflowInput.
   1900   */
   1901  bool IsValidFor(const ReflowInput& aReflowInput) const {
   1902    return mKey == Key(aReflowInput);
   1903  }
   1904 
   1905  nscoord BSize() const { return mBSize; }
   1906 };
   1907 
   1908 /**
   1909 * A cached copy of various metrics from a flex item's most recent final reflow.
   1910 * It can be used to determine whether we can optimize away the flex item's
   1911 * final reflow, when we perform an incremental reflow of its flex container.
   1912 */
   1913 class CachedFinalReflowMetrics final {
   1914 public:
   1915  CachedFinalReflowMetrics(const ReflowInput& aReflowInput,
   1916                           const ReflowOutput& aReflowOutput)
   1917      : CachedFinalReflowMetrics(aReflowInput.GetWritingMode(), aReflowInput,
   1918                                 aReflowOutput) {}
   1919 
   1920  CachedFinalReflowMetrics(const FlexItem& aItem, const LogicalSize& aSize)
   1921      : mBorderPadding(aItem.BorderPadding().ConvertTo(
   1922            aItem.GetWritingMode(), aItem.ContainingBlockWM())),
   1923        mSize(aSize),
   1924        mTreatBSizeAsIndefinite(aItem.TreatBSizeAsIndefinite()) {}
   1925 
   1926  const LogicalSize& Size() const { return mSize; }
   1927  const LogicalMargin& BorderPadding() const { return mBorderPadding; }
   1928  bool TreatBSizeAsIndefinite() const { return mTreatBSizeAsIndefinite; }
   1929 
   1930 private:
   1931  // A convenience constructor with a WritingMode argument.
   1932  CachedFinalReflowMetrics(WritingMode aWM, const ReflowInput& aReflowInput,
   1933                           const ReflowOutput& aReflowOutput)
   1934      : mBorderPadding(aReflowInput.ComputedLogicalBorderPadding(aWM)),
   1935        mSize(aReflowOutput.Size(aWM) - mBorderPadding.Size(aWM)),
   1936        mTreatBSizeAsIndefinite(aReflowInput.mFlags.mTreatBSizeAsIndefinite) {}
   1937 
   1938  // The flex item's border and padding, in its own writing-mode, that it used
   1939  // used during its most recent "final reflow".
   1940  LogicalMargin mBorderPadding;
   1941 
   1942  // The flex item's content-box size, in its own writing-mode, that it used
   1943  // during its most recent "final reflow".
   1944  LogicalSize mSize;
   1945 
   1946  // True if the flex item's BSize was considered "indefinite" in its most
   1947  // recent "final reflow". (For a flex item "final reflow", this is fully
   1948  // determined by the mTreatBSizeAsIndefinite flag in ReflowInput. See the
   1949  // flag's documentation for more information.)
   1950  bool mTreatBSizeAsIndefinite;
   1951 };
   1952 
   1953 /**
   1954 * When we instantiate/update a CachedFlexItemData, this enum must be used to
   1955 * indicate the sort of reflow whose results we're capturing. This impacts
   1956 * what we cache & how we use the cached information.
   1957 */
   1958 enum class FlexItemReflowType {
   1959  // A reflow to measure the block-axis size of a flex item (as an input to the
   1960  // flex layout algorithm).
   1961  Measuring,
   1962 
   1963  // A reflow with the flex item's "final" size at the end of the flex layout
   1964  // algorithm.
   1965  Final,
   1966 };
   1967 
   1968 /**
   1969 * This class stores information about the conditions and results for the most
   1970 * recent ReflowChild call that we made on a given flex item.  This information
   1971 * helps us reason about whether we can assume that a subsequent ReflowChild()
   1972 * invocation is unnecessary & skippable.
   1973 */
   1974 class nsFlexContainerFrame::CachedFlexItemData {
   1975 public:
   1976  CachedFlexItemData(const ReflowInput& aReflowInput,
   1977                     const ReflowOutput& aReflowOutput,
   1978                     FlexItemReflowType aType) {
   1979    Update(aReflowInput, aReflowOutput, aType);
   1980  }
   1981 
   1982  // This method is intended to be called after we perform either a "measuring
   1983  // reflow" or a "final reflow" for a given flex item.
   1984  void Update(const ReflowInput& aReflowInput,
   1985              const ReflowOutput& aReflowOutput, FlexItemReflowType aType) {
   1986    if (aType == FlexItemReflowType::Measuring) {
   1987      mBAxisMeasurement.reset();
   1988      mBAxisMeasurement.emplace(aReflowInput, aReflowOutput);
   1989      // Clear any cached "last final reflow metrics", too, because now the most
   1990      // recent reflow was *not* a "final reflow".
   1991      mFinalReflowMetrics.reset();
   1992      return;
   1993    }
   1994 
   1995    MOZ_ASSERT(aType == FlexItemReflowType::Final);
   1996    mFinalReflowMetrics.reset();
   1997    mFinalReflowMetrics.emplace(aReflowInput, aReflowOutput);
   1998  }
   1999 
   2000  // This method is intended to be called for situations where we decide to
   2001  // skip a final reflow because we've just done a measuring reflow which left
   2002  // us (and our descendants) with the correct sizes. In this scenario, we
   2003  // still want to cache the size as if we did a final reflow (because we've
   2004  // determined that the recent measuring reflow was sufficient).  That way,
   2005  // our flex container can still skip a final reflow for this item in the
   2006  // future as long as conditions are right.
   2007  void Update(const FlexItem& aItem, const LogicalSize& aSize) {
   2008    MOZ_ASSERT(!mFinalReflowMetrics,
   2009               "This version of the method is only intended to be called when "
   2010               "the most recent reflow was a 'measuring reflow'; and that "
   2011               "should have cleared out mFinalReflowMetrics");
   2012 
   2013    mFinalReflowMetrics.reset();  // Just in case this assert^ fails.
   2014    mFinalReflowMetrics.emplace(aItem, aSize);
   2015  }
   2016 
   2017  // If the flex container needs a measuring reflow for the flex item, then the
   2018  // resulting block-axis measurements can be cached here.  If no measurement
   2019  // has been needed so far, then this member will be Nothing().
   2020  Maybe<CachedBAxisMeasurement> mBAxisMeasurement;
   2021 
   2022  // The metrics that the corresponding flex item used in its most recent
   2023  // "final reflow". (Note: the assumption here is that this reflow was this
   2024  // item's most recent reflow of any type.  If the item ends up undergoing a
   2025  // subsequent measuring reflow, then this value needs to be cleared, because
   2026  // at that point it's no longer an accurate way of reasoning about the
   2027  // current state of the frame tree.)
   2028  Maybe<CachedFinalReflowMetrics> mFinalReflowMetrics;
   2029 
   2030  // Instances of this class are stored under this frame property, on
   2031  // frames that are flex items:
   2032  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, CachedFlexItemData)
   2033 };
   2034 
   2035 void nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(
   2036    nsIFrame* aItemFrame) {
   2037  MOZ_ASSERT(aItemFrame->IsFlexItem());
   2038  if (auto* cache = aItemFrame->GetProperty(CachedFlexItemData::Prop())) {
   2039    cache->mBAxisMeasurement.reset();
   2040    cache->mFinalReflowMetrics.reset();
   2041  }
   2042 }
   2043 
   2044 const CachedBAxisMeasurement& nsFlexContainerFrame::MeasureBSizeForFlexItem(
   2045    FlexItem& aItem, ReflowInput& aChildReflowInput) {
   2046  auto* cachedData = aItem.Frame()->GetProperty(CachedFlexItemData::Prop());
   2047 
   2048  if (cachedData && cachedData->mBAxisMeasurement) {
   2049    if (!aItem.Frame()->IsSubtreeDirty() &&
   2050        cachedData->mBAxisMeasurement->IsValidFor(aChildReflowInput)) {
   2051      FLEX_ITEM_LOG(aItem.Frame(),
   2052                    "[perf] Accepted cached measurement: block-size %d",
   2053                    cachedData->mBAxisMeasurement->BSize());
   2054      return *(cachedData->mBAxisMeasurement);
   2055    }
   2056    FLEX_ITEM_LOG(aItem.Frame(),
   2057                  "[perf] Rejected cached measurement: block-size %d",
   2058                  cachedData->mBAxisMeasurement->BSize());
   2059  } else {
   2060    FLEX_ITEM_LOG(aItem.Frame(), "[perf] No cached measurement");
   2061  }
   2062 
   2063  // CachedFlexItemData is stored in item's writing mode, so we pass
   2064  // aChildReflowInput into ReflowOutput's constructor.
   2065  ReflowOutput childReflowOutput(aChildReflowInput);
   2066  nsReflowStatus childStatus;
   2067 
   2068  const ReflowChildFlags flags = ReflowChildFlags::NoMoveFrame;
   2069  const WritingMode outerWM = GetWritingMode();
   2070  const LogicalPoint dummyPosition(outerWM);
   2071  const nsSize dummyContainerSize;
   2072 
   2073  // We use NoMoveFrame, so the position and container size used here are
   2074  // unimportant.
   2075  ReflowChild(aItem.Frame(), PresContext(), childReflowOutput,
   2076              aChildReflowInput, outerWM, dummyPosition, dummyContainerSize,
   2077              flags, childStatus);
   2078  aItem.SetHadMeasuringReflow();
   2079 
   2080  // We always use unconstrained available block-size to measure flex items,
   2081  // which means they should always complete.
   2082  MOZ_ASSERT(childStatus.IsComplete(),
   2083             "We gave flex item unconstrained available block-size, so it "
   2084             "should be complete");
   2085 
   2086  // Tell the child we're done with its initial reflow.
   2087  // (Necessary for e.g. GetBaseline() to work below w/out asserting)
   2088  FinishReflowChild(aItem.Frame(), PresContext(), childReflowOutput,
   2089                    &aChildReflowInput, outerWM, dummyPosition,
   2090                    dummyContainerSize, flags);
   2091 
   2092  aItem.SetAscent(childReflowOutput.BlockStartAscent());
   2093 
   2094  // Update (or add) our cached measurement, so that we can hopefully skip this
   2095  // measuring reflow the next time around:
   2096  if (cachedData) {
   2097    cachedData->Update(aChildReflowInput, childReflowOutput,
   2098                       FlexItemReflowType::Measuring);
   2099  } else {
   2100    cachedData = new CachedFlexItemData(aChildReflowInput, childReflowOutput,
   2101                                        FlexItemReflowType::Measuring);
   2102    aItem.Frame()->SetProperty(CachedFlexItemData::Prop(), cachedData);
   2103  }
   2104  return *(cachedData->mBAxisMeasurement);
   2105 }
   2106 
   2107 /* virtual */
   2108 void nsFlexContainerFrame::MarkIntrinsicISizesDirty() {
   2109  mCachedIntrinsicSizes.Clear();
   2110  nsContainerFrame::MarkIntrinsicISizesDirty();
   2111 }
   2112 
   2113 nscoord nsFlexContainerFrame::MeasureFlexItemContentBSize(
   2114    FlexItem& aFlexItem, bool aForceBResizeForMeasuringReflow,
   2115    const ReflowInput& aParentReflowInput) {
   2116  FLEX_ITEM_LOG(aFlexItem.Frame(), "Measuring item's content block-size");
   2117 
   2118  // Set up a reflow input for measuring the flex item's content block-size:
   2119  WritingMode wm = aFlexItem.Frame()->GetWritingMode();
   2120  LogicalSize availSize = aParentReflowInput.ComputedSize(wm);
   2121  availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
   2122 
   2123  StyleSizeOverrides sizeOverrides;
   2124  if (aFlexItem.IsStretched()) {
   2125    sizeOverrides.mStyleISize.emplace(aFlexItem.StyleCrossSize());
   2126    FLEX_LOGV("Cross size override: %d", aFlexItem.CrossSize());
   2127  }
   2128  sizeOverrides.mStyleBSize.emplace(StyleSize::Auto());
   2129 
   2130  ReflowInput childRIForMeasuringBSize(
   2131      PresContext(), aParentReflowInput, aFlexItem.Frame(), availSize,
   2132      Nothing(), {}, sizeOverrides, {ComputeSizeFlag::ShrinkWrap});
   2133 
   2134  // When measuring flex item's content block-size, disregard the item's
   2135  // min-block-size and max-block-size by resetting both to to their
   2136  // unconstraining (extreme) values. The flexbox layout algorithm does still
   2137  // explicitly clamp both sizes when resolving the target main size.
   2138  childRIForMeasuringBSize.SetComputedMinBSize(0);
   2139  childRIForMeasuringBSize.SetComputedMaxBSize(NS_UNCONSTRAINEDSIZE);
   2140 
   2141  if (aForceBResizeForMeasuringReflow) {
   2142    childRIForMeasuringBSize.SetBResize(true);
   2143    // Not 100% sure this is needed, but be conservative for now:
   2144    childRIForMeasuringBSize.SetBResizeForPercentages(true);
   2145  }
   2146 
   2147  const CachedBAxisMeasurement& measurement =
   2148      MeasureBSizeForFlexItem(aFlexItem, childRIForMeasuringBSize);
   2149 
   2150  return measurement.BSize();
   2151 }
   2152 
   2153 FlexItem::FlexItem(ReflowInput& aFlexItemReflowInput, float aFlexGrow,
   2154                   float aFlexShrink, nscoord aFlexBaseSize,
   2155                   nscoord aMainMinSize, nscoord aMainMaxSize,
   2156                   nscoord aTentativeCrossSize, nscoord aCrossMinSize,
   2157                   nscoord aCrossMaxSize,
   2158                   const FlexboxAxisTracker& aAxisTracker)
   2159    : mFrame(aFlexItemReflowInput.mFrame),
   2160      mFlexGrow(aFlexGrow),
   2161      mFlexShrink(aFlexShrink),
   2162      mAspectRatio(mFrame->GetAspectRatio()),
   2163      mWM(aFlexItemReflowInput.GetWritingMode()),
   2164      mCBWM(aAxisTracker.GetWritingMode()),
   2165      mMainAxis(aAxisTracker.MainAxis()),
   2166      mBorderPadding(aFlexItemReflowInput.ComputedLogicalBorderPadding(mCBWM)),
   2167      mMargin(aFlexItemReflowInput.ComputedLogicalMargin(mCBWM)),
   2168      mMainMinSize(aMainMinSize),
   2169      mMainMaxSize(aMainMaxSize),
   2170      mCrossMinSize(aCrossMinSize),
   2171      mCrossMaxSize(aCrossMaxSize),
   2172      mCrossSize(aTentativeCrossSize),
   2173      mIsInlineAxisMainAxis(aAxisTracker.IsInlineAxisMainAxis(mWM)),
   2174      mNeedsMinSizeAutoResolution(IsMinSizeAutoResolutionNeeded())
   2175 // mAlignSelf, mHasAnyAutoMargin see below
   2176 {
   2177  MOZ_ASSERT(mFrame, "expecting a non-null child frame");
   2178  MOZ_ASSERT(!mFrame->IsPlaceholderFrame(),
   2179             "placeholder frames should not be treated as flex items");
   2180  MOZ_ASSERT(mFrame->IsFlexItem(), "mFrame must be a flex item!");
   2181  MOZ_ASSERT(mIsInlineAxisMainAxis ==
   2182                 nsFlexContainerFrame::IsItemInlineAxisMainAxis(mFrame),
   2183             "public API should be consistent with internal state (about "
   2184             "whether flex item's inline axis is flex container's main axis)");
   2185 
   2186  const auto* container =
   2187      static_cast<nsFlexContainerFrame*>(mFrame->GetParent());
   2188  std::tie(mAlignSelf, mAlignSelfFlags) =
   2189      container->UsedAlignSelfAndFlagsForItem(mFrame);
   2190 
   2191  // Our main-size is considered definite if any of these are true:
   2192  // (a) main axis is the item's inline axis.
   2193  // (b) flex container has definite main size.
   2194  // (c) flex item has a definite flex basis.
   2195  //
   2196  // Hence, we need to take care to treat the final main-size as *indefinite*
   2197  // if none of these conditions are satisfied.
   2198  if (mIsInlineAxisMainAxis) {
   2199    // The item's block-axis is the flex container's cross axis. We don't need
   2200    // any special handling to treat cross sizes as indefinite, because the
   2201    // cases where we stomp on the cross size with a definite value are all...
   2202    // - situations where the spec requires us to treat the cross size as
   2203    // definite; specifically, `align-self:stretch` whose cross size is
   2204    // definite.
   2205    // - situations where definiteness doesn't matter (e.g. for an element with
   2206    // an aspect ratio, which for now are all leaf nodes and hence
   2207    // can't have any percent-height descendants that would care about the
   2208    // definiteness of its size. (Once bug 1528375 is fixed, we might need to
   2209    // be more careful about definite vs. indefinite sizing on flex items with
   2210    // aspect ratios.)
   2211    mTreatBSizeAsIndefinite = false;
   2212  } else {
   2213    // The item's block-axis is the flex container's main axis. So, the flex
   2214    // item's main size is its BSize, and is considered definite under certain
   2215    // conditions laid out for definite flex-item main-sizes in the spec.
   2216    const ReflowInput* containerRI = aFlexItemReflowInput.mParentReflowInput;
   2217    if (aAxisTracker.IsRowOriented() ||
   2218        (containerRI->ComputedBSize() != NS_UNCONSTRAINEDSIZE &&
   2219         !containerRI->mFlags.mTreatBSizeAsIndefinite)) {
   2220      // The flex *container* has a definite main-size (either by being
   2221      // row-oriented [and using its own inline size which is by definition
   2222      // definite, or by being column-oriented and having a definite
   2223      // block-size).  The spec says this means all of the flex items'
   2224      // post-flexing main sizes should *also* be treated as definite.
   2225      mTreatBSizeAsIndefinite = false;
   2226    } else if (aFlexBaseSize != NS_UNCONSTRAINEDSIZE) {
   2227      // The flex item has a definite flex basis, which we'll treat as making
   2228      // its main-size definite.
   2229      mTreatBSizeAsIndefinite = false;
   2230    } else {
   2231      // Otherwise, we have to treat the item's BSize as indefinite.
   2232      mTreatBSizeAsIndefinite = true;
   2233    }
   2234  }
   2235 
   2236  SetFlexBaseSizeAndMainSize(aFlexBaseSize);
   2237 
   2238  const nsStyleMargin* styleMargin = aFlexItemReflowInput.mStyleMargin;
   2239  const auto anchorResolutionParams =
   2240      AnchorPosResolutionParams::From(&aFlexItemReflowInput);
   2241  mHasAnyAutoMargin =
   2242      styleMargin->HasInlineAxisAuto(mCBWM, anchorResolutionParams) ||
   2243      styleMargin->HasBlockAxisAuto(mCBWM, anchorResolutionParams);
   2244 
   2245  // Assert that any "auto" margin components are set to 0.
   2246  // (We'll resolve them later; until then, we want to treat them as 0-sized.)
   2247 #ifdef DEBUG
   2248  {
   2249    for (const auto side : LogicalSides::All) {
   2250      if (styleMargin->GetMargin(side, mCBWM, anchorResolutionParams)
   2251              ->IsAuto()) {
   2252        MOZ_ASSERT(GetMarginComponentForSide(side) == 0,
   2253                   "Someone else tried to resolve our auto margin");
   2254      }
   2255    }
   2256  }
   2257 #endif  // DEBUG
   2258 
   2259  if (mAlignSelf == StyleAlignFlags::BASELINE ||
   2260      mAlignSelf == StyleAlignFlags::LAST_BASELINE) {
   2261    // Check which of the item's baselines we're meant to use (first vs. last)
   2262    const bool usingItemFirstBaseline =
   2263        (mAlignSelf == StyleAlignFlags::BASELINE);
   2264    if (IsBlockAxisCrossAxis()) {
   2265      // The flex item wants to be aligned in the cross axis using one of its
   2266      // baselines; and the cross axis is the item's block axis, so
   2267      // baseline-alignment in that axis makes sense.
   2268 
   2269      // To determine the item's baseline sharing group, we check whether the
   2270      // item's block axis has the same vs. opposite flow direction as the
   2271      // corresponding LogicalAxis on the flex container.  We do this by
   2272      // getting the physical side that corresponds to these axes' "logical
   2273      // start" sides, and we compare those physical sides to find out if
   2274      // they're the same vs. opposite.
   2275      mozilla::Side itemBlockStartSide = mWM.PhysicalSide(LogicalSide::BStart);
   2276 
   2277      // (Note: this is *not* the "flex-start" side; rather, it's the *logical*
   2278      // i.e. WM-relative block-start or inline-start side.)
   2279      mozilla::Side containerStartSideInCrossAxis = mCBWM.PhysicalSide(
   2280          MakeLogicalSide(aAxisTracker.CrossAxis(), LogicalEdge::Start));
   2281 
   2282      // We already know these two Sides (the item's block-start and the
   2283      // container's 'logical start' side for its cross axis) are in the same
   2284      // physical axis, since we're inside of a check for
   2285      // FlexItem::IsBlockAxisCrossAxis().  So these two Sides must be either
   2286      // the same physical side or opposite from each other.  If the Sides are
   2287      // the same, then the flow direction is the same, which means the item's
   2288      // {first,last} baseline participates in the {first,last}
   2289      // baseline-sharing group in its FlexLine.  Otherwise, the flow direction
   2290      // is opposite, and so the item's {first,last} baseline participates in
   2291      // the opposite i.e. {last,first} baseline-sharing group.  This is
   2292      // roughly per css-align-3 section 9.2, specifically the definition of
   2293      // what makes baseline alignment preferences "compatible".
   2294      bool itemBlockAxisFlowDirMatchesContainer =
   2295          (itemBlockStartSide == containerStartSideInCrossAxis);
   2296      mBaselineSharingGroup =
   2297          (itemBlockAxisFlowDirMatchesContainer == usingItemFirstBaseline)
   2298              ? BaselineSharingGroup::First
   2299              : BaselineSharingGroup::Last;
   2300    } else {
   2301      // The flex item wants to be aligned in the cross axis using one of its
   2302      // baselines, but we cannot get its baseline because the FlexItem's block
   2303      // axis is *orthogonal* to the container's cross axis. To handle this, we
   2304      // are supposed to synthesize a baseline from the item's border box and
   2305      // using that for baseline alignment.
   2306      mBaselineSharingGroup = usingItemFirstBaseline
   2307                                  ? BaselineSharingGroup::First
   2308                                  : BaselineSharingGroup::Last;
   2309    }
   2310  }
   2311 }
   2312 
   2313 // Simplified constructor for creating a special "strut" FlexItem, for a child
   2314 // with visibility:collapse. The strut has 0 main-size, and it only exists to
   2315 // impose a minimum cross size on whichever FlexLine it ends up in.
   2316 FlexItem::FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize,
   2317                   WritingMode aContainerWM,
   2318                   const FlexboxAxisTracker& aAxisTracker)
   2319    : mFrame(aChildFrame),
   2320      mWM(aChildFrame->GetWritingMode()),
   2321      mCBWM(aContainerWM),
   2322      mMainAxis(aAxisTracker.MainAxis()),
   2323      mBorderPadding(mCBWM),
   2324      mMargin(mCBWM),
   2325      mCrossSize(aCrossSize),
   2326      // Struts don't do layout, so its WM doesn't matter at this point. So, we
   2327      // just share container's WM for simplicity:
   2328      mIsFrozen(true),
   2329      mIsStrut(true),  // (this is the constructor for making struts, after all)
   2330      mAlignSelf(StyleAlignFlags::FLEX_START) {
   2331  MOZ_ASSERT(mFrame, "expecting a non-null child frame");
   2332  MOZ_ASSERT(mFrame->StyleVisibility()->IsCollapse(),
   2333             "Should only make struts for children with 'visibility:collapse'");
   2334  MOZ_ASSERT(!mFrame->IsPlaceholderFrame(),
   2335             "placeholder frames should not be treated as flex items");
   2336  MOZ_ASSERT(!mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
   2337             "out-of-flow frames should not be treated as flex items");
   2338 }
   2339 
   2340 bool FlexItem::IsMinSizeAutoResolutionNeeded() const {
   2341  // We'll need special behavior for "min-[width|height]:auto" (whichever is in
   2342  // the flex container's main axis) iff:
   2343  // (a) its computed value is "auto", and
   2344  // (b) the item is *not* a scroll container. (A scroll container's automatic
   2345  //     minimum size is zero.)
   2346  // https://drafts.csswg.org/css-flexbox-1/#min-size-auto
   2347  //
   2348  // Note that the scroll container case is redefined to be looking at the
   2349  // computed value instead, see https://github.com/w3c/csswg-drafts/issues/7714
   2350  const auto mainMinSize = Frame()->StylePosition()->MinSize(
   2351      MainAxis(), ContainingBlockWM(),
   2352      AnchorPosResolutionParams::From(Frame()));
   2353 
   2354  // "min-{height,width}:stretch" never produces an automatic minimum size. You
   2355  // might think it would result in an automatic min-size if the containing
   2356  // block size is indefinite, but "stretch" is instead treated as 0px in that
   2357  // case rather than auto. This WPT requires this behavior:
   2358  // https://wpt.live/css/css-sizing/stretch/indefinite-4.html More details &
   2359  // discussion here: https://github.com/w3c/csswg-drafts/issues/11006
   2360  if (mainMinSize->BehavesLikeStretchOnBlockAxis()) {
   2361    return false;
   2362  }
   2363  return IsAutoOrEnumOnBSize(*mainMinSize, IsInlineAxisMainAxis()) &&
   2364         !Frame()->StyleDisplay()->IsScrollableOverflow();
   2365 }
   2366 
   2367 Maybe<nscoord> FlexItem::MeasuredBSize() const {
   2368  auto* cachedData =
   2369      Frame()->FirstInFlow()->GetProperty(CachedFlexItemData::Prop());
   2370  if (!cachedData || !cachedData->mBAxisMeasurement) {
   2371    return Nothing();
   2372  }
   2373  return Some(cachedData->mBAxisMeasurement->BSize());
   2374 }
   2375 
   2376 nscoord FlexItem::BaselineOffsetFromOuterCrossEdge(
   2377    mozilla::Side aStartSide, bool aUseFirstLineBaseline) const {
   2378  // NOTE:
   2379  //  * We only use baselines for aligning in the flex container's cross axis.
   2380  //  * Baselines are a measurement in the item's block axis.
   2381  if (IsBlockAxisMainAxis()) {
   2382    // We get here if the item's block axis is *orthogonal* the container's
   2383    // cross axis. For example, a flex item with writing-mode:horizontal-tb in a
   2384    // column-oriented flex container. We need to synthesize the item's baseline
   2385    // from its border-box edge.
   2386    const bool isMainAxisHorizontal =
   2387        mCBWM.PhysicalAxis(MainAxis()) == PhysicalAxis::Horizontal;
   2388 
   2389    // When the main axis is horizontal, the synthesized baseline is the bottom
   2390    // edge of the item's border-box. Otherwise, when the main axis is vertical,
   2391    // the left edge. This is for compatibility with Google Chrome.
   2392    nscoord marginTopOrLeftToBaseline =
   2393        isMainAxisHorizontal ? PhysicalMargin().top : PhysicalMargin().left;
   2394    if (mCBWM.IsAlphabeticalBaseline()) {
   2395      marginTopOrLeftToBaseline += (isMainAxisHorizontal ? CrossSize() : 0);
   2396    } else {
   2397      MOZ_ASSERT(mCBWM.IsCentralBaseline());
   2398      marginTopOrLeftToBaseline += CrossSize() / 2;
   2399    }
   2400 
   2401    return aStartSide == mozilla::eSideTop || aStartSide == mozilla::eSideLeft
   2402               ? marginTopOrLeftToBaseline
   2403               : OuterCrossSize() - marginTopOrLeftToBaseline;
   2404  }
   2405 
   2406  // We get here if the item's block axis is parallel (or antiparallel) to the
   2407  // container's cross axis. We call ResolvedAscent() to get the item's
   2408  // baseline. If the item has no baseline, the method will synthesize one from
   2409  // the border-box edge.
   2410  MOZ_ASSERT(IsBlockAxisCrossAxis(),
   2411             "Only expecting to be doing baseline computations when the "
   2412             "cross axis is the block axis");
   2413 
   2414  mozilla::Side itemBlockStartSide = mWM.PhysicalSide(LogicalSide::BStart);
   2415 
   2416  nscoord marginBStartToBaseline = ResolvedAscent(aUseFirstLineBaseline) +
   2417                                   PhysicalMargin().Side(itemBlockStartSide);
   2418 
   2419  return (aStartSide == itemBlockStartSide)
   2420             ? marginBStartToBaseline
   2421             : OuterCrossSize() - marginBStartToBaseline;
   2422 }
   2423 
   2424 bool FlexItem::IsCrossSizeAuto() const {
   2425  const auto* styleFrame = nsLayoutUtils::GetStyleFrame(mFrame);
   2426  const nsStylePosition* stylePos = styleFrame->StylePosition();
   2427  const auto anchorResolutionParams =
   2428      AnchorPosResolutionParams::From(styleFrame);
   2429  // Check whichever component is in the flex container's cross axis.
   2430  // (IsInlineAxisCrossAxis() tells us whether that's our ISize or BSize, in
   2431  // terms of our own WritingMode, mWM.)
   2432  return IsInlineAxisCrossAxis()
   2433             ? stylePos->ISize(mWM, anchorResolutionParams)->IsAuto()
   2434             : stylePos->BSize(mWM, anchorResolutionParams)->IsAuto();
   2435 }
   2436 
   2437 bool FlexItem::IsCrossSizeDefinite(const ReflowInput& aItemReflowInput) const {
   2438  if (IsStretched()) {
   2439    // Definite cross-size, imposed via 'align-self:stretch' & flex container.
   2440    return true;
   2441  }
   2442 
   2443  const nsStylePosition* pos = aItemReflowInput.mStylePosition;
   2444  const auto anchorResolutionParams =
   2445      AnchorPosResolutionParams::From(&aItemReflowInput);
   2446  const auto itemWM = GetWritingMode();
   2447 
   2448  // The logic here should be similar to the logic for isAutoISize/isAutoBSize
   2449  // in nsContainerFrame::ComputeSizeWithIntrinsicDimensions().
   2450  if (IsInlineAxisCrossAxis()) {
   2451    return !pos->ISize(itemWM, anchorResolutionParams)->IsAuto();
   2452  }
   2453 
   2454  nscoord cbBSize = aItemReflowInput.mContainingBlockSize.BSize(itemWM);
   2455  return !nsLayoutUtils::IsAutoBSize(
   2456      *pos->BSize(itemWM, anchorResolutionParams), cbBSize);
   2457 }
   2458 
   2459 void FlexItem::ResolveFlexBaseSizeFromAspectRatio(
   2460    const ReflowInput& aItemReflowInput) {
   2461  // This implements the Flex Layout Algorithm Step 3B:
   2462  // https://drafts.csswg.org/css-flexbox-1/#algo-main-item
   2463  // If the flex item has ...
   2464  //  - an aspect ratio,
   2465  //  - a [used] flex-basis of 'content', and
   2466  //  - a definite cross size
   2467  // then the flex base size is calculated from its inner cross size and the
   2468  // flex item's preferred aspect ratio.
   2469  if (HasAspectRatio() &&
   2470      nsFlexContainerFrame::IsUsedFlexBasisContent(
   2471          aItemReflowInput.mStylePosition->mFlexBasis,
   2472          *aItemReflowInput.mStylePosition->Size(
   2473              MainAxis(), mCBWM,
   2474              AnchorPosResolutionParams::From(&aItemReflowInput))) &&
   2475      IsCrossSizeDefinite(aItemReflowInput)) {
   2476    const LogicalSize contentBoxSizeToBoxSizingAdjust =
   2477        aItemReflowInput.mStylePosition->mBoxSizing == StyleBoxSizing::Border
   2478            ? BorderPadding().Size(mCBWM)
   2479            : LogicalSize(mCBWM);
   2480    const nscoord mainSizeFromRatio = mAspectRatio.ComputeRatioDependentSize(
   2481        MainAxis(), mCBWM, CrossSize(), contentBoxSizeToBoxSizingAdjust);
   2482    SetFlexBaseSizeAndMainSize(mainSizeFromRatio);
   2483  }
   2484 }
   2485 
   2486 uint32_t FlexItem::NumAutoMarginsInAxis(LogicalAxis aAxis) const {
   2487  uint32_t numAutoMargins = 0;
   2488  const auto* styleMargin = mFrame->StyleMargin();
   2489  const auto anchorResolutionParams = AnchorPosResolutionParams::From(mFrame);
   2490  for (const auto edge : {LogicalEdge::Start, LogicalEdge::End}) {
   2491    const auto side = MakeLogicalSide(aAxis, edge);
   2492    if (styleMargin->GetMargin(side, mCBWM, anchorResolutionParams)->IsAuto()) {
   2493      numAutoMargins++;
   2494    }
   2495  }
   2496 
   2497  // Mostly for clarity:
   2498  MOZ_ASSERT(numAutoMargins <= 2,
   2499             "We're just looking at one item along one dimension, so we "
   2500             "should only have examined 2 margins");
   2501 
   2502  return numAutoMargins;
   2503 }
   2504 
   2505 bool FlexItem::CanMainSizeInfluenceCrossSize() const {
   2506  if (mIsStretched) {
   2507    // We've already had our cross-size stretched for "align-self:stretch").
   2508    // The container is imposing its cross size on us.
   2509    return false;
   2510  }
   2511 
   2512  if (mIsStrut) {
   2513    // Struts (for visibility:collapse items) have a predetermined size;
   2514    // no need to measure anything.
   2515    return false;
   2516  }
   2517 
   2518  if (HasAspectRatio()) {
   2519    // For flex items that have an aspect ratio (and maintain it, i.e. are
   2520    // not stretched, which we already checked above): changes to main-size
   2521    // *do* influence the cross size.
   2522    return true;
   2523  }
   2524 
   2525  if (IsInlineAxisCrossAxis()) {
   2526    // If we get here, this function is really asking: "can changes to this
   2527    // item's block size have an influence on its inline size"?
   2528 
   2529    // If a flex item's intrinsic inline size or its descendants' inline size
   2530    // contributions depend on the item's block size, the answer is "yes".
   2531    if (mFrame->HasAnyStateBits(
   2532            NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
   2533      return true;
   2534    }
   2535    // For blocks and tables, the answer is "no" (aside from the above special
   2536    // case).
   2537    if (mFrame->IsBlockFrame() || mFrame->IsTableWrapperFrame()) {
   2538      // XXXdholbert (Maybe use an IsFrameOfType query or something more
   2539      // general to test this across all frame types? For now, I'm just
   2540      // optimizing for block and table, since those are common containers that
   2541      // can contain arbitrarily-large subtrees (and that reliably have ISize
   2542      // being unaffected by BSize, per CSS2).  So optimizing away needless
   2543      // relayout is possible & especially valuable for these containers.)
   2544      return false;
   2545    }
   2546    // Other opt-outs can go here, as they're identified as being useful
   2547    // (particularly for containers where an extra reflow is expensive). But in
   2548    // general, we have to assume that a flexed BSize *could* influence the
   2549    // ISize. Some examples where this can definitely happen:
   2550    // * Intrinsically-sized multicol with fixed-ISize columns, which adds
   2551    // columns (i.e. grows in inline axis) depending on its block size.
   2552    // * Intrinsically-sized multi-line column-oriented flex container, which
   2553    // adds flex lines (i.e. grows in inline axis) depending on its block size.
   2554  }
   2555 
   2556  // Default assumption, if we haven't proven otherwise: the resolved main size
   2557  // *can* change the cross size.
   2558  return true;
   2559 }
   2560 
   2561 nscoord FlexItem::ClampMainSizeViaCrossAxisConstraints(
   2562    nscoord aMainSize, const ReflowInput& aItemReflowInput) const {
   2563  MOZ_ASSERT(HasAspectRatio(), "Caller should've checked the ratio is valid!");
   2564 
   2565  const LogicalSize contentBoxSizeToBoxSizingAdjust =
   2566      aItemReflowInput.mStylePosition->mBoxSizing == StyleBoxSizing::Border
   2567          ? BorderPadding().Size(mCBWM)
   2568          : LogicalSize(mCBWM);
   2569 
   2570  const nscoord mainMinSizeFromRatio = mAspectRatio.ComputeRatioDependentSize(
   2571      MainAxis(), mCBWM, CrossMinSize(), contentBoxSizeToBoxSizingAdjust);
   2572  nscoord clampedMainSize = std::max(aMainSize, mainMinSizeFromRatio);
   2573 
   2574  if (CrossMaxSize() != NS_UNCONSTRAINEDSIZE) {
   2575    const nscoord mainMaxSizeFromRatio = mAspectRatio.ComputeRatioDependentSize(
   2576        MainAxis(), mCBWM, CrossMaxSize(), contentBoxSizeToBoxSizingAdjust);
   2577    clampedMainSize = std::min(clampedMainSize, mainMaxSizeFromRatio);
   2578  }
   2579 
   2580  return clampedMainSize;
   2581 }
   2582 
   2583 /**
   2584 * Returns true if aFrame or any of its children have the
   2585 * NS_FRAME_CONTAINS_RELATIVE_BSIZE flag set -- i.e. if any of these frames (or
   2586 * their descendants) might have a relative-BSize dependency on aFrame (or its
   2587 * ancestors).
   2588 */
   2589 static bool FrameHasRelativeBSizeDependency(nsIFrame* aFrame) {
   2590  if (aFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
   2591    return true;
   2592  }
   2593  for (const auto& childList : aFrame->ChildLists()) {
   2594    for (nsIFrame* childFrame : childList.mList) {
   2595      if (childFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
   2596        return true;
   2597      }
   2598    }
   2599  }
   2600  return false;
   2601 }
   2602 
   2603 bool FlexItem::NeedsFinalReflow(const ReflowInput& aParentReflowInput) const {
   2604  if (!StaticPrefs::layout_flexbox_item_final_reflow_optimization_enabled()) {
   2605    FLEX_ITEM_LOG(mFrame,
   2606                  "[perf] Item needed a final reflow due to optimization being "
   2607                  "disabled via the preference");
   2608    return true;
   2609  }
   2610 
   2611  // NOTE: We can have continuations from an earlier constrained reflow.
   2612  if (mFrame->GetPrevInFlow() || mFrame->GetNextInFlow()) {
   2613    // This is an item has continuation(s). Reflow it.
   2614    FLEX_ITEM_LOG(mFrame,
   2615                  "[frag] Item needed a final reflow due to continuation(s)");
   2616    return true;
   2617  }
   2618 
   2619  // A flex item can grow its block-size in a fragmented context if there's any
   2620  // force break within it (bug 1663079), or if it has a repeated table header
   2621  // or footer (bug 1744363). We currently always reflow it.
   2622  //
   2623  // Bug 1815294: investigate if we can design a more specific condition to
   2624  // prevent triggering O(n^2) behavior when printing a deeply-nested flex
   2625  // container.
   2626  if (aParentReflowInput.IsInFragmentedContext()) {
   2627    FLEX_ITEM_LOG(mFrame,
   2628                  "[frag] Item needed both a measuring reflow and a final "
   2629                  "reflow due to being in a fragmented context");
   2630    return true;
   2631  }
   2632 
   2633  // Flex item's final content-box size (in terms of its own writing-mode):
   2634  const LogicalSize finalSize = mIsInlineAxisMainAxis
   2635                                    ? LogicalSize(mWM, mMainSize, mCrossSize)
   2636                                    : LogicalSize(mWM, mCrossSize, mMainSize);
   2637 
   2638  if (HadMeasuringReflow()) {
   2639    // We've already reflowed this flex item once, to measure it. In that
   2640    // reflow, did its frame happen to end up with the correct final size
   2641    // that the flex container would like it to have?
   2642    if (finalSize != mFrame->ContentSize(mWM)) {
   2643      // The measuring reflow left the item with a different size than its
   2644      // final flexed size. So, we need to reflow to give it the correct size.
   2645      FLEX_ITEM_LOG(mFrame,
   2646                    "[perf] Item needed both a measuring reflow and a final "
   2647                    "reflow due to measured size disagreeing with final size");
   2648      return true;
   2649    }
   2650 
   2651    if (FrameHasRelativeBSizeDependency(mFrame)) {
   2652      // This item has descendants with relative BSizes who may care that its
   2653      // size may now be considered "definite" in the final reflow (whereas it
   2654      // was indefinite during the measuring reflow).
   2655      FLEX_ITEM_LOG(mFrame,
   2656                    "[perf] Item needed both a measuring reflow and a final "
   2657                    "reflow due to BSize potentially becoming definite");
   2658      return true;
   2659    }
   2660 
   2661    // If we get here, then this flex item had a measuring reflow, it left us
   2662    // with the correct size, none of its descendants care that its BSize may
   2663    // now be considered definite, and it can fit into the available block-size.
   2664    // So it doesn't need a final reflow.
   2665    //
   2666    // We now cache this size as if we had done a final reflow (because we've
   2667    // determined that the measuring reflow was effectively equivalent).  This
   2668    // way, in our next time through flex layout, we may be able to skip both
   2669    // the measuring reflow *and* the final reflow (if conditions are the same
   2670    // as they are now).
   2671    if (auto* cache = mFrame->GetProperty(CachedFlexItemData::Prop())) {
   2672      cache->Update(*this, finalSize);
   2673    }
   2674 
   2675    return false;
   2676  }
   2677 
   2678  // This item didn't receive a measuring reflow (at least, not during this
   2679  // reflow of our flex container).  We may still be able to skip reflowing it
   2680  // (i.e. return false from this function), if its subtree is clean & its most
   2681  // recent "final reflow" had it at the correct content-box size &
   2682  // definiteness.
   2683  // Let's check for each condition that would still require us to reflow:
   2684  if (mFrame->IsSubtreeDirty()) {
   2685    FLEX_ITEM_LOG(
   2686        mFrame,
   2687        "[perf] Item needed a final reflow due to its subtree being dirty");
   2688    return true;
   2689  }
   2690 
   2691  // Cool; this item & its subtree haven't experienced any style/content
   2692  // changes that would automatically require a reflow.
   2693 
   2694  // Did we cache the metrics from its most recent "final reflow"?
   2695  auto* cache = mFrame->GetProperty(CachedFlexItemData::Prop());
   2696  if (!cache || !cache->mFinalReflowMetrics) {
   2697    FLEX_ITEM_LOG(mFrame,
   2698                  "[perf] Item needed a final reflow due to lacking a cached "
   2699                  "mFinalReflowMetrics (maybe cache was cleared)");
   2700    return true;
   2701  }
   2702 
   2703  // Does the cached size match our current size?
   2704  if (cache->mFinalReflowMetrics->Size() != finalSize) {
   2705    FLEX_ITEM_LOG(mFrame,
   2706                  "[perf] Item needed a final reflow due to having a different "
   2707                  "content box size vs. its most recent final reflow");
   2708    return true;
   2709  }
   2710 
   2711  // Does the cached border and padding match our current ones?
   2712  //
   2713  // Note: this is just to detect cases where we have a percent padding whose
   2714  // basis has changed. Any other sort of change to BorderPadding() (e.g. a new
   2715  // specified value) should result in the frame being marked dirty via proper
   2716  // change hint (see nsStylePadding::CalcDifference()), which will force it to
   2717  // reflow.
   2718  if (cache->mFinalReflowMetrics->BorderPadding() !=
   2719      BorderPadding().ConvertTo(mWM, mCBWM)) {
   2720    FLEX_ITEM_LOG(mFrame,
   2721                  "[perf] Item needed a final reflow due to having a different "
   2722                  "border and padding vs. its most recent final reflow");
   2723    return true;
   2724  }
   2725 
   2726  // The flex container is giving this flex item the same size that the item
   2727  // had on its most recent "final reflow". But if its definiteness changed and
   2728  // one of the descendants cares, then it would still need a reflow.
   2729  if (cache->mFinalReflowMetrics->TreatBSizeAsIndefinite() !=
   2730          mTreatBSizeAsIndefinite &&
   2731      FrameHasRelativeBSizeDependency(mFrame)) {
   2732    FLEX_ITEM_LOG(mFrame,
   2733                  "[perf] Item needed a final reflow due to having its BSize "
   2734                  "change definiteness & having a rel-BSize child");
   2735    return true;
   2736  }
   2737 
   2738  // If we get here, we can skip the final reflow! (The item's subtree isn't
   2739  // dirty, and our current conditions are sufficiently similar to the most
   2740  // recent "final reflow" that it should have left our subtree in the correct
   2741  // state.)
   2742  FLEX_ITEM_LOG(mFrame, "[perf] Item didn't need a final reflow");
   2743  return false;
   2744 }
   2745 
   2746 // Keeps track of our position along a particular axis (where a '0' position
   2747 // corresponds to the 'start' edge of that axis).
   2748 // This class shouldn't be instantiated directly -- rather, it should only be
   2749 // instantiated via its subclasses defined below.
   2750 class MOZ_STACK_CLASS PositionTracker {
   2751 public:
   2752  // Accessor for the current value of the position that we're tracking.
   2753  inline nscoord Position() const { return mPosition; }
   2754  inline LogicalAxis Axis() const { return mAxis; }
   2755 
   2756  inline LogicalSide StartSide() {
   2757    return MakeLogicalSide(
   2758        mAxis, mIsAxisReversed ? LogicalEdge::End : LogicalEdge::Start);
   2759  }
   2760 
   2761  inline LogicalSide EndSide() {
   2762    return MakeLogicalSide(
   2763        mAxis, mIsAxisReversed ? LogicalEdge::Start : LogicalEdge::End);
   2764  }
   2765 
   2766  // Advances our position across the start edge of the given margin, in the
   2767  // axis we're tracking.
   2768  void EnterMargin(const LogicalMargin& aMargin) {
   2769    mPosition += aMargin.Side(StartSide(), mWM);
   2770  }
   2771 
   2772  // Advances our position across the end edge of the given margin, in the axis
   2773  // we're tracking.
   2774  void ExitMargin(const LogicalMargin& aMargin) {
   2775    mPosition += aMargin.Side(EndSide(), mWM);
   2776  }
   2777 
   2778  // Advances our current position from the start side of a child frame's
   2779  // border-box to the frame's upper or left edge (depending on our axis).
   2780  // (Note that this is a no-op if our axis grows in the same direction as
   2781  // the corresponding logical axis.)
   2782  void EnterChildFrame(nscoord aChildFrameSize) {
   2783    if (mIsAxisReversed) {
   2784      mPosition += aChildFrameSize;
   2785    }
   2786  }
   2787 
   2788  // Advances our current position from a frame's upper or left border-box edge
   2789  // (whichever is in the axis we're tracking) to the 'end' side of the frame
   2790  // in the axis that we're tracking. (Note that this is a no-op if our axis
   2791  // is reversed with respect to the corresponding logical axis.)
   2792  void ExitChildFrame(nscoord aChildFrameSize) {
   2793    if (!mIsAxisReversed) {
   2794      mPosition += aChildFrameSize;
   2795    }
   2796  }
   2797 
   2798  // Delete copy-constructor & reassignment operator, to prevent accidental
   2799  // (unnecessary) copying.
   2800  PositionTracker(const PositionTracker&) = delete;
   2801  PositionTracker& operator=(const PositionTracker&) = delete;
   2802 
   2803 protected:
   2804  // Protected constructor, to be sure we're only instantiated via a subclass.
   2805  PositionTracker(WritingMode aWM, LogicalAxis aAxis, bool aIsAxisReversed)
   2806      : mWM(aWM), mAxis(aAxis), mIsAxisReversed(aIsAxisReversed) {}
   2807 
   2808  // Member data:
   2809  // The position we're tracking.
   2810  nscoord mPosition = 0;
   2811 
   2812  // The flex container's writing mode.
   2813  const WritingMode mWM;
   2814 
   2815  // The axis along which we're moving.
   2816  const LogicalAxis mAxis = LogicalAxis::Inline;
   2817 
   2818  // Is the axis along which we're moving reversed (e.g. LTR vs RTL) with
   2819  // respect to the corresponding axis on the flex container's WM?
   2820  const bool mIsAxisReversed = false;
   2821 };
   2822 
   2823 // Tracks our position in the main axis, when we're laying out flex items.
   2824 // The "0" position represents the main-start edge of the flex container's
   2825 // content-box.
   2826 class MOZ_STACK_CLASS MainAxisPositionTracker : public PositionTracker {
   2827 public:
   2828  MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker,
   2829                          const FlexLine* aLine,
   2830                          const StyleContentDistribution& aJustifyContent,
   2831                          nscoord aContentBoxMainSize);
   2832 
   2833  ~MainAxisPositionTracker() {
   2834    MOZ_ASSERT(mNumPackingSpacesRemaining == 0,
   2835               "miscounted the number of packing spaces");
   2836    MOZ_ASSERT(mNumAutoMarginsInMainAxis == 0,
   2837               "miscounted the number of auto margins");
   2838  }
   2839 
   2840  // Advances past the gap space (if any) between two flex items
   2841  void TraverseGap(nscoord aGapSize) { mPosition += aGapSize; }
   2842 
   2843  // Advances past the packing space (if any) between two flex items
   2844  void TraversePackingSpace();
   2845 
   2846  // If aItem has any 'auto' margins in the main axis, this method updates the
   2847  // corresponding values in its margin.
   2848  void ResolveAutoMarginsInMainAxis(FlexItem& aItem);
   2849 
   2850 private:
   2851  nscoord mPackingSpaceRemaining = 0;
   2852  uint32_t mNumAutoMarginsInMainAxis = 0;
   2853  uint32_t mNumPackingSpacesRemaining = 0;
   2854  StyleContentDistribution mJustifyContent = {StyleAlignFlags::AUTO};
   2855 };
   2856 
   2857 // Utility class for managing our position along the cross axis along
   2858 // the whole flex container (at a higher level than a single line).
   2859 // The "0" position represents the cross-start edge of the flex container's
   2860 // content-box.
   2861 class MOZ_STACK_CLASS CrossAxisPositionTracker : public PositionTracker {
   2862 public:
   2863  CrossAxisPositionTracker(nsTArray<FlexLine>& aLines,
   2864                           const ReflowInput& aReflowInput,
   2865                           nscoord aContentBoxCrossSize,
   2866                           bool aIsCrossSizeDefinite,
   2867                           const FlexboxAxisTracker& aAxisTracker,
   2868                           const nscoord aCrossGapSize);
   2869 
   2870  // Advances past the gap (if any) between two flex lines
   2871  void TraverseGap() { mPosition += mCrossGapSize; }
   2872 
   2873  // Advances past the packing space (if any) between two flex lines
   2874  void TraversePackingSpace();
   2875 
   2876  // Advances past the given FlexLine
   2877  void TraverseLine(FlexLine& aLine) { mPosition += aLine.LineCrossSize(); }
   2878 
   2879  // Redeclare the frame-related methods from PositionTracker with
   2880  // = delete, to be sure (at compile time) that no client code can invoke
   2881  // them. (Unlike the other PositionTracker derived classes, this class here
   2882  // deals with FlexLines, not with individual FlexItems or frames.)
   2883  void EnterMargin(const LogicalMargin& aMargin) = delete;
   2884  void ExitMargin(const LogicalMargin& aMargin) = delete;
   2885  void EnterChildFrame(nscoord aChildFrameSize) = delete;
   2886  void ExitChildFrame(nscoord aChildFrameSize) = delete;
   2887 
   2888 private:
   2889  nscoord mPackingSpaceRemaining = 0;
   2890  uint32_t mNumPackingSpacesRemaining = 0;
   2891  StyleContentDistribution mAlignContent = {StyleAlignFlags::AUTO};
   2892 
   2893  const nscoord mCrossGapSize;
   2894 };
   2895 
   2896 // Utility class for managing our position along the cross axis, *within* a
   2897 // single flex line.
   2898 class MOZ_STACK_CLASS SingleLineCrossAxisPositionTracker
   2899    : public PositionTracker {
   2900 public:
   2901  explicit SingleLineCrossAxisPositionTracker(
   2902      const FlexboxAxisTracker& aAxisTracker);
   2903 
   2904  void ResolveAutoMarginsInCrossAxis(const FlexLine& aLine, FlexItem& aItem);
   2905 
   2906  void EnterAlignPackingSpace(const FlexLine& aLine, const FlexItem& aItem,
   2907                              const FlexboxAxisTracker& aAxisTracker);
   2908 
   2909  // Resets our position to the cross-start edge of this line.
   2910  inline void ResetPosition() { mPosition = 0; }
   2911 };
   2912 
   2913 //----------------------------------------------------------------------
   2914 
   2915 // Frame class boilerplate
   2916 // =======================
   2917 
   2918 NS_QUERYFRAME_HEAD(nsFlexContainerFrame)
   2919  NS_QUERYFRAME_ENTRY(nsFlexContainerFrame)
   2920 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
   2921 
   2922 NS_IMPL_FRAMEARENA_HELPERS(nsFlexContainerFrame)
   2923 
   2924 nsContainerFrame* NS_NewFlexContainerFrame(PresShell* aPresShell,
   2925                                           ComputedStyle* aStyle) {
   2926  return new (aPresShell)
   2927      nsFlexContainerFrame(aStyle, aPresShell->GetPresContext());
   2928 }
   2929 
   2930 //----------------------------------------------------------------------
   2931 
   2932 // nsFlexContainerFrame Method Implementations
   2933 // ===========================================
   2934 
   2935 /* virtual */
   2936 nsFlexContainerFrame::~nsFlexContainerFrame() = default;
   2937 
   2938 /* virtual */
   2939 void nsFlexContainerFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
   2940                                nsIFrame* aPrevInFlow) {
   2941  nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
   2942 
   2943  if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
   2944    AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
   2945  }
   2946 
   2947  auto displayInside = StyleDisplay()->DisplayInside();
   2948  // If this frame is for a scrollable element, then it will actually have
   2949  // "display:block", and its *parent frame* will have the real
   2950  // flex-flavored display value. So in that case, check the parent frame to
   2951  // find out if we're legacy.
   2952  //
   2953  // TODO(emilio): Maybe ::-moz-scrolled-content and co should inherit `display`
   2954  // (or a blockified version thereof, to not hit bug 456484).
   2955  if (displayInside == StyleDisplayInside::Flow) {
   2956    MOZ_ASSERT(StyleDisplay()->mDisplay == StyleDisplay::Block);
   2957    MOZ_ASSERT(Style()->GetPseudoType() == PseudoStyleType::scrolledContent,
   2958               "The only way a nsFlexContainerFrame can have 'display:block' "
   2959               "should be if it's the inner part of a scrollable element");
   2960    displayInside = GetParent()->StyleDisplay()->DisplayInside();
   2961  }
   2962 
   2963  if (displayInside == StyleDisplayInside::WebkitBox) {
   2964    AddStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX);
   2965  }
   2966 }
   2967 
   2968 #ifdef DEBUG_FRAME_DUMP
   2969 nsresult nsFlexContainerFrame::GetFrameName(nsAString& aResult) const {
   2970  return MakeFrameName(u"FlexContainer"_ns, aResult);
   2971 }
   2972 #endif
   2973 
   2974 void nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
   2975                                            const nsDisplayListSet& aLists) {
   2976  nsDisplayListCollection tempLists(aBuilder);
   2977  DisplayBorderBackgroundOutline(aBuilder, tempLists);
   2978  if (HidesContent()) {
   2979    tempLists.MoveTo(aLists);
   2980    return;
   2981  }
   2982 
   2983  if (GetPrevInFlow()) {
   2984    DisplayOverflowContainers(aBuilder, tempLists);
   2985    DisplayPushedAbsoluteFrames(aBuilder, tempLists);
   2986  }
   2987 
   2988  // Our children are all block-level, so their borders/backgrounds all go on
   2989  // the BlockBorderBackgrounds list.
   2990  nsDisplayListSet childLists(tempLists, tempLists.BlockBorderBackgrounds());
   2991 
   2992  CSSOrderAwareFrameIterator iter(
   2993      this, FrameChildListID::Principal,
   2994      CSSOrderAwareFrameIterator::ChildFilter::IncludeAll,
   2995      OrderStateForIter(this), OrderingPropertyForIter(this));
   2996 
   2997  const auto flags = DisplayFlagsForFlexOrGridItem();
   2998  for (; !iter.AtEnd(); iter.Next()) {
   2999    nsIFrame* childFrame = *iter;
   3000    BuildDisplayListForChild(aBuilder, childFrame, childLists, flags);
   3001  }
   3002 
   3003  tempLists.MoveTo(aLists);
   3004 }
   3005 
   3006 void FlexLine::FreezeItemsEarly(bool aIsUsingFlexGrow,
   3007                                ComputedFlexLineInfo* aLineInfo) {
   3008  // After we've established the type of flexing we're doing (growing vs.
   3009  // shrinking), and before we try to flex any items, we freeze items that
   3010  // obviously *can't* flex.
   3011  //
   3012  // Quoting the spec:
   3013  //  # Freeze, setting its target main size to its hypothetical main size...
   3014  //  #  - any item that has a flex factor of zero
   3015  //  #  - if using the flex grow factor: any item that has a flex base size
   3016  //  #    greater than its hypothetical main size
   3017  //  #  - if using the flex shrink factor: any item that has a flex base size
   3018  //  #    smaller than its hypothetical main size
   3019  //  https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths
   3020  //
   3021  // (NOTE: At this point, item->MainSize() *is* the item's hypothetical
   3022  // main size, since SetFlexBaseSizeAndMainSize() sets it up that way, and the
   3023  // item hasn't had a chance to flex away from that yet.)
   3024 
   3025  // Since this loop only operates on unfrozen flex items, we can break as
   3026  // soon as we have seen all of them.
   3027  uint32_t numUnfrozenItemsToBeSeen = NumItems() - mNumFrozenItems;
   3028  for (FlexItem& item : Items()) {
   3029    if (numUnfrozenItemsToBeSeen == 0) {
   3030      break;
   3031    }
   3032 
   3033    if (!item.IsFrozen()) {
   3034      numUnfrozenItemsToBeSeen--;
   3035      bool shouldFreeze = (0.0f == item.GetFlexFactor(aIsUsingFlexGrow));
   3036      if (!shouldFreeze) {
   3037        if (aIsUsingFlexGrow) {
   3038          if (item.FlexBaseSize() > item.MainSize()) {
   3039            shouldFreeze = true;
   3040          }
   3041        } else {  // using flex-shrink
   3042          if (item.FlexBaseSize() < item.MainSize()) {
   3043            shouldFreeze = true;
   3044          }
   3045        }
   3046      }
   3047      if (shouldFreeze) {
   3048        // Freeze item! (at its hypothetical main size)
   3049        item.Freeze();
   3050        if (item.FlexBaseSize() < item.MainSize()) {
   3051          item.SetWasMinClamped();
   3052        } else if (item.FlexBaseSize() > item.MainSize()) {
   3053          item.SetWasMaxClamped();
   3054        }
   3055        mNumFrozenItems++;
   3056      }
   3057    }
   3058  }
   3059 
   3060  MOZ_ASSERT(numUnfrozenItemsToBeSeen == 0, "miscounted frozen items?");
   3061 }
   3062 
   3063 // Based on the sign of aTotalViolation, this function freezes a subset of our
   3064 // flexible sizes, and restores the remaining ones to their initial pref sizes.
   3065 void FlexLine::FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
   3066                                               bool aIsFinalIteration) {
   3067  enum FreezeType {
   3068    eFreezeEverything,
   3069    eFreezeMinViolations,
   3070    eFreezeMaxViolations
   3071  };
   3072 
   3073  FreezeType freezeType;
   3074  if (aTotalViolation == 0) {
   3075    freezeType = eFreezeEverything;
   3076  } else if (aTotalViolation > 0) {
   3077    freezeType = eFreezeMinViolations;
   3078  } else {  // aTotalViolation < 0
   3079    freezeType = eFreezeMaxViolations;
   3080  }
   3081 
   3082  // Since this loop only operates on unfrozen flex items, we can break as
   3083  // soon as we have seen all of them.
   3084  uint32_t numUnfrozenItemsToBeSeen = NumItems() - mNumFrozenItems;
   3085  for (FlexItem& item : Items()) {
   3086    if (numUnfrozenItemsToBeSeen == 0) {
   3087      break;
   3088    }
   3089 
   3090    if (!item.IsFrozen()) {
   3091      numUnfrozenItemsToBeSeen--;
   3092 
   3093      MOZ_ASSERT(!item.HadMinViolation() || !item.HadMaxViolation(),
   3094                 "Can have either min or max violation, but not both");
   3095 
   3096      bool hadMinViolation = item.HadMinViolation();
   3097      bool hadMaxViolation = item.HadMaxViolation();
   3098      if (eFreezeEverything == freezeType ||
   3099          (eFreezeMinViolations == freezeType && hadMinViolation) ||
   3100          (eFreezeMaxViolations == freezeType && hadMaxViolation)) {
   3101        MOZ_ASSERT(item.MainSize() >= item.MainMinSize(),
   3102                   "Freezing item at a size below its minimum");
   3103        MOZ_ASSERT(item.MainSize() <= item.MainMaxSize(),
   3104                   "Freezing item at a size above its maximum");
   3105 
   3106        item.Freeze();
   3107        if (hadMinViolation) {
   3108          item.SetWasMinClamped();
   3109        } else if (hadMaxViolation) {
   3110          item.SetWasMaxClamped();
   3111        }
   3112        mNumFrozenItems++;
   3113      } else if (MOZ_UNLIKELY(aIsFinalIteration)) {
   3114        // XXXdholbert If & when bug 765861 is fixed, we should upgrade this
   3115        // assertion to be fatal except in documents with enormous lengths.
   3116        NS_ERROR(
   3117            "Final iteration still has unfrozen items, this shouldn't"
   3118            " happen unless there was nscoord under/overflow.");
   3119        item.Freeze();
   3120        mNumFrozenItems++;
   3121      }  // else, we'll reset this item's main size to its flex base size on the
   3122         // next iteration of this algorithm.
   3123 
   3124      if (!item.IsFrozen()) {
   3125        // Clear this item's violation(s), now that we've dealt with them
   3126        item.ClearViolationFlags();
   3127      }
   3128    }
   3129  }
   3130 
   3131  MOZ_ASSERT(numUnfrozenItemsToBeSeen == 0, "miscounted frozen items?");
   3132 }
   3133 
   3134 void FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize,
   3135                                      ComputedFlexLineInfo* aLineInfo) {
   3136  // In this function, we use 64-bit coord type to avoid integer overflow in
   3137  // case several of the individual items have huge hypothetical main sizes,
   3138  // which can happen with percent-width table-layout:fixed descendants. Here we
   3139  // promote the container's main size to 64-bit to make the arithmetic
   3140  // convenient.
   3141  AuCoord64 flexContainerMainSize(aFlexContainerMainSize);
   3142 
   3143  // Before we start resolving sizes: if we have an aLineInfo structure to fill
   3144  // out, we inform it of each item's base size, and we initialize the "delta"
   3145  // for each item to 0. (And if the flex algorithm wants to grow or shrink the
   3146  // item, we'll update this delta further down.)
   3147  if (aLineInfo) {
   3148    uint32_t itemIndex = 0;
   3149    for (FlexItem& item : Items()) {
   3150      aLineInfo->mItems[itemIndex].mMainBaseSize = item.FlexBaseSize();
   3151      aLineInfo->mItems[itemIndex].mMainDeltaSize = 0;
   3152      ++itemIndex;
   3153    }
   3154  }
   3155 
   3156  // Determine whether we're going to be growing or shrinking items.
   3157  const bool isUsingFlexGrow =
   3158      (mTotalOuterHypotheticalMainSize < flexContainerMainSize);
   3159 
   3160  if (aLineInfo) {
   3161    aLineInfo->mGrowthState =
   3162        isUsingFlexGrow ? mozilla::dom::FlexLineGrowthState::Growing
   3163                        : mozilla::dom::FlexLineGrowthState::Shrinking;
   3164  }
   3165 
   3166  // Do an "early freeze" for flex items that obviously can't flex in the
   3167  // direction we've chosen:
   3168  FreezeItemsEarly(isUsingFlexGrow, aLineInfo);
   3169 
   3170  if ((mNumFrozenItems == NumItems()) && !aLineInfo) {
   3171    // All our items are frozen, so we have no flexible lengths to resolve,
   3172    // and we aren't being asked to generate computed line info.
   3173    FLEX_LOG("No flexible length to resolve");
   3174    return;
   3175  }
   3176  MOZ_ASSERT(!IsEmpty() || aLineInfo,
   3177             "empty lines should take the early-return above");
   3178 
   3179  FLEX_LOG("Resolving flexible lengths for items");
   3180 
   3181  // Subtract space occupied by our items' margins/borders/padding/gaps, so
   3182  // we can just be dealing with the space available for our flex items' content
   3183  // boxes.
   3184  const AuCoord64 totalItemMBPAndGaps = mTotalItemMBP + SumOfGaps();
   3185  const AuCoord64 spaceAvailableForFlexItemsContentBoxes =
   3186      flexContainerMainSize - totalItemMBPAndGaps;
   3187 
   3188  Maybe<AuCoord64> origAvailableFreeSpace;
   3189 
   3190  // NOTE: I claim that this chunk of the algorithm (the looping part) needs to
   3191  // run the loop at MOST NumItems() times.  This claim should hold up
   3192  // because we'll freeze at least one item on each loop iteration, and once
   3193  // we've run out of items to freeze, there's nothing left to do.  However,
   3194  // in most cases, we'll break out of this loop long before we hit that many
   3195  // iterations.
   3196  for (uint32_t iterationCounter = 0; iterationCounter < NumItems();
   3197       iterationCounter++) {
   3198    // Set every not-yet-frozen item's used main size to its
   3199    // flex base size, and subtract all the used main sizes from our
   3200    // total amount of space to determine the 'available free space'
   3201    // (positive or negative) to be distributed among our flexible items.
   3202    AuCoord64 availableFreeSpace = spaceAvailableForFlexItemsContentBoxes;
   3203    for (FlexItem& item : Items()) {
   3204      if (!item.IsFrozen()) {
   3205        item.SetMainSize(item.FlexBaseSize());
   3206      }
   3207      availableFreeSpace -= item.MainSize();
   3208    }
   3209 
   3210    FLEX_LOGV("Available free space: %" PRId64 "; flex items should \"%s\"",
   3211              availableFreeSpace.value, isUsingFlexGrow ? "grow" : "shrink");
   3212 
   3213    // The sign of our free space should agree with the type of flexing
   3214    // (grow/shrink) that we're doing. Any disagreement should've made us use
   3215    // the other type of flexing, or should've been resolved in
   3216    // FreezeItemsEarly.
   3217    //
   3218    // Note: it's possible that an individual flex item has huge
   3219    // margin/border/padding that makes either its
   3220    // MarginBorderPaddingSizeInMainAxis() or OuterMainSize() negative due to
   3221    // integer overflow. If that happens, the accumulated
   3222    // mTotalOuterHypotheticalMainSize or mTotalItemMBP could be negative due to
   3223    // that one item's negative (overflowed) size. Likewise, a huge main gap
   3224    // size between flex items can also make our accumulated SumOfGaps()
   3225    // negative. In these case, we throw up our hands and don't require
   3226    // isUsingFlexGrow to agree with availableFreeSpace. Luckily, we won't get
   3227    // stuck in the algorithm below, and just distribute the wrong
   3228    // availableFreeSpace with the wrong grow/shrink factors.
   3229    MOZ_ASSERT(!(mTotalOuterHypotheticalMainSize >= 0 && mTotalItemMBP >= 0 &&
   3230                 totalItemMBPAndGaps >= 0) ||
   3231                   (isUsingFlexGrow && availableFreeSpace >= 0) ||
   3232                   (!isUsingFlexGrow && availableFreeSpace <= 0),
   3233               "availableFreeSpace's sign should match isUsingFlexGrow");
   3234 
   3235    // If we have any free space available, give each flexible item a portion
   3236    // of availableFreeSpace.
   3237    if (availableFreeSpace != AuCoord64(0)) {
   3238      // The first time we do this, we initialize origAvailableFreeSpace.
   3239      if (!origAvailableFreeSpace) {
   3240        origAvailableFreeSpace.emplace(availableFreeSpace);
   3241      }
   3242 
   3243      // STRATEGY: On each item, we compute & store its "share" of the total
   3244      // weight that we've seen so far:
   3245      //   curWeight / weightSum
   3246      //
   3247      // Then, when we go to actually distribute the space (in the next loop),
   3248      // we can simply walk backwards through the elements and give each item
   3249      // its "share" multiplied by the remaining available space.
   3250      //
   3251      // SPECIAL CASE: If the sum of the weights is larger than the
   3252      // maximum representable double (overflowing to infinity), then we can't
   3253      // sensibly divide out proportional shares anymore. In that case, we
   3254      // simply treat the flex item(s) with the largest weights as if
   3255      // their weights were infinite (dwarfing all the others), and we
   3256      // distribute all of the available space among them.
   3257      double weightSum = 0.0;
   3258      double flexFactorSum = 0.0;
   3259      double largestWeight = 0.0;
   3260      uint32_t numItemsWithLargestWeight = 0;
   3261 
   3262      // Since this loop only operates on unfrozen flex items, we can break as
   3263      // soon as we have seen all of them.
   3264      uint32_t numUnfrozenItemsToBeSeen = NumItems() - mNumFrozenItems;
   3265      for (FlexItem& item : Items()) {
   3266        if (numUnfrozenItemsToBeSeen == 0) {
   3267          break;
   3268        }
   3269 
   3270        if (!item.IsFrozen()) {
   3271          numUnfrozenItemsToBeSeen--;
   3272 
   3273          const double curWeight = item.GetWeight(isUsingFlexGrow);
   3274          const double curFlexFactor = item.GetFlexFactor(isUsingFlexGrow);
   3275          MOZ_ASSERT(curWeight >= 0.0, "weights are non-negative");
   3276          MOZ_ASSERT(curFlexFactor >= 0.0, "flex factors are non-negative");
   3277 
   3278          weightSum += curWeight;
   3279          flexFactorSum += curFlexFactor;
   3280 
   3281          if (std::isfinite(weightSum)) {
   3282            if (curWeight == 0.0) {
   3283              item.SetShareOfWeightSoFar(0.0);
   3284            } else {
   3285              item.SetShareOfWeightSoFar(curWeight / weightSum);
   3286            }
   3287          }  // else, the sum of weights overflows to infinity, in which
   3288             // case we don't bother with "SetShareOfWeightSoFar" since
   3289             // we know we won't use it. (instead, we'll just give every
   3290             // item with the largest weight an equal share of space.)
   3291 
   3292          // Update our largest-weight tracking vars
   3293          if (curWeight > largestWeight) {
   3294            largestWeight = curWeight;
   3295            numItemsWithLargestWeight = 1;
   3296          } else if (curWeight == largestWeight) {
   3297            numItemsWithLargestWeight++;
   3298          }
   3299        }
   3300      }
   3301 
   3302      MOZ_ASSERT(numUnfrozenItemsToBeSeen == 0, "miscounted frozen items?");
   3303 
   3304      if (weightSum != 0.0) {
   3305        MOZ_ASSERT(flexFactorSum != 0.0,
   3306                   "flex factor sum can't be 0, if a weighted sum "
   3307                   "of its components (weightSum) is nonzero");
   3308        if (flexFactorSum < 1.0) {
   3309          // Our unfrozen flex items don't want all of the original free space!
   3310          // (Their flex factors add up to something less than 1.)
   3311          // Hence, make sure we don't distribute any more than the portion of
   3312          // our original free space that these items actually want.
   3313          auto totalDesiredPortionOfOrigFreeSpace =
   3314              AuCoord64::FromRound(*origAvailableFreeSpace * flexFactorSum);
   3315 
   3316          // Clamp availableFreeSpace to be no larger than that ^^.
   3317          // (using min or max, depending on sign).
   3318          // This should not change the sign of availableFreeSpace (except
   3319          // possibly by setting it to 0), as enforced by this assertion:
   3320          NS_ASSERTION(totalDesiredPortionOfOrigFreeSpace == AuCoord64(0) ||
   3321                           ((totalDesiredPortionOfOrigFreeSpace > 0) ==
   3322                            (availableFreeSpace > 0)),
   3323                       "When we reduce available free space for flex "
   3324                       "factors < 1, we shouldn't change the sign of the "
   3325                       "free space...");
   3326 
   3327          if (availableFreeSpace > 0) {
   3328            availableFreeSpace = std::min(availableFreeSpace,
   3329                                          totalDesiredPortionOfOrigFreeSpace);
   3330          } else {
   3331            availableFreeSpace = std::max(availableFreeSpace,
   3332                                          totalDesiredPortionOfOrigFreeSpace);
   3333          }
   3334        }
   3335 
   3336        FLEX_LOGV("Distributing available space:");
   3337        // Since this loop only operates on unfrozen flex items, we can break as
   3338        // soon as we have seen all of them.
   3339        numUnfrozenItemsToBeSeen = NumItems() - mNumFrozenItems;
   3340 
   3341        // NOTE: It's important that we traverse our items in *reverse* order
   3342        // here, for correct width distribution according to the items'
   3343        // "ShareOfWeightSoFar" progressively-calculated values.
   3344        for (FlexItem& item : Reversed(Items())) {
   3345          if (numUnfrozenItemsToBeSeen == 0) {
   3346            break;
   3347          }
   3348 
   3349          if (!item.IsFrozen()) {
   3350            numUnfrozenItemsToBeSeen--;
   3351 
   3352            // To avoid rounding issues, we compute the change in size for this
   3353            // item, and then subtract it from the remaining available space.
   3354            AuCoord64 sizeDelta = 0;
   3355            if (std::isfinite(weightSum)) {
   3356              double myShareOfRemainingSpace = item.ShareOfWeightSoFar();
   3357 
   3358              MOZ_ASSERT(myShareOfRemainingSpace >= 0.0 &&
   3359                             myShareOfRemainingSpace <= 1.0,
   3360                         "my share should be nonnegative fractional amount");
   3361 
   3362              if (myShareOfRemainingSpace == 1.0) {
   3363                // (We special-case 1.0 to avoid float error from converting
   3364                // availableFreeSpace from integer*1.0 --> double --> integer)
   3365                sizeDelta = availableFreeSpace;
   3366              } else if (myShareOfRemainingSpace > 0.0) {
   3367                sizeDelta = AuCoord64::FromRound(availableFreeSpace *
   3368                                                 myShareOfRemainingSpace);
   3369              }
   3370            } else if (item.GetWeight(isUsingFlexGrow) == largestWeight) {
   3371              // Total flexibility is infinite, so we're just distributing
   3372              // the available space equally among the items that are tied for
   3373              // having the largest weight (and this is one of those items).
   3374              sizeDelta = AuCoord64::FromRound(
   3375                  availableFreeSpace / double(numItemsWithLargestWeight));
   3376              numItemsWithLargestWeight--;
   3377            }
   3378 
   3379            availableFreeSpace -= sizeDelta;
   3380 
   3381            item.SetMainSize(item.MainSize() +
   3382                             nscoord(sizeDelta.ToMinMaxClamped()));
   3383            FLEX_LOGV("  Flex item %p receives %" PRId64 ", for a total of %d",
   3384                      item.Frame(), sizeDelta.value, item.MainSize());
   3385          }
   3386        }
   3387 
   3388        MOZ_ASSERT(numUnfrozenItemsToBeSeen == 0, "miscounted frozen items?");
   3389 
   3390        // If we have an aLineInfo structure to fill out, capture any
   3391        // size changes that may have occurred in the previous loop.
   3392        // We don't do this inside the previous loop, because we don't
   3393        // want to burden layout when aLineInfo is null.
   3394        if (aLineInfo) {
   3395          uint32_t itemIndex = 0;
   3396          for (FlexItem& item : Items()) {
   3397            if (!item.IsFrozen()) {
   3398              // Calculate a deltaSize that represents how much the flex sizing
   3399              // algorithm "wants" to stretch or shrink this item during this
   3400              // pass through the algorithm. Later passes through the algorithm
   3401              // may overwrite this, until this item is frozen. Note that this
   3402              // value may not reflect how much the size of the item is
   3403              // actually changed, since the size of the item will be clamped
   3404              // to min and max values later in this pass. That's intentional,
   3405              // since we want to report the value that the sizing algorithm
   3406              // tried to stretch or shrink the item.
   3407              nscoord deltaSize =
   3408                  item.MainSize() - aLineInfo->mItems[itemIndex].mMainBaseSize;
   3409 
   3410              aLineInfo->mItems[itemIndex].mMainDeltaSize = deltaSize;
   3411            }
   3412            ++itemIndex;
   3413          }
   3414        }
   3415      }
   3416    }
   3417 
   3418    // Fix min/max violations:
   3419    nscoord totalViolation = 0;  // keeps track of adjustments for min/max
   3420    FLEX_LOGV("Checking for violations:");
   3421 
   3422    // Since this loop only operates on unfrozen flex items, we can break as
   3423    // soon as we have seen all of them.
   3424    uint32_t numUnfrozenItemsToBeSeen = NumItems() - mNumFrozenItems;
   3425    for (FlexItem& item : Items()) {
   3426      if (numUnfrozenItemsToBeSeen == 0) {
   3427        break;
   3428      }
   3429 
   3430      if (!item.IsFrozen()) {
   3431        numUnfrozenItemsToBeSeen--;
   3432 
   3433        if (item.MainSize() < item.MainMinSize()) {
   3434          // min violation
   3435          totalViolation += item.MainMinSize() - item.MainSize();
   3436          item.SetMainSize(item.MainMinSize());
   3437          item.SetHadMinViolation();
   3438        } else if (item.MainSize() > item.MainMaxSize()) {
   3439          // max violation
   3440          totalViolation += item.MainMaxSize() - item.MainSize();
   3441          item.SetMainSize(item.MainMaxSize());
   3442          item.SetHadMaxViolation();
   3443        }
   3444      }
   3445    }
   3446 
   3447    MOZ_ASSERT(numUnfrozenItemsToBeSeen == 0, "miscounted frozen items?");
   3448 
   3449    FreezeOrRestoreEachFlexibleSize(totalViolation,
   3450                                    iterationCounter + 1 == NumItems());
   3451 
   3452    FLEX_LOGV("Total violation: %d", totalViolation);
   3453 
   3454    if (mNumFrozenItems == NumItems()) {
   3455      break;
   3456    }
   3457 
   3458    MOZ_ASSERT(totalViolation != 0,
   3459               "Zero violation should've made us freeze all items & break");
   3460  }
   3461 
   3462 #ifdef DEBUG
   3463  // Post-condition: all items should've been frozen.
   3464  // Make sure the counts match:
   3465  MOZ_ASSERT(mNumFrozenItems == NumItems(), "All items should be frozen");
   3466 
   3467  // For good measure, check each item directly, in case our counts are busted:
   3468  for (const FlexItem& item : Items()) {
   3469    MOZ_ASSERT(item.IsFrozen(), "All items should be frozen");
   3470  }
   3471 #endif  // DEBUG
   3472 }
   3473 
   3474 MainAxisPositionTracker::MainAxisPositionTracker(
   3475    const FlexboxAxisTracker& aAxisTracker, const FlexLine* aLine,
   3476    const StyleContentDistribution& aJustifyContent,
   3477    nscoord aContentBoxMainSize)
   3478    : PositionTracker(aAxisTracker.GetWritingMode(), aAxisTracker.MainAxis(),
   3479                      aAxisTracker.IsMainAxisReversed()),
   3480      // we chip away at this below
   3481      mPackingSpaceRemaining(aContentBoxMainSize),
   3482      mJustifyContent(aJustifyContent) {
   3483  // Extract the flag portion of mJustifyContent and strip off the flag bits
   3484  // NOTE: This must happen before any assignment to mJustifyContent to
   3485  // avoid overwriting the flag bits.
   3486  StyleAlignFlags justifyContentFlags =
   3487      mJustifyContent.primary & StyleAlignFlags::FLAG_BITS;
   3488  mJustifyContent.primary &= ~StyleAlignFlags::FLAG_BITS;
   3489 
   3490  // 'normal' behaves as 'stretch', and 'stretch' behaves as 'flex-start',
   3491  // in the main axis
   3492  // https://drafts.csswg.org/css-align-3/#propdef-justify-content
   3493  if (mJustifyContent.primary == StyleAlignFlags::NORMAL ||
   3494      mJustifyContent.primary == StyleAlignFlags::STRETCH) {
   3495    mJustifyContent.primary = StyleAlignFlags::FLEX_START;
   3496  }
   3497 
   3498  // mPackingSpaceRemaining is initialized to the container's main size.  Now
   3499  // we'll subtract out the main sizes of our flex items, so that it ends up
   3500  // with the *actual* amount of packing space.
   3501  for (const FlexItem& item : aLine->Items()) {
   3502    mPackingSpaceRemaining -= item.OuterMainSize();
   3503    mNumAutoMarginsInMainAxis += item.NumAutoMarginsInMainAxis();
   3504  }
   3505 
   3506  // Subtract space required for row/col gap from the remaining packing space
   3507  mPackingSpaceRemaining -= aLine->SumOfGaps();
   3508 
   3509  // If packing space is negative or we only have one item, 'space-between'
   3510  // falls back to 'safe flex-start'[1], and 'space-around' & 'space-evenly'
   3511  // fall back to 'safe center'. In those cases, it's simplest to just pretend
   3512  // we have a different 'justify-content' value and share code.
   3513  // https://drafts.csswg.org/css-align-3/#distribution-values
   3514  if (mPackingSpaceRemaining < 0 || aLine->NumItems() == 1) {
   3515    if (mJustifyContent.primary == StyleAlignFlags::SPACE_BETWEEN) {
   3516      // [1]  XXXdholbert Per spec, we should do...
   3517      //   justifyContentFlags = StyleAlignFlags::SAFE;
   3518      // ...here, but for now let's not do that since it causes overflow to be
   3519      // inadvertently clipped in situations with a reversed main axis!
   3520      // See https://github.com/w3c/csswg-drafts/issues/11937
   3521      mJustifyContent.primary = StyleAlignFlags::FLEX_START;
   3522    } else if (mJustifyContent.primary == StyleAlignFlags::SPACE_AROUND ||
   3523               mJustifyContent.primary == StyleAlignFlags::SPACE_EVENLY) {
   3524      justifyContentFlags = StyleAlignFlags::SAFE;
   3525      mJustifyContent.primary = StyleAlignFlags::CENTER;
   3526    }
   3527  }
   3528 
   3529  if (mPackingSpaceRemaining <= 0) {
   3530    // No available packing space to use for resolving auto margins.
   3531    mNumAutoMarginsInMainAxis = 0;
   3532    // If packing space is negative and <overflow-position> is set to 'safe'
   3533    // all justify options fall back to 'start'
   3534    if (justifyContentFlags & StyleAlignFlags::SAFE) {
   3535      mJustifyContent.primary = StyleAlignFlags::START;
   3536    }
   3537  }
   3538 
   3539  // Map 'left'/'right' to 'start'/'end'
   3540  if (mJustifyContent.primary == StyleAlignFlags::LEFT ||
   3541      mJustifyContent.primary == StyleAlignFlags::RIGHT) {
   3542    mJustifyContent.primary =
   3543        aAxisTracker.ResolveJustifyLeftRight(mJustifyContent.primary);
   3544  }
   3545 
   3546  // Map 'start'/'end' to 'flex-start'/'flex-end'.
   3547  if (mJustifyContent.primary == StyleAlignFlags::START) {
   3548    mJustifyContent.primary = aAxisTracker.IsMainAxisReversed()
   3549                                  ? StyleAlignFlags::FLEX_END
   3550                                  : StyleAlignFlags::FLEX_START;
   3551  } else if (mJustifyContent.primary == StyleAlignFlags::END) {
   3552    mJustifyContent.primary = aAxisTracker.IsMainAxisReversed()
   3553                                  ? StyleAlignFlags::FLEX_START
   3554                                  : StyleAlignFlags::FLEX_END;
   3555  }
   3556 
   3557  // Figure out how much space we'll set aside for auto margins or
   3558  // packing spaces, and advance past any leading packing-space.
   3559  if (mNumAutoMarginsInMainAxis == 0 && mPackingSpaceRemaining != 0 &&
   3560      !aLine->IsEmpty()) {
   3561    if (mJustifyContent.primary == StyleAlignFlags::FLEX_START) {
   3562      // All packing space should go at the end --> nothing to do here.
   3563    } else if (mJustifyContent.primary == StyleAlignFlags::FLEX_END) {
   3564      // All packing space goes at the beginning
   3565      mPosition += mPackingSpaceRemaining;
   3566    } else if (mJustifyContent.primary == StyleAlignFlags::CENTER) {
   3567      // Half the packing space goes at the beginning
   3568      mPosition += mPackingSpaceRemaining / 2;
   3569    } else if (mJustifyContent.primary == StyleAlignFlags::SPACE_BETWEEN ||
   3570               mJustifyContent.primary == StyleAlignFlags::SPACE_AROUND ||
   3571               mJustifyContent.primary == StyleAlignFlags::SPACE_EVENLY) {
   3572      nsFlexContainerFrame::CalculatePackingSpace(
   3573          aLine->NumItems(), mJustifyContent, &mPosition,
   3574          &mNumPackingSpacesRemaining, &mPackingSpaceRemaining);
   3575    } else {
   3576      MOZ_ASSERT_UNREACHABLE("Unexpected justify-content value");
   3577    }
   3578  }
   3579 
   3580  MOZ_ASSERT(mNumPackingSpacesRemaining == 0 || mNumAutoMarginsInMainAxis == 0,
   3581             "extra space should either go to packing space or to "
   3582             "auto margins, but not to both");
   3583 }
   3584 
   3585 void MainAxisPositionTracker::ResolveAutoMarginsInMainAxis(FlexItem& aItem) {
   3586  if (mNumAutoMarginsInMainAxis) {
   3587    const auto* styleMargin = aItem.Frame()->StyleMargin();
   3588    const auto anchorResolutionParams =
   3589        AnchorPosResolutionParams::From(aItem.Frame());
   3590    for (const auto side : {StartSide(), EndSide()}) {
   3591      if (styleMargin->GetMargin(side, mWM, anchorResolutionParams)->IsAuto()) {
   3592        // NOTE: This integer math will skew the distribution of remainder
   3593        // app-units towards the end, which is fine.
   3594        nscoord curAutoMarginSize =
   3595            mPackingSpaceRemaining / mNumAutoMarginsInMainAxis;
   3596 
   3597        MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0,
   3598                   "Expecting auto margins to have value '0' before we "
   3599                   "resolve them");
   3600        aItem.SetMarginComponentForSide(side, curAutoMarginSize);
   3601 
   3602        mNumAutoMarginsInMainAxis--;
   3603        mPackingSpaceRemaining -= curAutoMarginSize;
   3604      }
   3605    }
   3606  }
   3607 }
   3608 
   3609 void MainAxisPositionTracker::TraversePackingSpace() {
   3610  if (mNumPackingSpacesRemaining) {
   3611    MOZ_ASSERT(mJustifyContent.primary == StyleAlignFlags::SPACE_BETWEEN ||
   3612                   mJustifyContent.primary == StyleAlignFlags::SPACE_AROUND ||
   3613                   mJustifyContent.primary == StyleAlignFlags::SPACE_EVENLY,
   3614               "mNumPackingSpacesRemaining only applies for "
   3615               "space-between/space-around/space-evenly");
   3616 
   3617    MOZ_ASSERT(mPackingSpaceRemaining >= 0,
   3618               "ran out of packing space earlier than we expected");
   3619 
   3620    // NOTE: This integer math will skew the distribution of remainder
   3621    // app-units towards the end, which is fine.
   3622    nscoord curPackingSpace =
   3623        mPackingSpaceRemaining / mNumPackingSpacesRemaining;
   3624 
   3625    mPosition += curPackingSpace;
   3626    mNumPackingSpacesRemaining--;
   3627    mPackingSpaceRemaining -= curPackingSpace;
   3628  }
   3629 }
   3630 
   3631 CrossAxisPositionTracker::CrossAxisPositionTracker(
   3632    nsTArray<FlexLine>& aLines, const ReflowInput& aReflowInput,
   3633    nscoord aContentBoxCrossSize, bool aIsCrossSizeDefinite,
   3634    const FlexboxAxisTracker& aAxisTracker, const nscoord aCrossGapSize)
   3635    : PositionTracker(aAxisTracker.GetWritingMode(), aAxisTracker.CrossAxis(),
   3636                      aAxisTracker.IsCrossAxisReversed()),
   3637      mAlignContent(aReflowInput.mStylePosition->mAlignContent),
   3638      mCrossGapSize(aCrossGapSize) {
   3639  // Extract and strip the flag bits from alignContent
   3640  StyleAlignFlags alignContentFlags =
   3641      mAlignContent.primary & StyleAlignFlags::FLAG_BITS;
   3642  mAlignContent.primary &= ~StyleAlignFlags::FLAG_BITS;
   3643 
   3644  // 'normal' behaves as 'stretch'
   3645  if (mAlignContent.primary == StyleAlignFlags::NORMAL) {
   3646    mAlignContent.primary = StyleAlignFlags::STRETCH;
   3647  }
   3648 
   3649  if (IsSingleLine(aReflowInput.mFrame, aReflowInput.mStylePosition)) {
   3650    MOZ_ASSERT(aLines.Length() == 1,
   3651               "If we're styled as single-line, we should only have 1 line");
   3652    // "If the flex container is single-line and has a definite cross size, the
   3653    // cross size of the flex line is the flex container's inner cross size."
   3654    //
   3655    // SOURCE: https://drafts.csswg.org/css-flexbox/#algo-cross-line
   3656    // NOTE: This means (by definition) that there's no packing space, which
   3657    // means we don't need to be concerned with "align-content" at all and we
   3658    // can return early. This is handy, because this is the usual case (for
   3659    // single-line flexbox).
   3660    if (aIsCrossSizeDefinite) {
   3661      aLines[0].SetLineCrossSize(aContentBoxCrossSize);
   3662      return;
   3663    }
   3664 
   3665    // "If the flex container is single-line, then clamp the line's
   3666    // cross-size to be within the container's computed min and max cross-size
   3667    // properties."
   3668    aLines[0].SetLineCrossSize(
   3669        aReflowInput.ApplyMinMaxBSize(aLines[0].LineCrossSize()));
   3670  }
   3671 
   3672  // NOTE: The rest of this function should essentially match
   3673  // MainAxisPositionTracker's constructor, though with FlexLines instead of
   3674  // FlexItems, and with the additional value "stretch" (and of course with
   3675  // cross sizes instead of main sizes.)
   3676 
   3677  // Figure out how much packing space we have (container's cross size minus
   3678  // all the lines' cross sizes).  Also, share this loop to count how many
   3679  // lines we have. (We need that count in some cases below.)
   3680  mPackingSpaceRemaining = aContentBoxCrossSize;
   3681  uint32_t numLines = 0;
   3682  for (FlexLine& line : aLines) {
   3683    mPackingSpaceRemaining -= line.LineCrossSize();
   3684    numLines++;
   3685  }
   3686 
   3687  // Subtract space required for row/col gap from the remaining packing space
   3688  MOZ_ASSERT(numLines >= 1,
   3689             "GenerateFlexLines should've produced at least 1 line");
   3690  mPackingSpaceRemaining -= aCrossGapSize * (numLines - 1);
   3691 
   3692  // If packing space is negative, 'stretch' behaves like 'flex-start',
   3693  // 'space-between' behaves like 'safe flex-start', and 'space-around' and
   3694  // 'space-evenly' behave like 'safe center'. In those cases, it's simplest to
   3695  // just pretend we have a different 'align-content' value and share code. (If
   3696  // we only have one line, all of the 'space-*' keywords fall back as well, but
   3697  // 'stretch' doesn't because even a single line can still stretch.)
   3698  // https://drafts.csswg.org/css-align-3/#distribution-values
   3699  if (mPackingSpaceRemaining < 0 &&
   3700      mAlignContent.primary == StyleAlignFlags::STRETCH) {
   3701    mAlignContent.primary = StyleAlignFlags::FLEX_START;
   3702  } else if (mPackingSpaceRemaining < 0 || numLines == 1) {
   3703    if (mAlignContent.primary == StyleAlignFlags::SPACE_BETWEEN) {
   3704      alignContentFlags = StyleAlignFlags::SAFE;
   3705      mAlignContent.primary = StyleAlignFlags::FLEX_START;
   3706    } else if (mAlignContent.primary == StyleAlignFlags::SPACE_AROUND ||
   3707               mAlignContent.primary == StyleAlignFlags::SPACE_EVENLY) {
   3708      alignContentFlags = StyleAlignFlags::SAFE;
   3709      mAlignContent.primary = StyleAlignFlags::CENTER;
   3710    }
   3711  }
   3712 
   3713  // If <overflow-position> is 'safe' and packing space is negative
   3714  // all align options fall back to 'start'
   3715  if ((alignContentFlags & StyleAlignFlags::SAFE) &&
   3716      mPackingSpaceRemaining < 0) {
   3717    mAlignContent.primary = StyleAlignFlags::START;
   3718  }
   3719 
   3720  // Map 'start'/'end' to 'flex-start'/'flex-end'.
   3721  if (mAlignContent.primary == StyleAlignFlags::START) {
   3722    mAlignContent.primary = aAxisTracker.IsCrossAxisReversed()
   3723                                ? StyleAlignFlags::FLEX_END
   3724                                : StyleAlignFlags::FLEX_START;
   3725  } else if (mAlignContent.primary == StyleAlignFlags::END) {
   3726    mAlignContent.primary = aAxisTracker.IsCrossAxisReversed()
   3727                                ? StyleAlignFlags::FLEX_START
   3728                                : StyleAlignFlags::FLEX_END;
   3729  }
   3730 
   3731  // Figure out how much space we'll set aside for packing spaces, and advance
   3732  // past any leading packing-space.
   3733  if (mPackingSpaceRemaining != 0) {
   3734    if (mAlignContent.primary == StyleAlignFlags::BASELINE ||
   3735        mAlignContent.primary == StyleAlignFlags::LAST_BASELINE) {
   3736      // TODO: Bug 1480850 will implement 'align-content: [first/last] baseline'
   3737      // for flexbox. Until then, behaves as if align-content is 'flex-start' by
   3738      // doing nothing.
   3739    } else if (mAlignContent.primary == StyleAlignFlags::FLEX_START) {
   3740      // All packing space should go at the end --> nothing to do here.
   3741    } else if (mAlignContent.primary == StyleAlignFlags::FLEX_END) {
   3742      // All packing space goes at the beginning
   3743      mPosition += mPackingSpaceRemaining;
   3744    } else if (mAlignContent.primary == StyleAlignFlags::CENTER) {
   3745      // Half the packing space goes at the beginning
   3746      mPosition += mPackingSpaceRemaining / 2;
   3747    } else if (mAlignContent.primary == StyleAlignFlags::SPACE_BETWEEN ||
   3748               mAlignContent.primary == StyleAlignFlags::SPACE_AROUND ||
   3749               mAlignContent.primary == StyleAlignFlags::SPACE_EVENLY) {
   3750      nsFlexContainerFrame::CalculatePackingSpace(
   3751          numLines, mAlignContent, &mPosition, &mNumPackingSpacesRemaining,
   3752          &mPackingSpaceRemaining);
   3753    } else if (mAlignContent.primary == StyleAlignFlags::STRETCH) {
   3754      // Split space equally between the lines:
   3755      MOZ_ASSERT(mPackingSpaceRemaining > 0,
   3756                 "negative packing space should make us use 'flex-start' "
   3757                 "instead of 'stretch' (and we shouldn't bother with this "
   3758                 "code if we have 0 packing space)");
   3759 
   3760      uint32_t numLinesLeft = numLines;
   3761      for (FlexLine& line : aLines) {
   3762        // Our share is the amount of space remaining, divided by the number
   3763        // of lines remainig.
   3764        MOZ_ASSERT(numLinesLeft > 0, "miscalculated num lines");
   3765        nscoord shareOfExtraSpace = mPackingSpaceRemaining / numLinesLeft;
   3766        nscoord newSize = line.LineCrossSize() + shareOfExtraSpace;
   3767        line.SetLineCrossSize(newSize);
   3768 
   3769        mPackingSpaceRemaining -= shareOfExtraSpace;
   3770        numLinesLeft--;
   3771      }
   3772      MOZ_ASSERT(numLinesLeft == 0, "miscalculated num lines");
   3773    } else {
   3774      MOZ_ASSERT_UNREACHABLE("Unexpected align-content value");
   3775    }
   3776  }
   3777 }
   3778 
   3779 void CrossAxisPositionTracker::TraversePackingSpace() {
   3780  if (mNumPackingSpacesRemaining) {
   3781    MOZ_ASSERT(mAlignContent.primary == StyleAlignFlags::SPACE_BETWEEN ||
   3782                   mAlignContent.primary == StyleAlignFlags::SPACE_AROUND ||
   3783                   mAlignContent.primary == StyleAlignFlags::SPACE_EVENLY,
   3784               "mNumPackingSpacesRemaining only applies for "
   3785               "space-between/space-around/space-evenly");
   3786 
   3787    MOZ_ASSERT(mPackingSpaceRemaining >= 0,
   3788               "ran out of packing space earlier than we expected");
   3789 
   3790    // NOTE: This integer math will skew the distribution of remainder
   3791    // app-units towards the end, which is fine.
   3792    nscoord curPackingSpace =
   3793        mPackingSpaceRemaining / mNumPackingSpacesRemaining;
   3794 
   3795    mPosition += curPackingSpace;
   3796    mNumPackingSpacesRemaining--;
   3797    mPackingSpaceRemaining -= curPackingSpace;
   3798  }
   3799 }
   3800 
   3801 SingleLineCrossAxisPositionTracker::SingleLineCrossAxisPositionTracker(
   3802    const FlexboxAxisTracker& aAxisTracker)
   3803    : PositionTracker(aAxisTracker.GetWritingMode(), aAxisTracker.CrossAxis(),
   3804                      aAxisTracker.IsCrossAxisReversed()) {}
   3805 
   3806 void FlexLine::ComputeCrossSizeAndBaseline(
   3807    const FlexboxAxisTracker& aAxisTracker) {
   3808  // NOTE: in these "cross{Start,End}ToFurthest{First,Last}Baseline" variables,
   3809  // the "first/last" term is referring to the flex *line's* baseline-sharing
   3810  // groups, which may or may not match any flex *item's* exact align-self
   3811  // value. See the code that sets FlexItem::mBaselineSharingGroup for more
   3812  // details.
   3813  nscoord crossStartToFurthestFirstBaseline = nscoord_MIN;
   3814  nscoord crossEndToFurthestFirstBaseline = nscoord_MIN;
   3815  nscoord crossStartToFurthestLastBaseline = nscoord_MIN;
   3816  nscoord crossEndToFurthestLastBaseline = nscoord_MIN;
   3817 
   3818  nscoord largestOuterCrossSize = 0;
   3819  for (const FlexItem& item : Items()) {
   3820    nscoord curOuterCrossSize = item.OuterCrossSize();
   3821 
   3822    if ((item.AlignSelf() == StyleAlignFlags::BASELINE ||
   3823         item.AlignSelf() == StyleAlignFlags::LAST_BASELINE) &&
   3824        item.NumAutoMarginsInCrossAxis() == 0) {
   3825      const bool usingItemFirstBaseline =
   3826          (item.AlignSelf() == StyleAlignFlags::BASELINE);
   3827 
   3828      // Find distance from our item's cross-start and cross-end margin-box
   3829      // edges to its baseline.
   3830      //
   3831      // Here's a diagram of a flex-item that we might be doing this on.
   3832      // "mmm" is the margin-box, "bbb" is the border-box. The bottom of
   3833      // the text "BASE" is the baseline.
   3834      //
   3835      // ---(cross-start)---
   3836      //                ___              ___            ___
   3837      //   mmmmmmmmmmmm  |                |margin-start  |
   3838      //   m          m  |               _|_   ___       |
   3839      //   m bbbbbbbb m  |curOuterCrossSize     |        |crossStartToBaseline
   3840      //   m b      b m  |                      |ascent  |
   3841      //   m b BASE b m  |                     _|_      _|_
   3842      //   m b      b m  |                               |
   3843      //   m bbbbbbbb m  |                               |crossEndToBaseline
   3844      //   m          m  |                               |
   3845      //   mmmmmmmmmmmm _|_                             _|_
   3846      //
   3847      // ---(cross-end)---
   3848      //
   3849      // We already have the curOuterCrossSize, margin-start, and the ascent.
   3850      // * We can get crossStartToBaseline by adding margin-start + ascent.
   3851      // * If we subtract that from the curOuterCrossSize, we get
   3852      //   crossEndToBaseline.
   3853 
   3854      nscoord crossStartToBaseline = item.BaselineOffsetFromOuterCrossEdge(
   3855          aAxisTracker.CrossAxisPhysicalStartSide(), usingItemFirstBaseline);
   3856      nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline;
   3857 
   3858      // Now, update our "largest" values for these (across all the flex items
   3859      // in this flex line), so we can use them in computing the line's cross
   3860      // size below:
   3861      if (item.ItemBaselineSharingGroup() == BaselineSharingGroup::First) {
   3862        crossStartToFurthestFirstBaseline =
   3863            std::max(crossStartToFurthestFirstBaseline, crossStartToBaseline);
   3864        crossEndToFurthestFirstBaseline =
   3865            std::max(crossEndToFurthestFirstBaseline, crossEndToBaseline);
   3866      } else {
   3867        crossStartToFurthestLastBaseline =
   3868            std::max(crossStartToFurthestLastBaseline, crossStartToBaseline);
   3869        crossEndToFurthestLastBaseline =
   3870            std::max(crossEndToFurthestLastBaseline, crossEndToBaseline);
   3871      }
   3872    } else {
   3873      largestOuterCrossSize =
   3874          std::max(largestOuterCrossSize, curOuterCrossSize);
   3875    }
   3876  }
   3877 
   3878  // The line's baseline offset is the distance from the line's edge to the
   3879  // furthest item-baseline. The item(s) with that baseline will be exactly
   3880  // aligned with the line's edge.
   3881  mFirstBaselineOffset = crossStartToFurthestFirstBaseline;
   3882  mLastBaselineOffset = crossEndToFurthestLastBaseline;
   3883 
   3884  // The line's cross-size is the larger of:
   3885  //  (a) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
   3886  //      all baseline-aligned items with no cross-axis auto margins...
   3887  // and
   3888  //  (b) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
   3889  //      all last baseline-aligned items with no cross-axis auto margins...
   3890  // and
   3891  //  (c) largest cross-size of all other children.
   3892  mLineCrossSize = std::max(
   3893      std::max(
   3894          crossStartToFurthestFirstBaseline + crossEndToFurthestFirstBaseline,
   3895          crossStartToFurthestLastBaseline + crossEndToFurthestLastBaseline),
   3896      largestOuterCrossSize);
   3897 }
   3898 
   3899 nscoord FlexLine::ExtractBaselineOffset(
   3900    BaselineSharingGroup aBaselineGroup) const {
   3901  auto LastBaselineOffsetFromStartEdge = [this]() {
   3902    // Convert the distance to be relative from the line's cross-start edge.
   3903    const nscoord offset = LastBaselineOffset();
   3904    return offset != nscoord_MIN ? LineCrossSize() - offset : offset;
   3905  };
   3906 
   3907  auto PrimaryBaseline = [=]() {
   3908    return aBaselineGroup == BaselineSharingGroup::First
   3909               ? FirstBaselineOffset()
   3910               : LastBaselineOffsetFromStartEdge();
   3911  };
   3912  auto SecondaryBaseline = [=]() {
   3913    return aBaselineGroup == BaselineSharingGroup::First
   3914               ? LastBaselineOffsetFromStartEdge()
   3915               : FirstBaselineOffset();
   3916  };
   3917 
   3918  const nscoord primaryBaseline = PrimaryBaseline();
   3919  if (primaryBaseline != nscoord_MIN) {
   3920    return primaryBaseline;
   3921  }
   3922  return SecondaryBaseline();
   3923 }
   3924 
   3925 void FlexItem::ResolveStretchedCrossSize(nscoord aLineCrossSize) {
   3926  // We stretch IFF we are align-self:stretch, have no auto margins in
   3927  // cross axis, and have cross-axis size property == "auto". If any of those
   3928  // conditions don't hold up, we won't stretch.
   3929  // https://drafts.csswg.org/css-flexbox-1/#valdef-align-items-stretch
   3930  if (mAlignSelf != StyleAlignFlags::STRETCH ||
   3931      NumAutoMarginsInCrossAxis() != 0 || !IsCrossSizeAuto()) {
   3932    return;
   3933  }
   3934 
   3935  // If we've already been stretched, we can bail out early, too.
   3936  // No need to redo the calculation.
   3937  if (mIsStretched) {
   3938    return;
   3939  }
   3940 
   3941  // Reserve space for margins & border & padding, and then use whatever
   3942  // remains as our item's cross-size (clamped to its min/max range).
   3943  nscoord stretchedSize = aLineCrossSize - MarginBorderPaddingSizeInCrossAxis();
   3944 
   3945  stretchedSize = CSSMinMax(stretchedSize, mCrossMinSize, mCrossMaxSize);
   3946 
   3947  // Update the cross-size & make a note that it's stretched, so we know to
   3948  // override the reflow input's computed cross-size in our final reflow.
   3949  SetCrossSize(stretchedSize);
   3950  mIsStretched = true;
   3951 }
   3952 
   3953 static nsBlockFrame* FindFlexItemBlockFrame(nsIFrame* aFrame) {
   3954  if (nsBlockFrame* block = do_QueryFrame(aFrame)) {
   3955    return block;
   3956  }
   3957  for (nsIFrame* f : aFrame->PrincipalChildList()) {
   3958    if (nsBlockFrame* block = FindFlexItemBlockFrame(f)) {
   3959      return block;
   3960    }
   3961  }
   3962  return nullptr;
   3963 }
   3964 
   3965 nsBlockFrame* FlexItem::BlockFrame() const {
   3966  return FindFlexItemBlockFrame(Frame());
   3967 }
   3968 
   3969 void SingleLineCrossAxisPositionTracker::ResolveAutoMarginsInCrossAxis(
   3970    const FlexLine& aLine, FlexItem& aItem) {
   3971  // Subtract the space that our item is already occupying, to see how much
   3972  // space (if any) is available for its auto margins.
   3973  nscoord spaceForAutoMargins = aLine.LineCrossSize() - aItem.OuterCrossSize();
   3974 
   3975  if (spaceForAutoMargins <= 0) {
   3976    return;  // No available space  --> nothing to do
   3977  }
   3978 
   3979  uint32_t numAutoMargins = aItem.NumAutoMarginsInCrossAxis();
   3980  if (numAutoMargins == 0) {
   3981    return;  // No auto margins --> nothing to do.
   3982  }
   3983 
   3984  // OK, we have at least one auto margin and we have some available space.
   3985  // Give each auto margin a share of the space.
   3986  const auto* styleMargin = aItem.Frame()->StyleMargin();
   3987  const auto anchorResolutionParams =
   3988      AnchorPosResolutionParams::From(aItem.Frame());
   3989  for (const auto side : {StartSide(), EndSide()}) {
   3990    if (styleMargin->GetMargin(side, mWM, anchorResolutionParams)->IsAuto()) {
   3991      MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0,
   3992                 "Expecting auto margins to have value '0' before we "
   3993                 "update them");
   3994 
   3995      // NOTE: integer divison is fine here; numAutoMargins is either 1 or 2.
   3996      // If it's 2 & spaceForAutoMargins is odd, 1st margin gets smaller half.
   3997      nscoord curAutoMarginSize = spaceForAutoMargins / numAutoMargins;
   3998      aItem.SetMarginComponentForSide(side, curAutoMarginSize);
   3999      numAutoMargins--;
   4000      spaceForAutoMargins -= curAutoMarginSize;
   4001    }
   4002  }
   4003 }
   4004 
   4005 void SingleLineCrossAxisPositionTracker::EnterAlignPackingSpace(
   4006    const FlexLine& aLine, const FlexItem& aItem,
   4007    const FlexboxAxisTracker& aAxisTracker) {
   4008  // We don't do align-self alignment on items that have auto margins
   4009  // in the cross axis.
   4010  if (aItem.NumAutoMarginsInCrossAxis()) {
   4011    return;
   4012  }
   4013 
   4014  StyleAlignFlags alignSelf = aItem.AlignSelf();
   4015  // NOTE: 'stretch' behaves like 'flex-start' once we've stretched any
   4016  // auto-sized items (which we've already done).
   4017  if (alignSelf == StyleAlignFlags::STRETCH) {
   4018    alignSelf = StyleAlignFlags::FLEX_START;
   4019  }
   4020 
   4021  // Map 'self-start'/'self-end' to 'start'/'end'
   4022  if (alignSelf == StyleAlignFlags::SELF_START ||
   4023      alignSelf == StyleAlignFlags::SELF_END) {
   4024    const LogicalAxis logCrossAxis =
   4025        aAxisTracker.IsRowOriented() ? LogicalAxis::Block : LogicalAxis::Inline;
   4026    const WritingMode cWM = aAxisTracker.GetWritingMode();
   4027    const bool sameStart =
   4028        cWM.ParallelAxisStartsOnSameSide(logCrossAxis, aItem.GetWritingMode());
   4029    alignSelf = sameStart == (alignSelf == StyleAlignFlags::SELF_START)
   4030                    ? StyleAlignFlags::START
   4031                    : StyleAlignFlags::END;
   4032  }
   4033 
   4034  // Map 'start'/'end' to 'flex-start'/'flex-end'.
   4035  if (alignSelf == StyleAlignFlags::START) {
   4036    alignSelf = aAxisTracker.IsCrossAxisReversed()
   4037                    ? StyleAlignFlags::FLEX_END
   4038                    : StyleAlignFlags::FLEX_START;
   4039  } else if (alignSelf == StyleAlignFlags::END) {
   4040    alignSelf = aAxisTracker.IsCrossAxisReversed() ? StyleAlignFlags::FLEX_START
   4041                                                   : StyleAlignFlags::FLEX_END;
   4042  }
   4043 
   4044  // 'align-self' falls back to 'flex-start' if it is 'center'/'flex-end' and we
   4045  // have cross axis overflow
   4046  // XXX we should really be falling back to 'start' as of bug 1472843
   4047  if (aLine.LineCrossSize() < aItem.OuterCrossSize() &&
   4048      (aItem.AlignSelfFlags() & StyleAlignFlags::SAFE)) {
   4049    alignSelf = StyleAlignFlags::FLEX_START;
   4050  }
   4051 
   4052  if (alignSelf == StyleAlignFlags::FLEX_START) {
   4053    // No space to skip over -- we're done.
   4054  } else if (alignSelf == StyleAlignFlags::FLEX_END) {
   4055    mPosition += aLine.LineCrossSize() - aItem.OuterCrossSize();
   4056  } else if (alignSelf == StyleAlignFlags::CENTER ||
   4057             alignSelf == StyleAlignFlags::ANCHOR_CENTER) {
   4058    // TODO(dshin, Bug 1909339): For now, treat `anchor-center` as `center`.
   4059    // Note: If cross-size is odd, the "after" space will get the extra unit.
   4060    mPosition += (aLine.LineCrossSize() - aItem.OuterCrossSize()) / 2;
   4061  } else if (alignSelf == StyleAlignFlags::BASELINE ||
   4062             alignSelf == StyleAlignFlags::LAST_BASELINE) {
   4063    const bool usingItemFirstBaseline =
   4064        (alignSelf == StyleAlignFlags::BASELINE);
   4065 
   4066    // The first-baseline sharing group gets (collectively) aligned to the
   4067    // FlexLine's cross-start side, and similarly the last-baseline sharing
   4068    // group gets snapped to the cross-end side.
   4069    const bool isFirstBaselineSharingGroup =
   4070        aItem.ItemBaselineSharingGroup() == BaselineSharingGroup::First;
   4071    const mozilla::Side alignSide =
   4072        isFirstBaselineSharingGroup ? aAxisTracker.CrossAxisPhysicalStartSide()
   4073                                    : aAxisTracker.CrossAxisPhysicalEndSide();
   4074 
   4075    // To compute the aligned position for our flex item, we determine:
   4076    // (1) The distance from the item's alignSide edge to the item's relevant
   4077    //     baseline.
   4078    nscoord itemBaselineOffset = aItem.BaselineOffsetFromOuterCrossEdge(
   4079        alignSide, usingItemFirstBaseline);
   4080 
   4081    // (2) The distance between the FlexLine's alignSide edge and the relevant
   4082    //     baseline-sharing-group's baseline position.
   4083    nscoord lineBaselineOffset = isFirstBaselineSharingGroup
   4084                                     ? aLine.FirstBaselineOffset()
   4085                                     : aLine.LastBaselineOffset();
   4086 
   4087    NS_ASSERTION(lineBaselineOffset >= itemBaselineOffset,
   4088                 "failed at finding largest baseline offset");
   4089 
   4090    // (3) The difference between the above offsets, which tells us how far we
   4091    //     need to shift the item away from the FlexLine's alignSide edge so
   4092    //     that its baseline is at the proper position for its group.
   4093    nscoord itemOffsetFromLineEdge = lineBaselineOffset - itemBaselineOffset;
   4094 
   4095    if (isFirstBaselineSharingGroup) {
   4096      // alignSide is the line's cross-start edge. mPosition is already there.
   4097      // From there, we step *forward* by the baseline adjustment:
   4098      mPosition += itemOffsetFromLineEdge;
   4099    } else {
   4100      // alignSide is the line's cross-end edge. Advance mPosition to align
   4101      // item with that edge (as in FLEX_END case)...
   4102      mPosition += aLine.LineCrossSize() - aItem.OuterCrossSize();
   4103      // ...and step *back* by the baseline adjustment:
   4104      mPosition -= itemOffsetFromLineEdge;
   4105    }
   4106  } else {
   4107    MOZ_ASSERT_UNREACHABLE("Unexpected align-self value");
   4108  }
   4109 }
   4110 
   4111 FlexboxAxisInfo::FlexboxAxisInfo(const nsIFrame* aFlexContainer) {
   4112  MOZ_ASSERT(aFlexContainer && aFlexContainer->IsFlexContainerFrame(),
   4113             "Only flex containers may be passed to this constructor!");
   4114  if (aFlexContainer->IsLegacyWebkitBox()) {
   4115    InitAxesFromLegacyProps(aFlexContainer);
   4116  } else {
   4117    InitAxesFromModernProps(aFlexContainer);
   4118  }
   4119 }
   4120 
   4121 void FlexboxAxisInfo::InitAxesFromLegacyProps(const nsIFrame* aFlexContainer) {
   4122  const nsStyleXUL* styleXUL = aFlexContainer->StyleXUL();
   4123 
   4124  const bool boxOrientIsVertical =
   4125      styleXUL->mBoxOrient == StyleBoxOrient::Vertical;
   4126  const bool wmIsVertical = aFlexContainer->GetWritingMode().IsVertical();
   4127 
   4128  // If box-orient agrees with our writing-mode, then we're "row-oriented"
   4129  // (i.e. the flexbox main axis is the same as our writing mode's inline
   4130  // direction).  Otherwise, we're column-oriented (i.e. the flexbox's main
   4131  // axis is perpendicular to the writing-mode's inline direction).
   4132  mIsRowOriented = (boxOrientIsVertical == wmIsVertical);
   4133 
   4134  // Legacy flexbox can use "-webkit-box-direction: reverse" to reverse the
   4135  // main axis (so it runs in the reverse direction of the inline axis):
   4136  mIsMainAxisReversed = styleXUL->mBoxDirection == StyleBoxDirection::Reverse;
   4137 
   4138  // Legacy flexbox does not support reversing the cross axis -- it has no
   4139  // equivalent of modern flexbox's "flex-wrap: wrap-reverse".
   4140  mIsCrossAxisReversed = false;
   4141 }
   4142 
   4143 void FlexboxAxisInfo::InitAxesFromModernProps(const nsIFrame* aFlexContainer) {
   4144  const nsStylePosition* stylePos = aFlexContainer->StylePosition();
   4145  StyleFlexDirection flexDirection = stylePos->mFlexDirection;
   4146 
   4147  // Determine main axis:
   4148  switch (flexDirection) {
   4149    case StyleFlexDirection::Row:
   4150      mIsRowOriented = true;
   4151      mIsMainAxisReversed = false;
   4152      break;
   4153    case StyleFlexDirection::RowReverse:
   4154      mIsRowOriented = true;
   4155      mIsMainAxisReversed = true;
   4156      break;
   4157    case StyleFlexDirection::Column:
   4158      mIsRowOriented = false;
   4159      mIsMainAxisReversed = false;
   4160      break;
   4161    case StyleFlexDirection::ColumnReverse:
   4162      mIsRowOriented = false;
   4163      mIsMainAxisReversed = true;
   4164      break;
   4165  }
   4166 
   4167  // "flex-wrap: wrap-reverse" reverses our cross axis.
   4168  mIsCrossAxisReversed = stylePos->mFlexWrap == StyleFlexWrap::WrapReverse;
   4169 }
   4170 
   4171 FlexboxAxisTracker::FlexboxAxisTracker(
   4172    const nsFlexContainerFrame* aFlexContainer)
   4173    : mWM(aFlexContainer->GetWritingMode()), mAxisInfo(aFlexContainer) {}
   4174 
   4175 LogicalSide FlexboxAxisTracker::MainAxisStartSide() const {
   4176  return MakeLogicalSide(
   4177      MainAxis(), IsMainAxisReversed() ? LogicalEdge::End : LogicalEdge::Start);
   4178 }
   4179 
   4180 LogicalSide FlexboxAxisTracker::CrossAxisStartSide() const {
   4181  return MakeLogicalSide(CrossAxis(), IsCrossAxisReversed()
   4182                                          ? LogicalEdge::End
   4183                                          : LogicalEdge::Start);
   4184 }
   4185 
   4186 void nsFlexContainerFrame::GenerateFlexLines(
   4187    const ReflowInput& aReflowInput, const nscoord aTentativeContentBoxMainSize,
   4188    const nscoord aTentativeContentBoxCrossSize,
   4189    const nsTArray<StrutInfo>& aStruts, const FlexboxAxisTracker& aAxisTracker,
   4190    nscoord aMainGapSize, nsTArray<nsIFrame*>& aPlaceholders,
   4191    nsTArray<FlexLine>& aLines, bool& aHasCollapsedItems) {
   4192  MOZ_ASSERT(aLines.IsEmpty(), "Expecting outparam to start out empty");
   4193 
   4194  auto ConstructNewFlexLine = [&aLines, aMainGapSize]() {
   4195    return aLines.EmplaceBack(aMainGapSize);
   4196  };
   4197 
   4198  // We have at least one FlexLine. Even an empty flex container has a single
   4199  // (empty) flex line.
   4200  FlexLine* curLine = ConstructNewFlexLine();
   4201 
   4202  nscoord wrapThreshold;
   4203  if (IsSingleLine(aReflowInput.mFrame, aReflowInput.mStylePosition)) {
   4204    // Not wrapping. Set threshold to sentinel value that tells us not to wrap.
   4205    wrapThreshold = NS_UNCONSTRAINEDSIZE;
   4206  } else {
   4207    // Wrapping! Set wrap threshold to flex container's content-box main-size.
   4208    wrapThreshold = aTentativeContentBoxMainSize;
   4209 
   4210    // If the flex container doesn't have a definite content-box main-size
   4211    // (e.g. if main axis is vertical & 'height' is 'auto'), make sure we at
   4212    // least wrap when we hit its max main-size.
   4213    if (wrapThreshold == NS_UNCONSTRAINEDSIZE) {
   4214      const nscoord flexContainerMaxMainSize =
   4215          aAxisTracker.MainComponent(aReflowInput.ComputedMaxSize());
   4216      wrapThreshold = flexContainerMaxMainSize;
   4217    }
   4218  }
   4219 
   4220  // Tracks the index of the next strut, in aStruts (and when this hits
   4221  // aStruts.Length(), that means there are no more struts):
   4222  uint32_t nextStrutIdx = 0;
   4223 
   4224  // Overall index of the current flex item in the flex container. (This gets
   4225  // checked against entries in aStruts.)
   4226  uint32_t itemIdxInContainer = 0;
   4227 
   4228  CSSOrderAwareFrameIterator iter(
   4229      this, FrameChildListID::Principal,
   4230      CSSOrderAwareFrameIterator::ChildFilter::IncludeAll,
   4231      CSSOrderAwareFrameIterator::OrderState::Unknown,
   4232      OrderingPropertyForIter(this));
   4233 
   4234  AddOrRemoveStateBits(NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER,
   4235                       iter.ItemsAreAlreadyInOrder());
   4236 
   4237  const bool useMozBoxCollapseBehavior =
   4238      StyleVisibility()->UseLegacyCollapseBehavior();
   4239 
   4240  for (; !iter.AtEnd(); iter.Next()) {
   4241    nsIFrame* childFrame = *iter;
   4242    // Don't create flex items / lines for placeholder frames:
   4243    if (childFrame->IsPlaceholderFrame()) {
   4244      aPlaceholders.AppendElement(childFrame);
   4245      continue;
   4246    }
   4247 
   4248    const bool collapsed = childFrame->StyleVisibility()->IsCollapse();
   4249    aHasCollapsedItems = aHasCollapsedItems || collapsed;
   4250 
   4251    if (useMozBoxCollapseBehavior && collapsed) {
   4252      // Legacy visibility:collapse behavior: make a 0-sized strut. (No need to
   4253      // bother with aStruts and remembering cross size.)
   4254      curLine->Items().EmplaceBack(childFrame, 0, aReflowInput.GetWritingMode(),
   4255                                   aAxisTracker);
   4256    } else if (nextStrutIdx < aStruts.Length() &&
   4257               aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) {
   4258      // Use the simplified "strut" FlexItem constructor:
   4259      curLine->Items().EmplaceBack(childFrame,
   4260                                   aStruts[nextStrutIdx].mStrutCrossSize,
   4261                                   aReflowInput.GetWritingMode(), aAxisTracker);
   4262      nextStrutIdx++;
   4263    } else {
   4264      GenerateFlexItemForChild(*curLine, childFrame, aReflowInput, aAxisTracker,
   4265                               aTentativeContentBoxCrossSize);
   4266    }
   4267 
   4268    // Check if we need to wrap the newly appended item to a new line, i.e. if
   4269    // its outer hypothetical main size pushes our line over the threshold.
   4270    // But we don't wrap if the line-length is unconstrained, nor do we wrap if
   4271    // this was the first item on the line.
   4272    if (wrapThreshold != NS_UNCONSTRAINEDSIZE &&
   4273        curLine->Items().Length() > 1) {
   4274      // If the line will be longer than wrapThreshold or at least as long as
   4275      // nscoord_MAX because of the newly appended item, then wrap and move the
   4276      // item to a new line.
   4277      auto newOuterSize = curLine->TotalOuterHypotheticalMainSize();
   4278      newOuterSize += curLine->Items().LastElement().OuterMainSize();
   4279 
   4280      // Account for gap between this line's previous item and this item.
   4281      newOuterSize += aMainGapSize;
   4282 
   4283      if (newOuterSize >= nscoord_MAX || newOuterSize > wrapThreshold) {
   4284        curLine = ConstructNewFlexLine();
   4285 
   4286        // Get the previous line after adding a new line because the address can
   4287        // change if nsTArray needs to reallocate a new space for the new line.
   4288        FlexLine& prevLine = aLines[aLines.Length() - 2];
   4289 
   4290        // Move the item from the end of prevLine to the end of curLine.
   4291        curLine->Items().AppendElement(prevLine.Items().PopLastElement());
   4292      }
   4293    }
   4294 
   4295    // Update the line's bookkeeping about how large its items collectively are.
   4296    curLine->AddLastItemToMainSizeTotals();
   4297    itemIdxInContainer++;
   4298  }
   4299 }
   4300 
   4301 nsFlexContainerFrame::FlexLayoutResult
   4302 nsFlexContainerFrame::GenerateFlexLayoutResult() {
   4303  MOZ_ASSERT(GetPrevInFlow(), "This should be called by non-first-in-flows!");
   4304 
   4305  auto* data = FirstInFlow()->GetProperty(SharedFlexData::Prop());
   4306  MOZ_ASSERT(data, "SharedFlexData should be set by our first-in-flow!");
   4307 
   4308  FlexLayoutResult flr;
   4309 
   4310  // The order state of the children is consistent across entire continuation
   4311  // chain due to calling nsContainerFrame::NormalizeChildLists() at the
   4312  // beginning of Reflow(), so we can align our state bit with our
   4313  // prev-in-flow's state. Setup here before calling OrderStateForIter() below.
   4314  AddOrRemoveStateBits(NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER,
   4315                       GetPrevInFlow()->HasAnyStateBits(
   4316                           NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER));
   4317 
   4318  // Construct flex items for this flex container fragment from existing flex
   4319  // items in SharedFlexData.
   4320  CSSOrderAwareFrameIterator iter(
   4321      this, FrameChildListID::Principal,
   4322      CSSOrderAwareFrameIterator::ChildFilter::SkipPlaceholders,
   4323      OrderStateForIter(this), OrderingPropertyForIter(this));
   4324 
   4325  auto ConstructNewFlexLine = [&flr]() {
   4326    // Use zero main gap size since it doesn't matter in flex container's
   4327    // next-in-flows. We've computed flex items' positions in first-in-flow.
   4328    return flr.mLines.EmplaceBack(0);
   4329  };
   4330 
   4331  // We have at least one FlexLine. Even an empty flex container has a single
   4332  // (empty) flex line.
   4333  FlexLine* currentLine = ConstructNewFlexLine();
   4334 
   4335  if (!iter.AtEnd()) {
   4336    nsIFrame* child = *iter;
   4337    nsIFrame* childFirstInFlow = child->FirstInFlow();
   4338 
   4339    // We are iterating nested for-loops over the FlexLines and FlexItems
   4340    // generated by GenerateFlexLines() and cached in flex container's
   4341    // first-in-flow. For each flex item, check if its frame (must be a
   4342    // first-in-flow) is the first-in-flow of the first child frame in this flex
   4343    // container continuation. If so, clone the data from that FlexItem into a
   4344    // FlexLine. When we find a match for the item, we know that the next child
   4345    // frame might have its first-in-flow as the next item in the same original
   4346    // line. In this case, we'll put the cloned data in the same line here as
   4347    // well.
   4348    for (const FlexLine& line : data->mLines) {
   4349      // If currentLine is empty, either it is the first line, or all the items
   4350      // in the previous line have been placed in our prev-in-flows. No need to
   4351      // construct a new line.
   4352      if (!currentLine->IsEmpty()) {
   4353        currentLine = ConstructNewFlexLine();
   4354      }
   4355      for (const FlexItem& item : line.Items()) {
   4356        if (item.Frame() == childFirstInFlow) {
   4357          currentLine->Items().AppendElement(item.CloneFor(child));
   4358          iter.Next();
   4359          if (iter.AtEnd()) {
   4360            // We've constructed flex items for all children. No need to check
   4361            // rest of the items.
   4362            child = childFirstInFlow = nullptr;
   4363            break;
   4364          }
   4365          child = *iter;
   4366          childFirstInFlow = child->FirstInFlow();
   4367        }
   4368      }
   4369      if (iter.AtEnd()) {
   4370        // We've constructed flex items for all children. No need to check
   4371        // rest of the lines.
   4372        break;
   4373      }
   4374    }
   4375  }
   4376 
   4377  flr.mContentBoxMainSize = data->mContentBoxMainSize;
   4378  flr.mContentBoxCrossSize = data->mContentBoxCrossSize;
   4379 
   4380  return flr;
   4381 }
   4382 
   4383 // Returns the largest outer hypothetical main-size of any line in |aLines|.
   4384 // (i.e. the hypothetical main-size of the largest line)
   4385 static AuCoord64 GetLargestLineMainSize(nsTArray<FlexLine>& aLines) {
   4386  AuCoord64 largestLineOuterSize = 0;
   4387  for (const FlexLine& line : aLines) {
   4388    largestLineOuterSize =
   4389        std::max(largestLineOuterSize, line.TotalOuterHypotheticalMainSize());
   4390  }
   4391  return largestLineOuterSize;
   4392 }
   4393 
   4394 nscoord nsFlexContainerFrame::ComputeMainSize(
   4395    const ReflowInput& aReflowInput, const FlexboxAxisTracker& aAxisTracker,
   4396    const nscoord aTentativeContentBoxMainSize,
   4397    nsTArray<FlexLine>& aLines) const {
   4398  if (aAxisTracker.IsRowOriented()) {
   4399    // Row-oriented --> our main axis is the inline axis, so our main size
   4400    // is our inline size (which should already be resolved).
   4401    return aTentativeContentBoxMainSize;
   4402  }
   4403 
   4404  const bool shouldApplyAutomaticMinimumOnBlockAxis =
   4405      aReflowInput.ShouldApplyAutomaticMinimumOnBlockAxis();
   4406  if (aTentativeContentBoxMainSize != NS_UNCONSTRAINEDSIZE &&
   4407      !shouldApplyAutomaticMinimumOnBlockAxis) {
   4408    // Column-oriented case, with fixed BSize:
   4409    // Just use our fixed block-size because we always assume the available
   4410    // block-size is unconstrained, and the reflow input has already done the
   4411    // appropriate min/max-BSize clamping.
   4412    return aTentativeContentBoxMainSize;
   4413  }
   4414 
   4415  // Column-oriented case, with size-containment in block axis:
   4416  // Behave as if we had no content and just use our MinBSize.
   4417  if (Maybe<nscoord> containBSize =
   4418          aReflowInput.mFrame->ContainIntrinsicBSize()) {
   4419    return aReflowInput.ApplyMinMaxBSize(*containBSize);
   4420  }
   4421 
   4422  const AuCoord64 largestLineMainSize = GetLargestLineMainSize(aLines);
   4423  const nscoord contentBSize = aReflowInput.ApplyMinMaxBSize(
   4424      nscoord(largestLineMainSize.ToMinMaxClamped()));
   4425 
   4426  // If the clamped largest FlexLine length is larger than the tentative main
   4427  // size (which is resolved by aspect-ratio), we extend it to contain the
   4428  // entire FlexLine.
   4429  // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
   4430  if (shouldApplyAutomaticMinimumOnBlockAxis) {
   4431    // Column-oriented case, with auto BSize which is resolved by
   4432    // aspect-ratio.
   4433    return std::max(contentBSize, aTentativeContentBoxMainSize);
   4434  }
   4435 
   4436  // Column-oriented case, with auto BSize:
   4437  // Resolve auto BSize to the largest FlexLine length, clamped to our
   4438  // computed min/max main-size properties.
   4439  return contentBSize;
   4440 }
   4441 
   4442 nscoord nsFlexContainerFrame::ComputeCrossSize(
   4443    const ReflowInput& aReflowInput, const FlexboxAxisTracker& aAxisTracker,
   4444    const nscoord aTentativeContentBoxCrossSize, nscoord aSumLineCrossSizes,
   4445    bool* aIsDefinite) const {
   4446  MOZ_ASSERT(aIsDefinite, "outparam pointer must be non-null");
   4447 
   4448  if (aAxisTracker.IsColumnOriented()) {
   4449    // Column-oriented --> our cross axis is the inline axis, so our cross size
   4450    // is our inline size (which should already be resolved).
   4451    *aIsDefinite = true;
   4452    // FIXME: Bug 1661847 - there are cases where aTentativeContentBoxCrossSize
   4453    // (i.e. aReflowInput.ComputedISize()) might not be the right thing to
   4454    // return here. Specifically: if our cross size is an intrinsic size, and we
   4455    // have flex items that are flexible and have aspect ratios, then we may
   4456    // need to take their post-flexing main sizes into account (multiplied
   4457    // through their aspect ratios to get their cross sizes), in order to
   4458    // determine their flex line's size & the flex container's cross size (e.g.
   4459    // as `aSumLineCrossSizes`).
   4460    return aTentativeContentBoxCrossSize;
   4461  }
   4462 
   4463  const bool shouldApplyAutomaticMinimumOnBlockAxis =
   4464      aReflowInput.ShouldApplyAutomaticMinimumOnBlockAxis();
   4465  const nscoord computedBSize = aReflowInput.ComputedBSize();
   4466  if (computedBSize != NS_UNCONSTRAINEDSIZE &&
   4467      !shouldApplyAutomaticMinimumOnBlockAxis) {
   4468    // Row-oriented case (cross axis is block-axis), with fixed BSize:
   4469    *aIsDefinite = true;
   4470 
   4471    // Just use our fixed block-size because we always assume the available
   4472    // block-size is unconstrained, and the reflow input has already done the
   4473    // appropriate min/max-BSize clamping.
   4474    return computedBSize;
   4475  }
   4476 
   4477  // Row-oriented case, with size-containment in block axis:
   4478  // Behave as if we had no content and just use our MinBSize.
   4479  if (Maybe<nscoord> containBSize =
   4480          aReflowInput.mFrame->ContainIntrinsicBSize()) {
   4481    *aIsDefinite = true;
   4482    return aReflowInput.ApplyMinMaxBSize(*containBSize);
   4483  }
   4484 
   4485  // The cross size must not be definite in the following cases.
   4486  *aIsDefinite = false;
   4487 
   4488  const nscoord contentBSize =
   4489      aReflowInput.ApplyMinMaxBSize(aSumLineCrossSizes);
   4490  // If the content block-size is larger than the effective computed
   4491  // block-size, we extend the block-size to contain all the content.
   4492  // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
   4493  if (shouldApplyAutomaticMinimumOnBlockAxis) {
   4494    // Row-oriented case (cross axis is block-axis), with auto BSize which is
   4495    // resolved by aspect-ratio or content size.
   4496    return std::max(contentBSize, computedBSize);
   4497  }
   4498 
   4499  // Row-oriented case (cross axis is block axis), with auto BSize:
   4500  // Shrink-wrap our line(s), subject to our min-size / max-size
   4501  // constraints in that (block) axis.
   4502  return contentBSize;
   4503 }
   4504 
   4505 LogicalSize nsFlexContainerFrame::ComputeAvailableSizeForItems(
   4506    const ReflowInput& aReflowInput,
   4507    const mozilla::LogicalMargin& aBorderPadding) const {
   4508  const WritingMode wm = GetWritingMode();
   4509  nscoord availableBSize = aReflowInput.AvailableBSize();
   4510 
   4511  if (availableBSize != NS_UNCONSTRAINEDSIZE) {
   4512    // Available block-size is constrained. Subtract block-start border and
   4513    // padding from it.
   4514    availableBSize -= aBorderPadding.BStart(wm);
   4515 
   4516    if (aReflowInput.mStyleBorder->mBoxDecorationBreak ==
   4517        StyleBoxDecorationBreak::Clone) {
   4518      // We have box-decoration-break:clone. Subtract block-end border and
   4519      // padding from the available block-size as well.
   4520      availableBSize -= aBorderPadding.BEnd(wm);
   4521    }
   4522 
   4523    // Available block-size can became negative after subtracting block-axis
   4524    // border and padding. Per spec, to guarantee progress, fragmentainers are
   4525    // assumed to have a minimum block size of 1px regardless of their used
   4526    // size. https://drafts.csswg.org/css-break/#breaking-rules
   4527    availableBSize =
   4528        std::max(nsPresContext::CSSPixelsToAppUnits(1), availableBSize);
   4529  }
   4530 
   4531  return LogicalSize(wm, aReflowInput.ComputedISize(), availableBSize);
   4532 }
   4533 
   4534 void FlexLine::PositionItemsInMainAxis(
   4535    const StyleContentDistribution& aJustifyContent,
   4536    nscoord aContentBoxMainSize, const FlexboxAxisTracker& aAxisTracker) {
   4537  MainAxisPositionTracker mainAxisPosnTracker(
   4538      aAxisTracker, this, aJustifyContent, aContentBoxMainSize);
   4539  for (FlexItem& item : Items()) {
   4540    nscoord itemMainBorderBoxSize =
   4541        item.MainSize() + item.BorderPaddingSizeInMainAxis();
   4542 
   4543    // Resolve any main-axis 'auto' margins on aChild to an actual value.
   4544    mainAxisPosnTracker.ResolveAutoMarginsInMainAxis(item);
   4545 
   4546    // Advance our position tracker to child's upper-left content-box corner,
   4547    // and use that as its position in the main axis.
   4548    mainAxisPosnTracker.EnterMargin(item.Margin());
   4549    mainAxisPosnTracker.EnterChildFrame(itemMainBorderBoxSize);
   4550 
   4551    item.SetMainPosition(mainAxisPosnTracker.Position());
   4552 
   4553    mainAxisPosnTracker.ExitChildFrame(itemMainBorderBoxSize);
   4554    mainAxisPosnTracker.ExitMargin(item.Margin());
   4555    mainAxisPosnTracker.TraversePackingSpace();
   4556    if (&item != &Items().LastElement()) {
   4557      mainAxisPosnTracker.TraverseGap(mMainGapSize);
   4558    }
   4559  }
   4560 }
   4561 
   4562 void nsFlexContainerFrame::SizeItemInCrossAxis(ReflowInput& aChildReflowInput,
   4563                                               FlexItem& aItem) {
   4564  // If cross axis is the item's inline axis, just use ISize from reflow input,
   4565  // and don't bother with a full reflow.
   4566  if (aItem.IsInlineAxisCrossAxis()) {
   4567    aItem.SetCrossSize(aChildReflowInput.ComputedISize());
   4568    return;
   4569  }
   4570 
   4571  MOZ_ASSERT(!aItem.HadMeasuringReflow(),
   4572             "We shouldn't need more than one measuring reflow");
   4573 
   4574  if (aItem.AlignSelf() == StyleAlignFlags::STRETCH) {
   4575    // This item's got "align-self: stretch", so we probably imposed a
   4576    // stretched computed cross-size on it during its previous
   4577    // reflow. We're not imposing that BSize for *this* "measuring" reflow, so
   4578    // we need to tell it to treat this reflow as a resize in its block axis
   4579    // (regardless of whether any of its ancestors are actually being resized).
   4580    // (Note: we know that the cross axis is the item's *block* axis -- if it
   4581    // weren't, then we would've taken the early-return above.)
   4582    aChildReflowInput.SetBResize(true);
   4583    // Not 100% sure this is needed, but be conservative for now:
   4584    aChildReflowInput.SetBResizeForPercentages(true);
   4585  }
   4586 
   4587  // Potentially reflow the item, and get the sizing info.
   4588  const CachedBAxisMeasurement& measurement =
   4589      MeasureBSizeForFlexItem(aItem, aChildReflowInput);
   4590 
   4591  // Save the sizing info that we learned from this reflow
   4592  // -----------------------------------------------------
   4593 
   4594  // Tentatively store the child's desired content-box cross-size.
   4595  aItem.SetCrossSize(measurement.BSize());
   4596 }
   4597 
   4598 void FlexLine::PositionItemsInCrossAxis(
   4599    nscoord aLineStartPosition, const FlexboxAxisTracker& aAxisTracker) {
   4600  SingleLineCrossAxisPositionTracker lineCrossAxisPosnTracker(aAxisTracker);
   4601 
   4602  for (FlexItem& item : Items()) {
   4603    // First, stretch the item's cross size (if appropriate), and resolve any
   4604    // auto margins in this axis.
   4605    item.ResolveStretchedCrossSize(mLineCrossSize);
   4606    lineCrossAxisPosnTracker.ResolveAutoMarginsInCrossAxis(*this, item);
   4607 
   4608    // Compute the cross-axis position of this item
   4609    nscoord itemCrossBorderBoxSize =
   4610        item.CrossSize() + item.BorderPaddingSizeInCrossAxis();
   4611    lineCrossAxisPosnTracker.EnterAlignPackingSpace(*this, item, aAxisTracker);
   4612    lineCrossAxisPosnTracker.EnterMargin(item.Margin());
   4613    lineCrossAxisPosnTracker.EnterChildFrame(itemCrossBorderBoxSize);
   4614 
   4615    item.SetCrossPosition(aLineStartPosition +
   4616                          lineCrossAxisPosnTracker.Position());
   4617 
   4618    // Back out to cross-axis edge of the line.
   4619    lineCrossAxisPosnTracker.ResetPosition();
   4620  }
   4621 }
   4622 
   4623 void nsFlexContainerFrame::Reflow(nsPresContext* aPresContext,
   4624                                  ReflowOutput& aReflowOutput,
   4625                                  const ReflowInput& aReflowInput,
   4626                                  nsReflowStatus& aStatus) {
   4627  if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
   4628    return;
   4629  }
   4630 
   4631  MarkInReflow();
   4632  DO_GLOBAL_REFLOW_COUNT("nsFlexContainerFrame");
   4633  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
   4634  MOZ_ASSERT(aPresContext == PresContext());
   4635  NS_WARNING_ASSERTION(
   4636      aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE,
   4637      "Unconstrained inline size; this should only result from huge sizes "
   4638      "(not intrinsic sizing w/ orthogonal flows)");
   4639 
   4640  FLEX_LOG("Reflow flex container frame %p", this);
   4641 
   4642  if (IsFrameTreeTooDeep(aReflowInput, aReflowOutput, aStatus)) {
   4643    return;
   4644  }
   4645 
   4646  NormalizeChildLists();
   4647 
   4648 #ifdef DEBUG
   4649  mDidPushItemsBitMayLie = false;
   4650  SanityCheckChildListsBeforeReflow();
   4651 #endif  // DEBUG
   4652 
   4653  // We (and our children) can only depend on our ancestor's bsize if we have
   4654  // a percent-bsize, or if we're positioned and we have "block-start" and
   4655  // "block-end" set and have block-size:auto.  (There are actually other cases,
   4656  // too -- e.g. if our parent is itself a block-dir flex container and we're
   4657  // flexible -- but we'll let our ancestors handle those sorts of cases.)
   4658  //
   4659  // TODO(emilio): the !bsize.IsLengthPercentage() preserves behavior, but it's
   4660  // too conservative. min/max-content don't really depend on the container.
   4661  WritingMode wm = aReflowInput.GetWritingMode();
   4662  const nsStylePosition* stylePos = StylePosition();
   4663  const auto anchorResolutionParams =
   4664      AnchorPosOffsetResolutionParams::UseCBFrameSize(
   4665          AnchorPosResolutionParams::From(this));
   4666  const auto bsize = stylePos->BSize(wm, anchorResolutionParams.mBaseParams);
   4667  if (bsize->HasPercent() ||
   4668      (StyleDisplay()->IsAbsolutelyPositionedStyle() &&
   4669       (bsize->IsAuto() || !bsize->IsLengthPercentage()) &&
   4670       !stylePos
   4671            ->GetAnchorResolvedInset(LogicalSide::BStart, wm,
   4672                                     anchorResolutionParams)
   4673            ->IsAuto() &&
   4674       !stylePos
   4675            ->GetAnchorResolvedInset(LogicalSide::BEnd, wm,
   4676                                     anchorResolutionParams)
   4677            ->IsAuto())) {
   4678    AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
   4679  }
   4680 
   4681  const FlexboxAxisTracker axisTracker(this);
   4682 
   4683  // Check to see if we need to create a computed info structure, to
   4684  // be filled out for use by devtools.
   4685  ComputedFlexContainerInfo* containerInfo = CreateOrClearFlexContainerInfo();
   4686 
   4687  FlexLayoutResult flr;
   4688  PerFragmentFlexData fragmentData;
   4689  const nsIFrame* prevInFlow = GetPrevInFlow();
   4690  if (!prevInFlow) {
   4691    const LogicalSize tentativeContentBoxSize = aReflowInput.ComputedSize();
   4692    const nscoord tentativeContentBoxMainSize =
   4693        axisTracker.MainComponent(tentativeContentBoxSize);
   4694    const nscoord tentativeContentBoxCrossSize =
   4695        axisTracker.CrossComponent(tentativeContentBoxSize);
   4696 
   4697    // Calculate gap sizes for main and cross axis. We only need them in
   4698    // DoFlexLayout in the first-in-flow, so no need to worry about consumed
   4699    // block-size.
   4700    const auto& mainGapStyle =
   4701        axisTracker.IsRowOriented() ? stylePos->mColumnGap : stylePos->mRowGap;
   4702    const auto& crossGapStyle =
   4703        axisTracker.IsRowOriented() ? stylePos->mRowGap : stylePos->mColumnGap;
   4704    const nscoord mainGapSize = nsLayoutUtils::ResolveGapToLength(
   4705        mainGapStyle, tentativeContentBoxMainSize);
   4706    const nscoord crossGapSize = nsLayoutUtils::ResolveGapToLength(
   4707        crossGapStyle, tentativeContentBoxCrossSize);
   4708 
   4709    // When fragmenting a flex container, we run the flex algorithm without
   4710    // regards to pagination in order to compute the flex container's desired
   4711    // content-box size. https://drafts.csswg.org/css-flexbox-1/#pagination-algo
   4712    //
   4713    // Note: For a multi-line column-oriented flex container, the sample
   4714    // algorithm suggests we wrap the flex line at the block-end edge of a
   4715    // column/page, but we do not implement it intentionally. This brings the
   4716    // layout result closer to the one as if there's no fragmentation.
   4717    AutoTArray<StrutInfo, 1> struts;
   4718    flr = DoFlexLayout(aReflowInput, tentativeContentBoxMainSize,
   4719                       tentativeContentBoxCrossSize, axisTracker, mainGapSize,
   4720                       crossGapSize, struts, containerInfo);
   4721 
   4722    if (!struts.IsEmpty()) {
   4723      // We're restarting flex layout, with new knowledge of collapsed items.
   4724      flr.mLines.Clear();
   4725      flr.mPlaceholders.Clear();
   4726      flr = DoFlexLayout(aReflowInput, tentativeContentBoxMainSize,
   4727                         tentativeContentBoxCrossSize, axisTracker, mainGapSize,
   4728                         crossGapSize, struts, containerInfo);
   4729    }
   4730  } else {
   4731    flr = GenerateFlexLayoutResult();
   4732    auto* fragmentDataProp =
   4733        prevInFlow->GetProperty(PerFragmentFlexData::Prop());
   4734    MOZ_ASSERT(fragmentDataProp,
   4735               "PerFragmentFlexData should be set in our prev-in-flow!");
   4736    fragmentData = *fragmentDataProp;
   4737  }
   4738 
   4739  LogicalSize contentBoxSize = axisTracker.LogicalSizeFromFlexRelativeSizes(
   4740      flr.mContentBoxMainSize, flr.mContentBoxCrossSize);
   4741 
   4742  const nscoord consumedBSize = CalcAndCacheConsumedBSize();
   4743  const nscoord effectiveContentBSize =
   4744      contentBoxSize.BSize(wm) - consumedBSize;
   4745  LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding(wm);
   4746  if (MOZ_UNLIKELY(aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)) {
   4747    // We assume we are the last fragment by using
   4748    // PreReflowBlockLevelLogicalSkipSides(), and skip block-end border and
   4749    // padding if needed.
   4750    borderPadding.ApplySkipSides(PreReflowBlockLevelLogicalSkipSides());
   4751  }
   4752 
   4753  // Determine this frame's tentative border-box size. This is used for logical
   4754  // to physical coordinate conversion when positioning children.
   4755  //
   4756  // Note that vertical-rl writing-mode is the only case where the block flow
   4757  // direction progresses in a negative physical direction, and therefore block
   4758  // direction coordinate conversion depends on knowing the width of the
   4759  // coordinate space in order to translate between the logical and physical
   4760  // origins. As a result, if our final border-box block-size is different from
   4761  // this tentative one, and we are in vertical-rl writing mode, we need to
   4762  // adjust our children's position after reflowing them.
   4763  const LogicalSize tentativeBorderBoxSize(
   4764      wm, contentBoxSize.ISize(wm) + borderPadding.IStartEnd(wm),
   4765      std::min(effectiveContentBSize + borderPadding.BStartEnd(wm),
   4766               aReflowInput.AvailableBSize()));
   4767  const nsSize containerSize = tentativeBorderBoxSize.GetPhysicalSize(wm);
   4768 
   4769  OverflowAreas ocBounds;
   4770  nsReflowStatus ocStatus;
   4771  if (prevInFlow) {
   4772    ReflowOverflowContainerChildren(
   4773        aPresContext, aReflowInput, ocBounds, ReflowChildFlags::Default,
   4774        ocStatus, MergeSortedFrameListsFor, Some(containerSize));
   4775  }
   4776 
   4777  const LogicalSize availableSizeForItems =
   4778      ComputeAvailableSizeForItems(aReflowInput, borderPadding);
   4779  const auto [childrenBEndEdge, childrenStatus] =
   4780      ReflowChildren(aReflowInput, containerSize, availableSizeForItems,
   4781                     borderPadding, axisTracker, flr, fragmentData);
   4782 
   4783  bool mayNeedNextInFlow = false;
   4784  if (aReflowInput.IsInFragmentedContext()) {
   4785    // This fragment's contribution to the flex container's cumulative
   4786    // content-box block-size, if it turns out that this is the final vs.
   4787    // non-final fragment:
   4788    //
   4789    // * If it turns out we *are* the final fragment, then this fragment's
   4790    // content-box contribution is the distance from the start of our content
   4791    // box to the block-end edge of our children (note the borderPadding
   4792    // subtraction is just to get us to a content-box-relative offset here):
   4793    const nscoord bSizeContributionIfFinalFragment =
   4794        childrenBEndEdge - borderPadding.BStart(wm);
   4795 
   4796    // * If it turns out we're *not* the final fragment, then this fragment's
   4797    // content-box extends to the edge of the availableSizeForItems (at least),
   4798    // regardless of whether we actually have items at that location:
   4799    const nscoord bSizeContributionIfNotFinalFragment = std::max(
   4800        bSizeContributionIfFinalFragment, availableSizeForItems.BSize(wm));
   4801 
   4802    // mCumulativeBEndEdgeShift was updated in ReflowChildren(), and our
   4803    // children's block-size may grow in fragmented context. If our block-size
   4804    // and max-block-size are unconstrained, then we allow the flex container to
   4805    // grow to accommodate any children whose sizes grew as a result of
   4806    // fragmentation.
   4807    if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE) {
   4808      contentBoxSize.BSize(wm) = aReflowInput.ApplyMinMaxBSize(
   4809          contentBoxSize.BSize(wm) + fragmentData.mCumulativeBEndEdgeShift);
   4810 
   4811      if (childrenStatus.IsComplete()) {
   4812        // All of the children fit! We know that we're using a content-based
   4813        // block-size, and we know our children's block-size may have grown due
   4814        // to fragmentation. So we allow ourselves to grow our block-size here
   4815        // to contain the block-end edge of our last child (subject to our
   4816        // min/max constraints).
   4817        contentBoxSize.BSize(wm) = aReflowInput.ApplyMinMaxBSize(std::max(
   4818            contentBoxSize.BSize(wm), fragmentData.mCumulativeContentBoxBSize +
   4819                                          bSizeContributionIfFinalFragment));
   4820      } else {
   4821        // As in the if-branch above, we extend our block-size, but in this case
   4822        // we know that a child didn't fit and might overshot our available
   4823        // size, so we assume this fragment won't be the final fragment, and
   4824        // hence it should contribute bSizeContributionIfNotFinalFragment
   4825        // (subject to our min/max constraints).
   4826        contentBoxSize.BSize(wm) = aReflowInput.ApplyMinMaxBSize(std::max(
   4827            contentBoxSize.BSize(wm), fragmentData.mCumulativeContentBoxBSize +
   4828                                          bSizeContributionIfNotFinalFragment));
   4829 
   4830        if (aReflowInput.ComputedMaxBSize() == NS_UNCONSTRAINEDSIZE) {
   4831          mayNeedNextInFlow = true;
   4832        } else {
   4833          // The definite max-block-size can be the upper bound of our
   4834          // content-box block-size. We should check whether we need a
   4835          // next-in-flow.
   4836          mayNeedNextInFlow = contentBoxSize.BSize(wm) - consumedBSize >
   4837                              availableSizeForItems.BSize(wm);
   4838        }
   4839      }
   4840    } else {
   4841      mayNeedNextInFlow = contentBoxSize.BSize(wm) - consumedBSize >
   4842                          availableSizeForItems.BSize(wm);
   4843    }
   4844    fragmentData.mCumulativeContentBoxBSize +=
   4845        bSizeContributionIfNotFinalFragment;
   4846 
   4847    // If we may need a next-in-flow, we'll need to skip block-end border and
   4848    // padding.
   4849    if (mayNeedNextInFlow && aReflowInput.mStyleBorder->mBoxDecorationBreak ==
   4850                                 StyleBoxDecorationBreak::Slice) {
   4851      borderPadding.BEnd(wm) = 0;
   4852    }
   4853  }
   4854 
   4855  PopulateReflowOutput(aReflowOutput, aReflowInput, aStatus, contentBoxSize,
   4856                       borderPadding, consumedBSize, mayNeedNextInFlow,
   4857                       childrenBEndEdge, childrenStatus, axisTracker, flr);
   4858 
   4859  if (wm.IsVerticalRL()) {
   4860    // If the final border-box block-size is different from the tentative one,
   4861    // adjust our children's position.
   4862    const nscoord deltaBCoord =
   4863        tentativeBorderBoxSize.BSize(wm) - aReflowOutput.Size(wm).BSize(wm);
   4864    if (deltaBCoord != 0) {
   4865      const LogicalPoint delta(wm, 0, deltaBCoord);
   4866      for (const FlexLine& line : flr.mLines) {
   4867        for (const FlexItem& item : line.Items()) {
   4868          item.Frame()->MovePositionBy(wm, delta);
   4869        }
   4870      }
   4871    }
   4872  }
   4873 
   4874  // Overflow area = union(my overflow area, children's overflow areas)
   4875  aReflowOutput.SetOverflowAreasToDesiredBounds();
   4876  UnionInFlowChildOverflow(aReflowOutput.mOverflowAreas);
   4877 
   4878  // Merge overflow container bounds and status.
   4879  aReflowOutput.mOverflowAreas.UnionWith(ocBounds);
   4880  aStatus.MergeCompletionStatusFrom(ocStatus);
   4881 
   4882  FinishReflowWithAbsoluteFrames(PresContext(), aReflowOutput, aReflowInput,
   4883                                 aStatus);
   4884 
   4885  // Finally update our line and item measurements in our containerInfo.
   4886  if (MOZ_UNLIKELY(containerInfo)) {
   4887    UpdateFlexLineAndItemInfo(*containerInfo, flr.mLines);
   4888  }
   4889 
   4890  // If we are the first-in-flow, we want to store data for our next-in-flows,
   4891  // or clear the existing data if it is not needed.
   4892  if (!prevInFlow) {
   4893    SharedFlexData* sharedData = GetProperty(SharedFlexData::Prop());
   4894    if (!aStatus.IsFullyComplete()) {
   4895      if (!sharedData) {
   4896        sharedData = new SharedFlexData;
   4897        SetProperty(SharedFlexData::Prop(), sharedData);
   4898      }
   4899      sharedData->Update(std::move(flr));
   4900    } else if (sharedData && !GetNextInFlow()) {
   4901      // We are fully-complete, so no next-in-flow is needed. However, if we
   4902      // report SetInlineLineBreakBeforeAndReset() in an incremental reflow, our
   4903      // next-in-flow might still exist. It can be reflowed again before us if
   4904      // it is an overflow container. Delete the existing data only if we don't
   4905      // have a next-in-flow.
   4906      RemoveProperty(SharedFlexData::Prop());
   4907    }
   4908  }
   4909 
   4910  PerFragmentFlexData* fragmentDataProp =
   4911      GetProperty(PerFragmentFlexData::Prop());
   4912  if (!aStatus.IsFullyComplete()) {
   4913    if (!fragmentDataProp) {
   4914      fragmentDataProp = new PerFragmentFlexData;
   4915      SetProperty(PerFragmentFlexData::Prop(), fragmentDataProp);
   4916    }
   4917    *fragmentDataProp = fragmentData;
   4918  } else if (fragmentDataProp && !GetNextInFlow()) {
   4919    // Similar to the condition to remove SharedFlexData, delete the
   4920    // existing data only if we don't have a next-in-flow.
   4921    RemoveProperty(PerFragmentFlexData::Prop());
   4922  }
   4923 }
   4924 
   4925 Maybe<nscoord> nsFlexContainerFrame::GetNaturalBaselineBOffset(
   4926    WritingMode aWM, BaselineSharingGroup aBaselineGroup,
   4927    BaselineExportContext) const {
   4928  if (StyleDisplay()->IsContainLayout() ||
   4929      HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
   4930    return Nothing{};
   4931  }
   4932  return Some(aBaselineGroup == BaselineSharingGroup::First ? mFirstBaseline
   4933                                                            : mLastBaseline);
   4934 }
   4935 
   4936 void nsFlexContainerFrame::UnionInFlowChildOverflow(
   4937    OverflowAreas& aOverflowAreas, bool aAsIfScrolled) {
   4938  // The CSS Overflow spec [1] requires that a scrollable container's
   4939  // scrollable overflow should include the following areas.
   4940  //
   4941  // a) "the box's own content and padding areas": we treat the *content* as
   4942  // the scrolled inner frame's theoretical content-box that's intrinsically
   4943  // sized to the union of all the flex items' margin boxes, _without_
   4944  // relative positioning applied. The *padding areas* is just inflation on
   4945  // top of the theoretical content-box by the flex container's padding.
   4946  //
   4947  // b) "the margin areas of grid item and flex item boxes for which the box
   4948  // establishes a containing block": a) already includes the flex items'
   4949  // normal-positioned margin boxes into the scrollable overflow, but their
   4950  // relative-positioned margin boxes should also be included because relpos
   4951  // children are still flex items.
   4952  //
   4953  // [1] https://drafts.csswg.org/css-overflow-3/#scrollable.
   4954  const bool isScrolledContent =
   4955      aAsIfScrolled ||
   4956      Style()->GetPseudoType() == PseudoStyleType::scrolledContent;
   4957  bool anyScrolledContentItem = false;
   4958  // Union of normal-positioned margin boxes for all the items.
   4959  nsRect itemMarginBoxes;
   4960  // Overflow areas containing the union of relative-positioned and
   4961  // stick-positioned margin boxes of relpos items.
   4962  //
   4963  // Note for sticky-positioned margin boxes, we only union it with the ink
   4964  // overflow to avoid circular dependencies with the scroll container. (The
   4965  // scroll position and the scroll container's size impact the sticky position,
   4966  // so we don't want the sticky position to impact them.)
   4967  OverflowAreas relPosItemMarginBoxes;
   4968  const bool useMozBoxCollapseBehavior =
   4969      StyleVisibility()->UseLegacyCollapseBehavior();
   4970  for (nsIFrame* f : mFrames) {
   4971    if (useMozBoxCollapseBehavior && f->StyleVisibility()->IsCollapse()) {
   4972      continue;
   4973    }
   4974    ConsiderChildOverflow(aOverflowAreas, f, aAsIfScrolled);
   4975    if (!isScrolledContent) {
   4976      continue;
   4977    }
   4978    if (f->IsPlaceholderFrame()) {
   4979      continue;
   4980    }
   4981    anyScrolledContentItem = true;
   4982    if (MOZ_UNLIKELY(f->IsRelativelyOrStickyPositioned())) {
   4983      const nsRect marginRect = f->GetMarginRectRelativeToSelf();
   4984      itemMarginBoxes =
   4985          itemMarginBoxes.Union(marginRect + f->GetNormalPosition());
   4986      if (f->IsRelativelyPositioned()) {
   4987        relPosItemMarginBoxes.UnionAllWith(marginRect + f->GetPosition());
   4988      } else {
   4989        MOZ_ASSERT(f->IsStickyPositioned());
   4990        relPosItemMarginBoxes.UnionWith(
   4991            OverflowAreas(marginRect + f->GetPosition(), nsRect()));
   4992      }
   4993    } else {
   4994      itemMarginBoxes = itemMarginBoxes.Union(f->GetMarginRect());
   4995    }
   4996  }
   4997 
   4998  if (anyScrolledContentItem) {
   4999    itemMarginBoxes.Inflate(GetUsedPadding());
   5000    aOverflowAreas.UnionAllWith(itemMarginBoxes);
   5001    aOverflowAreas.UnionWith(relPosItemMarginBoxes);
   5002  }
   5003 }
   5004 
   5005 void nsFlexContainerFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
   5006                                              bool aAsIfScrolled) {
   5007  UnionInFlowChildOverflow(aOverflowAreas, aAsIfScrolled);
   5008  // Union with child frames, skipping the principal list since we already
   5009  // handled those above.
   5010  nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas,
   5011                                    {FrameChildListID::Principal});
   5012 }
   5013 
   5014 void nsFlexContainerFrame::CalculatePackingSpace(
   5015    uint32_t aNumThingsToPack, const StyleContentDistribution& aAlignVal,
   5016    nscoord* aFirstSubjectOffset, uint32_t* aNumPackingSpacesRemaining,
   5017    nscoord* aPackingSpaceRemaining) {
   5018  StyleAlignFlags val = aAlignVal.primary;
   5019  MOZ_ASSERT(val == StyleAlignFlags::SPACE_BETWEEN ||
   5020                 val == StyleAlignFlags::SPACE_AROUND ||
   5021                 val == StyleAlignFlags::SPACE_EVENLY,
   5022             "Unexpected alignment value");
   5023 
   5024  MOZ_ASSERT(*aPackingSpaceRemaining >= 0,
   5025             "Should not be called with negative packing space");
   5026 
   5027  // Note: In the aNumThingsToPack==1 case, the fallback behavior for
   5028  // 'space-between' depends on precise information about the axes that we
   5029  // don't have here. So, for that case, we just depend on the caller to
   5030  // explicitly convert 'space-{between,around,evenly}' keywords to the
   5031  // appropriate fallback alignment and skip this function.
   5032  MOZ_ASSERT(aNumThingsToPack > 1,
   5033             "Should not be called unless there's more than 1 thing to pack");
   5034 
   5035  // Packing spaces between items:
   5036  *aNumPackingSpacesRemaining = aNumThingsToPack - 1;
   5037 
   5038  if (val == StyleAlignFlags::SPACE_BETWEEN) {
   5039    // No need to reserve space at beginning/end, so we're done.
   5040    return;
   5041  }
   5042 
   5043  // We need to add 1 or 2 packing spaces, split between beginning/end, for
   5044  // space-around / space-evenly:
   5045  size_t numPackingSpacesForEdges =
   5046      val == StyleAlignFlags::SPACE_AROUND ? 1 : 2;
   5047 
   5048  // How big will each "full" packing space be:
   5049  nscoord packingSpaceSize =
   5050      *aPackingSpaceRemaining /
   5051      (*aNumPackingSpacesRemaining + numPackingSpacesForEdges);
   5052  // How much packing-space are we allocating to the edges:
   5053  nscoord totalEdgePackingSpace = numPackingSpacesForEdges * packingSpaceSize;
   5054 
   5055  // Use half of that edge packing space right now:
   5056  *aFirstSubjectOffset += totalEdgePackingSpace / 2;
   5057  // ...but we need to subtract all of it right away, so that we won't
   5058  // hand out any of it to intermediate packing spaces.
   5059  *aPackingSpaceRemaining -= totalEdgePackingSpace;
   5060 }
   5061 
   5062 ComputedFlexContainerInfo*
   5063 nsFlexContainerFrame::CreateOrClearFlexContainerInfo() {
   5064  if (!HasAnyStateBits(NS_STATE_FLEX_COMPUTED_INFO)) {
   5065    return nullptr;
   5066  }
   5067 
   5068  // The flag that sets ShouldGenerateComputedInfo() will never be cleared.
   5069  // That's acceptable because it's only set in a Chrome API invoked by
   5070  // devtools, and won't impact normal browsing.
   5071 
   5072  // Re-use the ComputedFlexContainerInfo, if it exists.
   5073  ComputedFlexContainerInfo* info = GetProperty(FlexContainerInfo());
   5074  if (info) {
   5075    // We can reuse, as long as we clear out old data.
   5076    info->mLines.Clear();
   5077  } else {
   5078    info = new ComputedFlexContainerInfo();
   5079    SetProperty(FlexContainerInfo(), info);
   5080  }
   5081 
   5082  return info;
   5083 }
   5084 
   5085 nscoord nsFlexContainerFrame::FlexItemConsumedBSize(const FlexItem& aItem) {
   5086  nsSplittableFrame* f = do_QueryFrame(aItem.Frame());
   5087  return f ? ConsumedBSize(f) : 0;
   5088 }
   5089 
   5090 void nsFlexContainerFrame::CreateFlexLineAndFlexItemInfo(
   5091    ComputedFlexContainerInfo& aContainerInfo,
   5092    const nsTArray<FlexLine>& aLines) {
   5093  for (const FlexLine& line : aLines) {
   5094    ComputedFlexLineInfo* lineInfo = aContainerInfo.mLines.AppendElement();
   5095    // Most of the remaining lineInfo properties will be filled out in
   5096    // UpdateFlexLineAndItemInfo (some will be provided by other functions),
   5097    // when we have real values. But we still add all the items here, so
   5098    // we can capture computed data for each item as we proceed.
   5099    for (const FlexItem& item : line.Items()) {
   5100      nsIFrame* frame = item.Frame();
   5101 
   5102      // The frame may be for an element, or it may be for an
   5103      // anonymous flex item, e.g. wrapping one or more text nodes.
   5104      // DevTools wants the content node for the actual child in
   5105      // the DOM tree, so we descend through anonymous boxes.
   5106      nsIContent* content = nullptr;
   5107      nsIFrame* targetFrame = GetFirstNonAnonBoxInSubtree(frame);
   5108      if (targetFrame) {
   5109        content = targetFrame->GetContent();
   5110      }
   5111 
   5112      // Skip over content that is only whitespace, which might
   5113      // have been broken off from a text node which is our real
   5114      // target.
   5115      while (content && content->TextIsOnlyWhitespace()) {
   5116        // If content is only whitespace, try the frame sibling.
   5117        targetFrame = targetFrame->GetNextSibling();
   5118        if (targetFrame) {
   5119          content = targetFrame->GetContent();
   5120        } else {
   5121          content = nullptr;
   5122        }
   5123      }
   5124 
   5125      ComputedFlexItemInfo* itemInfo = lineInfo->mItems.AppendElement();
   5126 
   5127      itemInfo->mNode = content;
   5128 
   5129      // itemInfo->mMainBaseSize and mMainDeltaSize will be filled out
   5130      // in ResolveFlexibleLengths(). Other measurements will be captured in
   5131      // UpdateFlexLineAndItemInfo.
   5132    }
   5133  }
   5134 }
   5135 
   5136 void nsFlexContainerFrame::ComputeFlexDirections(
   5137    ComputedFlexContainerInfo& aContainerInfo,
   5138    const FlexboxAxisTracker& aAxisTracker) {
   5139  auto ConvertPhysicalStartSideToFlexPhysicalDirection =
   5140      [](mozilla::Side aStartSide) {
   5141        switch (aStartSide) {
   5142          case eSideLeft:
   5143            return dom::FlexPhysicalDirection::Horizontal_lr;
   5144          case eSideRight:
   5145            return dom::FlexPhysicalDirection::Horizontal_rl;
   5146          case eSideTop:
   5147            return dom::FlexPhysicalDirection::Vertical_tb;
   5148          case eSideBottom:
   5149            return dom::FlexPhysicalDirection::Vertical_bt;
   5150        }
   5151 
   5152        MOZ_ASSERT_UNREACHABLE("We should handle all sides!");
   5153        return dom::FlexPhysicalDirection::Horizontal_lr;
   5154      };
   5155 
   5156  aContainerInfo.mMainAxisDirection =
   5157      ConvertPhysicalStartSideToFlexPhysicalDirection(
   5158          aAxisTracker.MainAxisPhysicalStartSide());
   5159  aContainerInfo.mCrossAxisDirection =
   5160      ConvertPhysicalStartSideToFlexPhysicalDirection(
   5161          aAxisTracker.CrossAxisPhysicalStartSide());
   5162 }
   5163 
   5164 void nsFlexContainerFrame::UpdateFlexLineAndItemInfo(
   5165    ComputedFlexContainerInfo& aContainerInfo,
   5166    const nsTArray<FlexLine>& aLines) {
   5167  uint32_t lineIndex = 0;
   5168  for (const FlexLine& line : aLines) {
   5169    ComputedFlexLineInfo& lineInfo = aContainerInfo.mLines[lineIndex];
   5170 
   5171    lineInfo.mCrossSize = line.LineCrossSize();
   5172    lineInfo.mFirstBaselineOffset = line.FirstBaselineOffset();
   5173    lineInfo.mLastBaselineOffset = line.LastBaselineOffset();
   5174 
   5175    uint32_t itemIndex = 0;
   5176    for (const FlexItem& item : line.Items()) {
   5177      ComputedFlexItemInfo& itemInfo = lineInfo.mItems[itemIndex];
   5178      itemInfo.mFrameRect = item.Frame()->GetRect();
   5179      itemInfo.mMainMinSize = item.MainMinSize();
   5180      itemInfo.mMainMaxSize = item.MainMaxSize();
   5181      itemInfo.mCrossMinSize = item.CrossMinSize();
   5182      itemInfo.mCrossMaxSize = item.CrossMaxSize();
   5183      itemInfo.mClampState =
   5184          item.WasMinClamped()
   5185              ? mozilla::dom::FlexItemClampState::Clamped_to_min
   5186              : (item.WasMaxClamped()
   5187                     ? mozilla::dom::FlexItemClampState::Clamped_to_max
   5188                     : mozilla::dom::FlexItemClampState::Unclamped);
   5189      ++itemIndex;
   5190    }
   5191    ++lineIndex;
   5192  }
   5193 }
   5194 
   5195 nsFlexContainerFrame* nsFlexContainerFrame::GetFlexFrameWithComputedInfo(
   5196    nsIFrame* aFrame) {
   5197  // Prepare a lambda function that we may need to call multiple times.
   5198  auto GetFlexContainerFrame = [](nsIFrame* aFrame) -> nsFlexContainerFrame* {
   5199    // Return the aFrame's content insertion frame, iff it is a flex container.
   5200    if (!aFrame) {
   5201      return nullptr;
   5202    }
   5203    return do_QueryFrame(aFrame->GetContentInsertionFrame());
   5204  };
   5205 
   5206  nsFlexContainerFrame* flexFrame = GetFlexContainerFrame(aFrame);
   5207  if (!flexFrame) {
   5208    return nullptr;
   5209  }
   5210  // Generate the FlexContainerInfo data, if it's not already there.
   5211  if (flexFrame->HasProperty(FlexContainerInfo())) {
   5212    return flexFrame;
   5213  }
   5214  // Trigger a reflow that generates additional flex property data.
   5215  // Hold onto aFrame while we do this, in case reflow destroys it.
   5216  AutoWeakFrame weakFrameRef(aFrame);
   5217 
   5218  RefPtr<mozilla::PresShell> presShell = flexFrame->PresShell();
   5219  flexFrame->AddStateBits(NS_STATE_FLEX_COMPUTED_INFO);
   5220  presShell->FrameNeedsReflow(flexFrame, IntrinsicDirty::None,
   5221                              NS_FRAME_IS_DIRTY);
   5222  presShell->FlushPendingNotifications(FlushType::Layout);
   5223 
   5224  // Since the reflow may have side effects, get the flex frame
   5225  // again. But if the weakFrameRef is no longer valid, then we
   5226  // must bail out.
   5227  if (!weakFrameRef.IsAlive()) {
   5228    return nullptr;
   5229  }
   5230 
   5231  flexFrame = GetFlexContainerFrame(weakFrameRef.GetFrame());
   5232 
   5233  NS_WARNING_ASSERTION(
   5234      !flexFrame || flexFrame->HasProperty(FlexContainerInfo()),
   5235      "The state bit should've made our forced-reflow "
   5236      "generate a FlexContainerInfo object");
   5237  return flexFrame;
   5238 }
   5239 
   5240 /* static */
   5241 bool nsFlexContainerFrame::IsItemInlineAxisMainAxis(nsIFrame* aFrame) {
   5242  MOZ_ASSERT(aFrame && aFrame->IsFlexItem(), "expecting arg to be a flex item");
   5243  const WritingMode flexItemWM = aFrame->GetWritingMode();
   5244  const nsIFrame* flexContainer = aFrame->GetParent();
   5245 
   5246  if (flexContainer->IsLegacyWebkitBox()) {
   5247    // For legacy boxes, the main axis is determined by "box-orient", and we can
   5248    // just directly check if that's vertical, and compare that to whether the
   5249    // item's WM is also vertical:
   5250    bool boxOrientIsVertical =
   5251        flexContainer->StyleXUL()->mBoxOrient == StyleBoxOrient::Vertical;
   5252    return flexItemWM.IsVertical() == boxOrientIsVertical;
   5253  }
   5254 
   5255  // For modern CSS flexbox, we get our return value by asking two questions
   5256  // and comparing their answers.
   5257  // Question 1: does aFrame have the same inline axis as its flex container?
   5258  bool itemInlineAxisIsParallelToParent =
   5259      !flexItemWM.IsOrthogonalTo(flexContainer->GetWritingMode());
   5260 
   5261  // Question 2: is aFrame's flex container row-oriented? (This tells us
   5262  // whether the flex container's main axis is its inline axis.)
   5263  auto flexDirection = flexContainer->StylePosition()->mFlexDirection;
   5264  bool flexContainerIsRowOriented =
   5265      flexDirection == StyleFlexDirection::Row ||
   5266      flexDirection == StyleFlexDirection::RowReverse;
   5267 
   5268  // aFrame's inline axis is its flex container's main axis IFF the above
   5269  // questions have the same answer.
   5270  return flexContainerIsRowOriented == itemInlineAxisIsParallelToParent;
   5271 }
   5272 
   5273 /* static */
   5274 bool nsFlexContainerFrame::IsUsedFlexBasisContent(
   5275    const StyleFlexBasis& aFlexBasis, const StyleSize& aMainSize) {
   5276  // We have a used flex-basis of 'content' if flex-basis explicitly has that
   5277  // value, OR if flex-basis is 'auto' (deferring to the main-size property)
   5278  // and the main-size property is also 'auto'.
   5279  // See https://drafts.csswg.org/css-flexbox-1/#valdef-flex-basis-auto
   5280  if (aFlexBasis.IsContent()) {
   5281    return true;
   5282  }
   5283  return aFlexBasis.IsAuto() && aMainSize.IsAuto();
   5284 }
   5285 
   5286 nsFlexContainerFrame::FlexLayoutResult nsFlexContainerFrame::DoFlexLayout(
   5287    const ReflowInput& aReflowInput, const nscoord aTentativeContentBoxMainSize,
   5288    const nscoord aTentativeContentBoxCrossSize,
   5289    const FlexboxAxisTracker& aAxisTracker, nscoord aMainGapSize,
   5290    nscoord aCrossGapSize, nsTArray<StrutInfo>& aStruts,
   5291    ComputedFlexContainerInfo* const aContainerInfo) {
   5292  FlexLayoutResult flr;
   5293 
   5294  GenerateFlexLines(aReflowInput, aTentativeContentBoxMainSize,
   5295                    aTentativeContentBoxCrossSize, aStruts, aAxisTracker,
   5296                    aMainGapSize, flr.mPlaceholders, flr.mLines,
   5297                    flr.mHasCollapsedItems);
   5298 
   5299  if ((flr.mLines.Length() == 1 && flr.mLines[0].IsEmpty()) ||
   5300      aReflowInput.mStyleDisplay->IsContainLayout()) {
   5301    // We have no flex items, or we're layout-contained. So, we have no
   5302    // baseline, and our parent should synthesize a baseline if needed.
   5303    AddStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
   5304  } else {
   5305    RemoveStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
   5306  }
   5307 
   5308  // Construct our computed info if we've been asked to do so. This is
   5309  // necessary to do now so we can capture some computed values for
   5310  // FlexItems during layout that would not otherwise be saved (like
   5311  // size adjustments). We'll later fix up the line properties,
   5312  // because the correct values aren't available yet.
   5313  if (aContainerInfo) {
   5314    MOZ_ASSERT(HasAnyStateBits(NS_STATE_FLEX_COMPUTED_INFO),
   5315               "We should only have the info struct if we should generate it");
   5316 
   5317    if (!aStruts.IsEmpty()) {
   5318      // We restarted DoFlexLayout, and may have stale mLines to clear:
   5319      aContainerInfo->mLines.Clear();
   5320    } else {
   5321      MOZ_ASSERT(aContainerInfo->mLines.IsEmpty(), "Shouldn't have lines yet.");
   5322    }
   5323 
   5324    CreateFlexLineAndFlexItemInfo(*aContainerInfo, flr.mLines);
   5325    ComputeFlexDirections(*aContainerInfo, aAxisTracker);
   5326  }
   5327 
   5328  flr.mContentBoxMainSize = ComputeMainSize(
   5329      aReflowInput, aAxisTracker, aTentativeContentBoxMainSize, flr.mLines);
   5330 
   5331  uint32_t lineIndex = 0;
   5332  for (FlexLine& line : flr.mLines) {
   5333    ComputedFlexLineInfo* lineInfo =
   5334        aContainerInfo ? &aContainerInfo->mLines[lineIndex] : nullptr;
   5335    line.ResolveFlexibleLengths(flr.mContentBoxMainSize, lineInfo);
   5336    ++lineIndex;
   5337  }
   5338 
   5339  // Cross Size Determination - Flexbox spec section 9.4
   5340  // https://drafts.csswg.org/css-flexbox-1/#cross-sizing
   5341  // ===================================================
   5342  // Calculate the hypothetical cross size of each item:
   5343 
   5344  // 'sumLineCrossSizes' includes the size of all gaps between lines. We
   5345  // initialize it with the sum of all the gaps, and add each line's cross size
   5346  // at the end of the following for-loop.
   5347  nscoord sumLineCrossSizes = aCrossGapSize * (flr.mLines.Length() - 1);
   5348  for (FlexLine& line : flr.mLines) {
   5349    for (FlexItem& item : line.Items()) {
   5350      // The item may already have the correct cross-size; only recalculate
   5351      // if the item's main size resolution (flexing) could have influenced it:
   5352      if (item.CanMainSizeInfluenceCrossSize()) {
   5353        StyleSizeOverrides sizeOverrides;
   5354        if (item.IsInlineAxisMainAxis()) {
   5355          sizeOverrides.mStyleISize.emplace(item.StyleMainSize());
   5356        } else {
   5357          sizeOverrides.mStyleBSize.emplace(item.StyleMainSize());
   5358        }
   5359        FLEX_ITEM_LOG(item.Frame(), "Sizing item in cross axis");
   5360        FLEX_LOGV("Main size override: %d", item.MainSize());
   5361 
   5362        const WritingMode wm = item.GetWritingMode();
   5363        LogicalSize availSize = aReflowInput.ComputedSize(wm);
   5364        availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
   5365        ReflowInput childReflowInput(PresContext(), aReflowInput, item.Frame(),
   5366                                     availSize, Nothing(), {}, sizeOverrides,
   5367                                     {ComputeSizeFlag::ShrinkWrap});
   5368        if (item.IsBlockAxisMainAxis() && item.TreatBSizeAsIndefinite()) {
   5369          childReflowInput.mFlags.mTreatBSizeAsIndefinite = true;
   5370        }
   5371 
   5372        SizeItemInCrossAxis(childReflowInput, item);
   5373      }
   5374    }
   5375    // Now that we've finished with this line's items, size the line itself:
   5376    line.ComputeCrossSizeAndBaseline(aAxisTracker);
   5377    sumLineCrossSizes += line.LineCrossSize();
   5378  }
   5379 
   5380  bool isCrossSizeDefinite;
   5381  flr.mContentBoxCrossSize = ComputeCrossSize(
   5382      aReflowInput, aAxisTracker, aTentativeContentBoxCrossSize,
   5383      sumLineCrossSizes, &isCrossSizeDefinite);
   5384 
   5385  // Set up state for cross-axis alignment, at a high level (outside the
   5386  // scope of a particular flex line)
   5387  CrossAxisPositionTracker crossAxisPosnTracker(
   5388      flr.mLines, aReflowInput, flr.mContentBoxCrossSize, isCrossSizeDefinite,
   5389      aAxisTracker, aCrossGapSize);
   5390 
   5391  // Now that we know the cross size of each line (including
   5392  // "align-content:stretch" adjustments, from the CrossAxisPositionTracker
   5393  // constructor), we can create struts for any flex items with
   5394  // "visibility: collapse" (and restart flex layout).
   5395  // Make sure to only do this if we had no struts.
   5396  if (aStruts.IsEmpty() && flr.mHasCollapsedItems &&
   5397      !StyleVisibility()->UseLegacyCollapseBehavior()) {
   5398    BuildStrutInfoFromCollapsedItems(flr.mLines, aStruts);
   5399    if (!aStruts.IsEmpty()) {
   5400      // Restart flex layout, using our struts.
   5401      return flr;
   5402    }
   5403  }
   5404 
   5405  // If the flex container is row-oriented, it should derive its first/last
   5406  // baseline from the WM-relative startmost/endmost FlexLine if any items in
   5407  // the line participate in baseline alignment.
   5408  // https://drafts.csswg.org/css-flexbox-1/#flex-baselines
   5409  //
   5410  // Initialize the relevant variables here so that we can establish baselines
   5411  // while iterating FlexLine later (while crossAxisPosnTracker is conveniently
   5412  // pointing at the cross-start edge of that line, which the line's baseline
   5413  // offset is measured from).
   5414  const FlexLine* lineForFirstBaseline = nullptr;
   5415  const FlexLine* lineForLastBaseline = nullptr;
   5416  if (aAxisTracker.IsRowOriented()) {
   5417    lineForFirstBaseline = &StartmostLine(flr.mLines, aAxisTracker);
   5418    lineForLastBaseline = &EndmostLine(flr.mLines, aAxisTracker);
   5419  } else {
   5420    // For column-oriented flex container, use sentinel value to prompt us to
   5421    // get baselines from the startmost/endmost items.
   5422    flr.mAscent = nscoord_MIN;
   5423    flr.mAscentForLast = nscoord_MIN;
   5424  }
   5425 
   5426  const auto justifyContent =
   5427      aReflowInput.mFrame->IsLegacyWebkitBox()
   5428          ? ConvertLegacyStyleToJustifyContent(StyleXUL())
   5429          : aReflowInput.mStylePosition->mJustifyContent;
   5430 
   5431  lineIndex = 0;
   5432  for (FlexLine& line : flr.mLines) {
   5433    // Main-Axis Alignment - Flexbox spec section 9.5
   5434    // https://drafts.csswg.org/css-flexbox-1/#main-alignment
   5435    // ==============================================
   5436    line.PositionItemsInMainAxis(justifyContent, flr.mContentBoxMainSize,
   5437                                 aAxisTracker);
   5438 
   5439    // See if we need to extract some computed info for this line.
   5440    if (MOZ_UNLIKELY(aContainerInfo)) {
   5441      ComputedFlexLineInfo& lineInfo = aContainerInfo->mLines[lineIndex];
   5442      lineInfo.mCrossStart = crossAxisPosnTracker.Position();
   5443    }
   5444 
   5445    // Cross-Axis Alignment - Flexbox spec section 9.6
   5446    // https://drafts.csswg.org/css-flexbox-1/#cross-alignment
   5447    // ===============================================
   5448    line.PositionItemsInCrossAxis(crossAxisPosnTracker.Position(),
   5449                                  aAxisTracker);
   5450 
   5451    // Flex Container Baselines - Flexbox spec section 8.5
   5452    // https://drafts.csswg.org/css-flexbox-1/#flex-baselines
   5453    auto ComputeAscentFromLine = [&](const FlexLine& aLine,
   5454                                     BaselineSharingGroup aBaselineGroup) {
   5455      MOZ_ASSERT(aAxisTracker.IsRowOriented(),
   5456                 "This makes sense only if we are row-oriented!");
   5457 
   5458      // baselineOffsetInLine is a distance from the line's cross-start edge.
   5459      const nscoord baselineOffsetInLine =
   5460          aLine.ExtractBaselineOffset(aBaselineGroup);
   5461 
   5462      if (baselineOffsetInLine == nscoord_MIN) {
   5463        // No "first baseline"-aligned or "last baseline"-aligned items in
   5464        // aLine. Return a sentinel value to prompt us to get baseline from the
   5465        // startmost or endmost FlexItem after we've reflowed it.
   5466        return nscoord_MIN;
   5467      }
   5468 
   5469      // This "ascent" variable is a distance from the flex container's
   5470      // content-box block-start edge.
   5471      const nscoord ascent = aAxisTracker.LogicalAscentFromFlexRelativeAscent(
   5472          crossAxisPosnTracker.Position() + baselineOffsetInLine,
   5473          flr.mContentBoxCrossSize);
   5474 
   5475      // Convert "ascent" variable to a distance from border-box start or end
   5476      // edge, per documentation for FlexLayoutResult ascent members.
   5477      const auto wm = aAxisTracker.GetWritingMode();
   5478      if (aBaselineGroup == BaselineSharingGroup::First) {
   5479        return ascent +
   5480               aReflowInput.ComputedLogicalBorderPadding(wm).BStart(wm);
   5481      }
   5482      return flr.mContentBoxCrossSize - ascent +
   5483             aReflowInput.ComputedLogicalBorderPadding(wm).BEnd(wm);
   5484    };
   5485 
   5486    if (lineForFirstBaseline && lineForFirstBaseline == &line) {
   5487      flr.mAscent = ComputeAscentFromLine(line, BaselineSharingGroup::First);
   5488    }
   5489    if (lineForLastBaseline && lineForLastBaseline == &line) {
   5490      flr.mAscentForLast =
   5491          ComputeAscentFromLine(line, BaselineSharingGroup::Last);
   5492    }
   5493 
   5494    crossAxisPosnTracker.TraverseLine(line);
   5495    crossAxisPosnTracker.TraversePackingSpace();
   5496 
   5497    if (&line != &flr.mLines.LastElement()) {
   5498      crossAxisPosnTracker.TraverseGap();
   5499    }
   5500    ++lineIndex;
   5501  }
   5502 
   5503  return flr;
   5504 }
   5505 
   5506 // This data structure is used in fragmentation, storing the block coordinate
   5507 // metrics when reflowing 1) the BStart-most line in each fragment of a
   5508 // row-oriented flex container or, 2) the BStart-most item in each fragment of a
   5509 // single-line column-oriented flex container.
   5510 //
   5511 // When we lay out a row-oriented flex container fragment, its first line might
   5512 // contain one or more monolithic items that were pushed from the previous
   5513 // fragment specifically to avoid having those monolithic items overlap the
   5514 // page/column break. The situation is similar for single-row column-oriented
   5515 // flex container fragments, but a bit simpler; only their first item might have
   5516 // been pushed to avoid overlapping a page/column break.
   5517 //
   5518 // We'll have to place any such pushed items at the block-start edge of the
   5519 // current fragment's content-box, which is as close as we can get them to their
   5520 // theoretical/unfragmented position (without slicing them); but it does
   5521 // represent a shift away from their theoretical/unfragmented position (which
   5522 // was somewhere in the previous fragment).
   5523 //
   5524 // When that happens, we need to record the maximum such shift that we had to
   5525 // perform so that we can apply the same block-endwards shift to "downstream"
   5526 // items (items towards the block-end edge) that we could otherwise collide
   5527 // with. We also potentially apply the same shift when computing the block-end
   5528 // edge of this flex container fragment's content-box so that we don't
   5529 // inadvertently shift the last item (or line-of-items) to overlap the flex
   5530 // container's border, or content beyond the flex container.
   5531 //
   5532 // We use this structure to keep track of several metrics, in service of this
   5533 // goal. This structure is also necessary to adjust PerFragmentFlexData at the
   5534 // end of ReflowChildren().
   5535 //
   5536 // Note: "First" in the struct name means "BStart-most", not the order in the
   5537 // flex line array or flex item array.
   5538 struct FirstLineOrFirstItemBAxisMetrics final {
   5539  // This value stores the block-end edge shift for 1) the BStart-most line in
   5540  // the current fragment of a row-oriented flex container, or 2) the
   5541  // BStart-most item in the current fragment of a single-line column-oriented
   5542  // flex container. This number is non-negative.
   5543  //
   5544  // This value may become positive when any item is a first-in-flow and also
   5545  // satisfies either the above condition 1) or 2), since that's a hint that it
   5546  // could be monolithic or have a monolithic first descendant, and therefore an
   5547  // item that might incur a page/column-break-dodging position-shift that this
   5548  // variable needs to track.
   5549  //
   5550  // This value also stores the fragmentation-imposed growth in the block-size
   5551  // of a) the BStart-most line in the current fragment of a row-oriented flex
   5552  // container, or b) the BStart-most item in the current fragment of a
   5553  // single-line column-oriented flex container. This number is non-negative.
   5554  nscoord mBEndEdgeShift = 0;
   5555 
   5556  // The first and second value in the pair store the max block-end edges for
   5557  // items before and after applying the per-item position-shift in the block
   5558  // axis. We only record the block-end edges for items with first-in-flow
   5559  // frames placed in the current flex container fragment. This is used only by
   5560  // row-oriented flex containers.
   5561  Maybe<std::pair<nscoord, nscoord>> mMaxBEndEdge;
   5562 };
   5563 
   5564 std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren(
   5565    const ReflowInput& aReflowInput, const nsSize& aContainerSize,
   5566    const LogicalSize& aAvailableSizeForItems,
   5567    const LogicalMargin& aBorderPadding, const FlexboxAxisTracker& aAxisTracker,
   5568    FlexLayoutResult& aFlr, PerFragmentFlexData& aFragmentData) {
   5569  if (HidesContentForLayout()) {
   5570    return {0, nsReflowStatus()};
   5571  }
   5572 
   5573  // Before giving each child a final reflow, calculate the origin of the
   5574  // flex container's content box (with respect to its border-box), so that
   5575  // we can compute our flex item's final positions.
   5576  WritingMode flexWM = aReflowInput.GetWritingMode();
   5577  const LogicalPoint containerContentBoxOrigin =
   5578      aBorderPadding.StartOffset(flexWM);
   5579 
   5580  // The block-end of children is relative to the flex container's border-box.
   5581  nscoord maxBlockEndEdgeOfChildren = containerContentBoxOrigin.B(flexWM);
   5582 
   5583  FirstLineOrFirstItemBAxisMetrics bAxisMetrics;
   5584  FrameHashtable pushedItems;
   5585  FrameHashtable incompleteItems;
   5586  FrameHashtable overflowIncompleteItems;
   5587 
   5588  const bool isSingleLine =
   5589      IsSingleLine(aReflowInput.mFrame, aReflowInput.mStylePosition);
   5590  const FlexLine& startmostLine = StartmostLine(aFlr.mLines, aAxisTracker);
   5591  const FlexLine& endmostLine = EndmostLine(aFlr.mLines, aAxisTracker);
   5592  const FlexItem* startmostItem =
   5593      startmostLine.IsEmpty() ? nullptr
   5594                              : &startmostLine.StartmostItem(aAxisTracker);
   5595  const FlexItem* endmostItem =
   5596      endmostLine.IsEmpty() ? nullptr : &endmostLine.EndmostItem(aAxisTracker);
   5597 
   5598  bool endmostItemOrLineHasBreakAfter = false;
   5599  // If true, push all remaining flex items to the container's next-in-flow.
   5600  bool shouldPushRemainingItems = false;
   5601 
   5602  // FINAL REFLOW: Give each child frame another chance to reflow.
   5603  const size_t numLines = aFlr.mLines.Length();
   5604  for (size_t lineIdx = 0; lineIdx < numLines; ++lineIdx) {
   5605    // Iterate flex lines from the startmost to endmost (relative to flex
   5606    // container's writing-mode).
   5607    const auto& line =
   5608        aFlr.mLines[aAxisTracker.IsCrossAxisReversed() ? numLines - lineIdx - 1
   5609                                                       : lineIdx];
   5610    MOZ_ASSERT(lineIdx != 0 || &line == &startmostLine,
   5611               "Logic for finding startmost line should be consistent!");
   5612 
   5613    // These two variables can be set when we are a row-oriented flex container
   5614    // during fragmentation.
   5615    bool lineHasBreakBefore = false;
   5616    bool lineHasBreakAfter = false;
   5617 
   5618    const size_t numItems = line.Items().Length();
   5619    for (size_t itemIdx = 0; itemIdx < numItems; ++itemIdx) {
   5620      // Iterate flex items from the startmost to endmost (relative to flex
   5621      // container's writing-mode).
   5622      const FlexItem& item = line.Items()[aAxisTracker.IsMainAxisReversed()
   5623                                              ? numItems - itemIdx - 1
   5624                                              : itemIdx];
   5625      MOZ_ASSERT(lineIdx != 0 || itemIdx != 0 || &item == startmostItem,
   5626                 "Logic for finding startmost item should be consistent!");
   5627 
   5628      LogicalPoint framePos = aAxisTracker.LogicalPointFromFlexRelativePoint(
   5629          item.MainPosition(), item.CrossPosition(), aFlr.mContentBoxMainSize,
   5630          aFlr.mContentBoxCrossSize);
   5631      // This variable records the item's block-end edge before we give it a
   5632      // per-item-position-shift, if the item is a first-in-flow in the
   5633      // startmost line of a row-oriented flex container fragment. It is used to
   5634      // determine the block-end edge shift for the startmost line at the end of
   5635      // the outer loop.
   5636      Maybe<nscoord> frameBPosBeforePerItemShift;
   5637 
   5638      if (item.Frame()->GetPrevInFlow()) {
   5639        // The item is a continuation. Lay it out at the beginning of the
   5640        // available space.
   5641        framePos.B(flexWM) = 0;
   5642      } else if (GetPrevInFlow()) {
   5643        // The item we're placing is not a continuation; though we're placing it
   5644        // into a flex container fragment which *is* a continuation. To compute
   5645        // the item's correct position in this fragment, we adjust the item's
   5646        // theoretical/unfragmented block-direction position by subtracting the
   5647        // cumulative content-box block-size for all the previous fragments and
   5648        // adding the cumulative block-end edge shift.
   5649        //
   5650        // Note that the item's position in this fragment has not been finalized
   5651        // yet. At this point, we've adjusted the item's
   5652        // theoretical/unfragmented position to be relative to the block-end
   5653        // edge of the previous container fragment's content-box. Later, we'll
   5654        // compute per-item position-shift to finalize its position.
   5655        framePos.B(flexWM) -= aFragmentData.mCumulativeContentBoxBSize;
   5656        framePos.B(flexWM) += aFragmentData.mCumulativeBEndEdgeShift;
   5657 
   5658        // This helper gets the per-item position-shift in the block-axis.
   5659        auto GetPerItemPositionShiftToBEnd = [&]() {
   5660          if (framePos.B(flexWM) >= 0) {
   5661            // The item final position might be in current flex container
   5662            // fragment or in any of the later fragments. No adjustment needed.
   5663            return 0;
   5664          }
   5665 
   5666          // The item's block position is negative, but we want to place it at
   5667          // the content-box block-start edge of this container fragment. To
   5668          // achieve this, return a negated (positive) value to make the final
   5669          // block position zero.
   5670          //
   5671          // This scenario occurs when fragmenting a row-oriented flex container
   5672          // where this item is pushed to this container fragment.
   5673          return -framePos.B(flexWM);
   5674        };
   5675 
   5676        if (aAxisTracker.IsRowOriented()) {
   5677          if (&line == &startmostLine) {
   5678            frameBPosBeforePerItemShift.emplace(framePos.B(flexWM));
   5679            framePos.B(flexWM) += GetPerItemPositionShiftToBEnd();
   5680          } else {
   5681            // We've computed two things for the startmost line during the outer
   5682            // loop's first iteration: 1) how far the block-end edge had to
   5683            // shift and 2) how large the block-size needed to grow. Here, we
   5684            // just shift all items in the rest of the lines the same amount.
   5685            framePos.B(flexWM) += bAxisMetrics.mBEndEdgeShift;
   5686          }
   5687        } else {
   5688          MOZ_ASSERT(aAxisTracker.IsColumnOriented());
   5689          if (isSingleLine) {
   5690            if (&item == startmostItem) {
   5691              bAxisMetrics.mBEndEdgeShift = GetPerItemPositionShiftToBEnd();
   5692            }
   5693            framePos.B(flexWM) += bAxisMetrics.mBEndEdgeShift;
   5694          } else {
   5695            // Bug 1806717: We need a more sophisticated solution for multi-line
   5696            // column-oriented flex container when each line has a different
   5697            // position-shift value. For now, we don't shift them.
   5698          }
   5699        }
   5700      }
   5701 
   5702      // Adjust available block-size for the item. (We compute it here because
   5703      // framePos is still relative to the container's content-box.)
   5704      //
   5705      // Note: The available block-size can become negative if item's
   5706      // block-direction position is below available space's block-end.
   5707      const nscoord availableBSizeForItem =
   5708          aAvailableSizeForItems.BSize(flexWM) == NS_UNCONSTRAINEDSIZE
   5709              ? NS_UNCONSTRAINEDSIZE
   5710              : aAvailableSizeForItems.BSize(flexWM) - framePos.B(flexWM);
   5711 
   5712      // Adjust framePos to be relative to the container's border-box
   5713      // (i.e. its frame rect), instead of the container's content-box:
   5714      framePos += containerContentBoxOrigin;
   5715 
   5716      // Check if we can skip reflowing the item because it will be pushed to
   5717      // our next-in-flow -- i.e. if there was a forced break before it, or its
   5718      // position is beyond the available space's block-end.
   5719      bool itemInPushedItems = false;
   5720      if (shouldPushRemainingItems) {
   5721        FLEX_ITEM_LOG(
   5722            item.Frame(),
   5723            "[frag] Item needed to be pushed to container's next-in-flow due "
   5724            "to a forced break before it");
   5725        pushedItems.Insert(item.Frame());
   5726        itemInPushedItems = true;
   5727      } else if (availableBSizeForItem != NS_UNCONSTRAINEDSIZE &&
   5728                 availableBSizeForItem <= 0) {
   5729        // The item's position is beyond the available space, so we have to push
   5730        // it.
   5731        //
   5732        // Note: Even if all of our items are beyond the available space & get
   5733        // pushed here, we'll be guaranteed to place at least one of them (and
   5734        // make progress) in one of the flex container's *next* fragment. It's
   5735        // because ComputeAvailableSizeForItems() always reserves at least 1px
   5736        // available block-size for its children, and we consume all available
   5737        // block-size and add it to
   5738        // PerFragmentFlexData::mCumulativeContentBoxBSize even if we are not
   5739        // laying out any child.
   5740        FLEX_ITEM_LOG(
   5741            item.Frame(),
   5742            "[frag] Item needed to be pushed to container's next-in-flow due "
   5743            "to being positioned beyond block-end edge of available space");
   5744        pushedItems.Insert(item.Frame());
   5745        itemInPushedItems = true;
   5746      } else if (item.NeedsFinalReflow(aReflowInput)) {
   5747        // The available size must be in item's writing-mode.
   5748        const WritingMode itemWM = item.GetWritingMode();
   5749        const auto availableSize =
   5750            LogicalSize(flexWM, aAvailableSizeForItems.ISize(flexWM),
   5751                        availableBSizeForItem)
   5752                .ConvertTo(itemWM, flexWM);
   5753 
   5754        const bool isAdjacentWithBStart =
   5755            framePos.B(flexWM) == containerContentBoxOrigin.B(flexWM);
   5756        const nsReflowStatus childStatus =
   5757            ReflowFlexItem(aAxisTracker, aReflowInput, item, framePos,
   5758                           isAdjacentWithBStart, availableSize, aContainerSize);
   5759 
   5760        if (aReflowInput.IsInFragmentedContext()) {
   5761          const bool itemHasBreakBefore =
   5762              item.Frame()->ShouldBreakBefore(aReflowInput.mBreakType) ||
   5763              childStatus.IsInlineBreakBefore();
   5764          if (itemHasBreakBefore) {
   5765            if (aAxisTracker.IsRowOriented()) {
   5766              lineHasBreakBefore = true;
   5767            } else if (isSingleLine) {
   5768              if (&item == startmostItem) {
   5769                if (!GetPrevInFlow() && !aReflowInput.mFlags.mIsTopOfPage) {
   5770                  // If we are first-in-flow and not at top-of-page, early
   5771                  // return here to propagate forced break-before from the
   5772                  // startmost item to the flex container.
   5773                  nsReflowStatus childrenStatus;
   5774                  childrenStatus.SetInlineLineBreakBeforeAndReset();
   5775                  return {0, childrenStatus};
   5776                }
   5777              } else {
   5778                shouldPushRemainingItems = true;
   5779              }
   5780            } else {
   5781              // Bug 1806717: We haven't implemented fragmentation for
   5782              // multi-line column-oriented flex container, so we just ignore
   5783              // forced breaks for now.
   5784            }
   5785          }
   5786        }
   5787 
   5788        const bool shouldPushItem = [&]() {
   5789          if (shouldPushRemainingItems) {
   5790            return true;
   5791          }
   5792          if (availableBSizeForItem == NS_UNCONSTRAINEDSIZE) {
   5793            // If the available block-size is unconstrained, then we're not
   5794            // fragmenting and we don't want to push the item.
   5795            return false;
   5796          }
   5797          if (isAdjacentWithBStart) {
   5798            // The flex item is adjacent with block-start of the container's
   5799            // content-box. Don't push it, or we'll trap in an infinite loop.
   5800            return false;
   5801          }
   5802          if (item.Frame()->BSize() <= availableBSizeForItem) {
   5803            return false;
   5804          }
   5805          if (aAxisTracker.IsColumnOriented() &&
   5806              item.Frame()->StyleDisplay()->mBreakBefore ==
   5807                  StyleBreakBetween::Avoid) {
   5808            return false;
   5809          }
   5810          return true;
   5811        }();
   5812        if (shouldPushItem) {
   5813          FLEX_ITEM_LOG(
   5814              item.Frame(),
   5815              "[frag] Item needed to be pushed to container's next-in-flow "
   5816              "because it encounters a forced break before it, or its "
   5817              "block-size is larger than the available space");
   5818          pushedItems.Insert(item.Frame());
   5819          itemInPushedItems = true;
   5820        } else if (childStatus.IsIncomplete()) {
   5821          incompleteItems.Insert(item.Frame());
   5822        } else if (childStatus.IsOverflowIncomplete()) {
   5823          overflowIncompleteItems.Insert(item.Frame());
   5824        }
   5825 
   5826        if (aReflowInput.IsInFragmentedContext()) {
   5827          const bool itemHasBreakAfter =
   5828              item.Frame()->ShouldBreakAfter(aReflowInput.mBreakType) ||
   5829              childStatus.IsInlineBreakAfter();
   5830          if (itemHasBreakAfter) {
   5831            if (aAxisTracker.IsRowOriented()) {
   5832              lineHasBreakAfter = true;
   5833            } else if (isSingleLine) {
   5834              shouldPushRemainingItems = true;
   5835              if (&item == endmostItem) {
   5836                endmostItemOrLineHasBreakAfter = true;
   5837              }
   5838            } else {
   5839              // Bug 1806717: We haven't implemented fragmentation for
   5840              // multi-line column-oriented flex container, so we just ignore
   5841              // forced breaks for now.
   5842            }
   5843          }
   5844        }
   5845      } else {
   5846        // We already reflowed the item with the right content-box size, so we
   5847        // can simply move it into place.
   5848        MoveFlexItemToFinalPosition(item, framePos, aContainerSize);
   5849      }
   5850 
   5851      if (!itemInPushedItems) {
   5852        const nscoord borderBoxBSize = item.Frame()->BSize(flexWM);
   5853        const nscoord bEndEdgeAfterPerItemShift =
   5854            framePos.B(flexWM) + borderBoxBSize;
   5855 
   5856        // The item (or a fragment thereof) was placed in this flex container
   5857        // fragment. Update the max block-end edge with the item's block-end
   5858        // edge.
   5859        maxBlockEndEdgeOfChildren =
   5860            std::max(maxBlockEndEdgeOfChildren, bEndEdgeAfterPerItemShift);
   5861 
   5862        if (frameBPosBeforePerItemShift) {
   5863          // Make the block-end edge relative to flex container's border-box
   5864          // because bEndEdgeAfterPerItemShift is relative to the border-box.
   5865          const nscoord bEndEdgeBeforePerItemShift =
   5866              containerContentBoxOrigin.B(flexWM) +
   5867              *frameBPosBeforePerItemShift + borderBoxBSize;
   5868 
   5869          if (bAxisMetrics.mMaxBEndEdge) {
   5870            auto& [before, after] = *bAxisMetrics.mMaxBEndEdge;
   5871            before = std::max(before, bEndEdgeBeforePerItemShift);
   5872            after = std::max(after, bEndEdgeAfterPerItemShift);
   5873          } else {
   5874            bAxisMetrics.mMaxBEndEdge.emplace(bEndEdgeBeforePerItemShift,
   5875                                              bEndEdgeAfterPerItemShift);
   5876          }
   5877        }
   5878 
   5879        if (item.Frame()->GetPrevInFlow()) {
   5880          // Items with a previous-continuation may experience some
   5881          // fragmentation-imposed growth in their block-size; we compute that
   5882          // here.
   5883          const nscoord bSizeOfThisFragment =
   5884              item.Frame()->ContentSize(flexWM).BSize(flexWM);
   5885          const nscoord consumedBSize = FlexItemConsumedBSize(item);
   5886          const nscoord unfragmentedBSize = item.BSize();
   5887          nscoord bSizeGrowthOfThisFragment = 0;
   5888 
   5889          if (consumedBSize >= unfragmentedBSize) {
   5890            // The item's block-size has been grown to exceed the unfragmented
   5891            // block-size in the previous fragments.
   5892            bSizeGrowthOfThisFragment = bSizeOfThisFragment;
   5893          } else if (consumedBSize + bSizeOfThisFragment >= unfragmentedBSize) {
   5894            // The item's block-size just grows in the current fragment to
   5895            // exceed the unfragmented block-size.
   5896            bSizeGrowthOfThisFragment =
   5897                consumedBSize + bSizeOfThisFragment - unfragmentedBSize;
   5898          }
   5899 
   5900          if (aAxisTracker.IsRowOriented()) {
   5901            if (&line == &startmostLine) {
   5902              bAxisMetrics.mBEndEdgeShift = std::max(
   5903                  bAxisMetrics.mBEndEdgeShift, bSizeGrowthOfThisFragment);
   5904            }
   5905          } else {
   5906            MOZ_ASSERT(aAxisTracker.IsColumnOriented());
   5907            if (isSingleLine) {
   5908              if (&item == startmostItem) {
   5909                MOZ_ASSERT(bAxisMetrics.mBEndEdgeShift == 0,
   5910                           "The item's frame is a continuation, so it "
   5911                           "shouldn't shift!");
   5912                bAxisMetrics.mBEndEdgeShift = bSizeGrowthOfThisFragment;
   5913              }
   5914            } else {
   5915              // Bug 1806717: We need a more sophisticated solution for
   5916              // multi-line column-oriented flex container when each line has a
   5917              // different block-size growth value. For now, we don't deal with
   5918              // them.
   5919            }
   5920          }
   5921        }
   5922      }
   5923 
   5924      // If the item has auto margins, and we were tracking the UsedMargin
   5925      // property, set the property to the computed margin values.
   5926      if (item.HasAnyAutoMargin()) {
   5927        nsMargin* propValue =
   5928            item.Frame()->GetProperty(nsIFrame::UsedMarginProperty());
   5929        if (propValue) {
   5930          *propValue = item.PhysicalMargin();
   5931        }
   5932      }
   5933    }
   5934 
   5935    if (aReflowInput.IsInFragmentedContext() && aAxisTracker.IsRowOriented()) {
   5936      // Propagate forced break values from the flex items to its flex line.
   5937      if (lineHasBreakBefore) {
   5938        if (&line == &startmostLine) {
   5939          if (!GetPrevInFlow() && !aReflowInput.mFlags.mIsTopOfPage) {
   5940            // If we are first-in-flow and not at top-of-page, early return here
   5941            // to propagate forced break-before from the startmost line to the
   5942            // flex container.
   5943            nsReflowStatus childrenStatus;
   5944            childrenStatus.SetInlineLineBreakBeforeAndReset();
   5945            return {0, childrenStatus};
   5946          }
   5947        } else {
   5948          // Current non-startmost line has forced break-before, so push all the
   5949          // items in this line.
   5950          for (const FlexItem& item : line.Items()) {
   5951            pushedItems.Insert(item.Frame());
   5952            incompleteItems.Remove(item.Frame());
   5953            overflowIncompleteItems.Remove(item.Frame());
   5954          }
   5955          shouldPushRemainingItems = true;
   5956        }
   5957      }
   5958      if (lineHasBreakAfter) {
   5959        shouldPushRemainingItems = true;
   5960        if (&line == &endmostLine) {
   5961          endmostItemOrLineHasBreakAfter = true;
   5962        }
   5963      }
   5964    }
   5965 
   5966    // Now we've finished processing all the items in the startmost line.
   5967    // Determine the amount by which the startmost line's block-end edge has
   5968    // shifted, so we can apply the same shift for the remaining lines.
   5969    if (GetPrevInFlow() && aAxisTracker.IsRowOriented() &&
   5970        &line == &startmostLine && bAxisMetrics.mMaxBEndEdge) {
   5971      auto& [before, after] = *bAxisMetrics.mMaxBEndEdge;
   5972      bAxisMetrics.mBEndEdgeShift =
   5973          std::max(bAxisMetrics.mBEndEdgeShift, after - before);
   5974    }
   5975  }
   5976 
   5977  if (!aFlr.mPlaceholders.IsEmpty()) {
   5978    ReflowPlaceholders(aReflowInput, aFlr.mPlaceholders,
   5979                       containerContentBoxOrigin, aContainerSize);
   5980  }
   5981 
   5982  nsReflowStatus childrenStatus;
   5983  if (!pushedItems.IsEmpty() || !incompleteItems.IsEmpty()) {
   5984    childrenStatus.SetIncomplete();
   5985  } else if (!overflowIncompleteItems.IsEmpty()) {
   5986    childrenStatus.SetOverflowIncomplete();
   5987  } else if (endmostItemOrLineHasBreakAfter) {
   5988    childrenStatus.SetInlineLineBreakAfter();
   5989  }
   5990  PushIncompleteChildren(pushedItems, incompleteItems, overflowIncompleteItems);
   5991 
   5992  // TODO: Try making this a fatal assertion after we fix bug 1751260.
   5993  NS_ASSERTION(childrenStatus.IsFullyComplete() ||
   5994                   aAvailableSizeForItems.BSize(flexWM) != NS_UNCONSTRAINEDSIZE,
   5995               "We shouldn't have any incomplete children if the available "
   5996               "block-size is unconstrained!");
   5997 
   5998  if (!pushedItems.IsEmpty()) {
   5999    AddStateBits(NS_STATE_FLEX_DID_PUSH_ITEMS);
   6000  }
   6001 
   6002  if (GetPrevInFlow()) {
   6003    aFragmentData.mCumulativeBEndEdgeShift += bAxisMetrics.mBEndEdgeShift;
   6004  }
   6005 
   6006  return {maxBlockEndEdgeOfChildren, childrenStatus};
   6007 }
   6008 
   6009 void nsFlexContainerFrame::PopulateReflowOutput(
   6010    ReflowOutput& aReflowOutput, const ReflowInput& aReflowInput,
   6011    nsReflowStatus& aStatus, const LogicalSize& aContentBoxSize,
   6012    const LogicalMargin& aBorderPadding, const nscoord aConsumedBSize,
   6013    const bool aMayNeedNextInFlow, const nscoord aMaxBlockEndEdgeOfChildren,
   6014    const nsReflowStatus& aChildrenStatus,
   6015    const FlexboxAxisTracker& aAxisTracker, FlexLayoutResult& aFlr) {
   6016  const WritingMode flexWM = aReflowInput.GetWritingMode();
   6017 
   6018  // Compute flex container's desired size (in its own writing-mode).
   6019  LogicalSize desiredSizeInFlexWM(flexWM);
   6020  desiredSizeInFlexWM.ISize(flexWM) =
   6021      aContentBoxSize.ISize(flexWM) + aBorderPadding.IStartEnd(flexWM);
   6022 
   6023  // Unconditionally skip adding block-end border and padding for now. We add it
   6024  // lower down, after we've established baseline and decided whether bottom
   6025  // border-padding fits (if we're fragmented).
   6026  const nscoord effectiveContentBSizeWithBStartBP =
   6027      aContentBoxSize.BSize(flexWM) - aConsumedBSize +
   6028      aBorderPadding.BStart(flexWM);
   6029  nscoord blockEndContainerBP = aBorderPadding.BEnd(flexWM);
   6030 
   6031  if (aMayNeedNextInFlow) {
   6032    // We assume our status should be reported as incomplete because we may need
   6033    // a next-in-flow.
   6034    bool isStatusIncomplete = true;
   6035 
   6036    const nscoord availableBSizeMinusBEndBP =
   6037        aReflowInput.AvailableBSize() - aBorderPadding.BEnd(flexWM);
   6038 
   6039    if (aMaxBlockEndEdgeOfChildren <= availableBSizeMinusBEndBP) {
   6040      // Consume all the available block-size.
   6041      desiredSizeInFlexWM.BSize(flexWM) = availableBSizeMinusBEndBP;
   6042    } else {
   6043      // This case happens if we have some tall unbreakable children exceeding
   6044      // the available block-size.
   6045      desiredSizeInFlexWM.BSize(flexWM) = std::min(
   6046          effectiveContentBSizeWithBStartBP, aMaxBlockEndEdgeOfChildren);
   6047 
   6048      if ((aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE ||
   6049           aChildrenStatus.IsFullyComplete()) &&
   6050          aMaxBlockEndEdgeOfChildren >= effectiveContentBSizeWithBStartBP) {
   6051        // We have some tall unbreakable child that's sticking off the end of
   6052        // our fragment, *and* forcing us to consume all of our remaining
   6053        // content block-size and call ourselves complete.
   6054        //
   6055        // - If we have a definite block-size: we get here if the tall child
   6056        //   makes us reach that block-size.
   6057        // - If we have a content-based block-size: we get here if the tall
   6058        //   child makes us reach the content-based block-size from a
   6059        //   theoretical unfragmented layout, *and* all our children are
   6060        //   complete. (Note that if we have some incomplete child, then we
   6061        //   instead prefer to return an incomplete status, so we can get a
   6062        //   next-in-flow to include that child's requested next-in-flow, in the
   6063        //   spirit of having a block-size that fits the content.)
   6064        //
   6065        // TODO: the auto-height case might need more subtlety; see bug 1828977.
   6066        isStatusIncomplete = false;
   6067 
   6068        // We also potentially need to get the unskipped block-end border and
   6069        // padding (if we assumed it'd be skipped as part of our tentative
   6070        // assumption that we'd be incomplete).
   6071        if (aReflowInput.mStyleBorder->mBoxDecorationBreak ==
   6072            StyleBoxDecorationBreak::Slice) {
   6073          blockEndContainerBP =
   6074              aReflowInput.ComputedLogicalBorderPadding(flexWM).BEnd(flexWM);
   6075        }
   6076      }
   6077    }
   6078 
   6079    if (isStatusIncomplete) {
   6080      aStatus.SetIncomplete();
   6081    }
   6082  } else {
   6083    // Our own effective content-box block-size can fit within the available
   6084    // block-size.
   6085    desiredSizeInFlexWM.BSize(flexWM) = effectiveContentBSizeWithBStartBP;
   6086  }
   6087 
   6088  // Now, we account for how the block-end border and padding (if any) impacts
   6089  // our desired size. If adding it pushes us over the available block-size,
   6090  // then we become incomplete (unless we already weren't asking for any
   6091  // block-size, in which case we stay complete to avoid looping forever).
   6092  //
   6093  // NOTE: If we have auto block-size, we allow our block-end border and padding
   6094  // to push us over the available block-size without requesting a continuation,
   6095  // for consistency with the behavior of "display:block" elements.
   6096  const nscoord effectiveContentBSizeWithBStartEndBP =
   6097      desiredSizeInFlexWM.BSize(flexWM) + blockEndContainerBP;
   6098 
   6099  if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
   6100      effectiveContentBSizeWithBStartEndBP > aReflowInput.AvailableBSize() &&
   6101      desiredSizeInFlexWM.BSize(flexWM) != 0 &&
   6102      aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) {
   6103    // We couldn't fit with the block-end border and padding included, so we'll
   6104    // need a continuation.
   6105    aStatus.SetIncomplete();
   6106 
   6107    if (aReflowInput.mStyleBorder->mBoxDecorationBreak ==
   6108        StyleBoxDecorationBreak::Slice) {
   6109      blockEndContainerBP = 0;
   6110    }
   6111  }
   6112 
   6113  // The variable "blockEndContainerBP" now accurately reflects how much (if
   6114  // any) block-end border and padding we want for this frame, so we can proceed
   6115  // to add it in.
   6116  desiredSizeInFlexWM.BSize(flexWM) += blockEndContainerBP;
   6117 
   6118  if (aStatus.IsComplete() && !aChildrenStatus.IsFullyComplete()) {
   6119    aStatus.SetOverflowIncomplete();
   6120    aStatus.SetNextInFlowNeedsReflow();
   6121  }
   6122 
   6123  // If we are the first-in-flow and not fully complete (either our block-size
   6124  // or any of our flex items cannot fit in the available block-size), and the
   6125  // style requires us to avoid breaking inside, set the status to prompt our
   6126  // parent to push us to the next page/column.
   6127  if (!GetPrevInFlow() && !aStatus.IsFullyComplete() &&
   6128      ShouldAvoidBreakInside(aReflowInput)) {
   6129    aStatus.SetInlineLineBreakBeforeAndReset();
   6130    return;
   6131  }
   6132 
   6133  // Propagate forced break values from flex items or flex lines.
   6134  if (aChildrenStatus.IsInlineBreakBefore()) {
   6135    aStatus.SetInlineLineBreakBeforeAndReset();
   6136  }
   6137  if (aChildrenStatus.IsInlineBreakAfter()) {
   6138    aStatus.SetInlineLineBreakAfter();
   6139  }
   6140 
   6141  // If we haven't established a baseline for the container yet, i.e. if we
   6142  // don't have any flex item in the startmost flex line that participates in
   6143  // baseline alignment, then use the startmost flex item to derive the
   6144  // container's baseline.
   6145  if (const FlexLine& line = StartmostLine(aFlr.mLines, aAxisTracker);
   6146      aFlr.mAscent == nscoord_MIN && !line.IsEmpty()) {
   6147    const FlexItem& item = line.StartmostItem(aAxisTracker);
   6148    aFlr.mAscent = item.Frame()
   6149                       ->GetLogicalPosition(
   6150                           flexWM, desiredSizeInFlexWM.GetPhysicalSize(flexWM))
   6151                       .B(flexWM) +
   6152                   item.ResolvedAscent(true);
   6153  }
   6154 
   6155  // Likewise, if we don't have any flex item in the endmost flex line that
   6156  // participates in last baseline alignment, then use the endmost flex item to
   6157  // derived the container's last baseline.
   6158  if (const FlexLine& line = EndmostLine(aFlr.mLines, aAxisTracker);
   6159      aFlr.mAscentForLast == nscoord_MIN && !line.IsEmpty()) {
   6160    const FlexItem& item = line.EndmostItem(aAxisTracker);
   6161    const nscoord lastAscent =
   6162        item.Frame()
   6163            ->GetLogicalPosition(flexWM,
   6164                                 desiredSizeInFlexWM.GetPhysicalSize(flexWM))
   6165            .B(flexWM) +
   6166        item.ResolvedAscent(false);
   6167 
   6168    aFlr.mAscentForLast = desiredSizeInFlexWM.BSize(flexWM) - lastAscent;
   6169  }
   6170 
   6171  if (aFlr.mAscent == nscoord_MIN) {
   6172    // Still don't have our baseline set -- this happens if we have no
   6173    // children, if our children are huge enough that they have nscoord_MIN
   6174    // as their baseline, or our content is hidden in which case, we'll use the
   6175    // wrong baseline (but no big deal).
   6176    NS_WARNING_ASSERTION(
   6177        HidesContentForLayout() || aFlr.mLines[0].IsEmpty(),
   6178        "Have flex items but didn't get an ascent - that's odd (or there are "
   6179        "just gigantic sizes involved)");
   6180    // Per spec, synthesize baseline from the flex container's content box
   6181    // (i.e. use block-end side of content-box)
   6182    // XXXdholbert This only makes sense if parent's writing mode is
   6183    // horizontal (& even then, really we should be using the BSize in terms
   6184    // of the parent's writing mode, not ours). Clean up in bug 1155322.
   6185    aFlr.mAscent = effectiveContentBSizeWithBStartBP;
   6186  }
   6187 
   6188  if (aFlr.mAscentForLast == nscoord_MIN) {
   6189    // Still don't have our last baseline set -- this happens if we have no
   6190    // children, if our children are huge enough that they have nscoord_MIN
   6191    // as their baseline, or our content is hidden in which case, we'll use the
   6192    // wrong baseline (but no big deal).
   6193    NS_WARNING_ASSERTION(
   6194        HidesContentForLayout() || aFlr.mLines[0].IsEmpty(),
   6195        "Have flex items but didn't get an ascent - that's odd (or there are "
   6196        "just gigantic sizes involved)");
   6197    // Per spec, synthesize baseline from the flex container's content box
   6198    // (i.e. use block-end side of content-box)
   6199    // XXXdholbert This only makes sense if parent's writing mode is
   6200    // horizontal (& even then, really we should be using the BSize in terms
   6201    // of the parent's writing mode, not ours). Clean up in bug 1155322.
   6202    aFlr.mAscentForLast = blockEndContainerBP;
   6203  }
   6204 
   6205  if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
   6206    // This will force our parent to call GetLogicalBaseline, which will
   6207    // synthesize a margin-box baseline.
   6208    aReflowOutput.SetBlockStartAscent(ReflowOutput::ASK_FOR_BASELINE);
   6209  } else {
   6210    // XXXdholbert aFlr.mAscent needs to be in terms of our parent's
   6211    // writing-mode here. See bug 1155322.
   6212    aReflowOutput.SetBlockStartAscent(aFlr.mAscent);
   6213  }
   6214 
   6215  // Cache the container baselines so that our parent can baseline-align us.
   6216  mFirstBaseline = aFlr.mAscent;
   6217  mLastBaseline = aFlr.mAscentForLast;
   6218 
   6219  // Convert flex container's final desired size to parent's WM, for outparam.
   6220  aReflowOutput.SetSize(flexWM, desiredSizeInFlexWM);
   6221 }
   6222 
   6223 void nsFlexContainerFrame::MoveFlexItemToFinalPosition(
   6224    const FlexItem& aItem, const LogicalPoint& aFramePos,
   6225    const nsSize& aContainerSize) {
   6226  const WritingMode outerWM = aItem.ContainingBlockWM();
   6227  const nsStyleDisplay* display = aItem.Frame()->StyleDisplay();
   6228  LogicalPoint pos(aFramePos);
   6229  if (display->IsRelativelyOrStickyPositionedStyle()) {
   6230    // If the item is relatively positioned, look up its offsets (cached from
   6231    // previous reflow). A sticky positioned item can pass a dummy
   6232    // logicalOffsets into ApplyRelativePositioning().
   6233    LogicalMargin logicalOffsets(outerWM);
   6234    if (display->IsRelativelyPositionedStyle()) {
   6235      nsMargin* cachedOffsets =
   6236          aItem.Frame()->GetProperty(nsIFrame::ComputedOffsetProperty());
   6237      MOZ_ASSERT(
   6238          cachedOffsets,
   6239          "relpos previously-reflowed frame should've cached its offsets");
   6240      logicalOffsets = LogicalMargin(outerWM, *cachedOffsets);
   6241    }
   6242    ReflowInput::ApplyRelativePositioning(aItem.Frame(), outerWM,
   6243                                          logicalOffsets, &pos, aContainerSize);
   6244  }
   6245 
   6246  FLEX_ITEM_LOG(aItem.Frame(), "Moving item to its desired position %s",
   6247                ToString(pos).c_str());
   6248  aItem.Frame()->SetPosition(outerWM, pos, aContainerSize);
   6249 }
   6250 
   6251 nsReflowStatus nsFlexContainerFrame::ReflowFlexItem(
   6252    const FlexboxAxisTracker& aAxisTracker, const ReflowInput& aReflowInput,
   6253    const FlexItem& aItem, const LogicalPoint& aFramePos,
   6254    const bool aIsAdjacentWithBStart, const LogicalSize& aAvailableSize,
   6255    const nsSize& aContainerSize) {
   6256  FLEX_ITEM_LOG(aItem.Frame(), "Doing final reflow");
   6257 
   6258  // Returns true if we should use 'auto' in block axis's StyleSizeOverrides to
   6259  // allow fragmentation-imposed block-size growth.
   6260  auto ComputeBSizeOverrideWithAuto = [&]() {
   6261    if (!aReflowInput.IsInFragmentedContext()) {
   6262      return false;
   6263    }
   6264    if (aItem.Frame()->IsReplaced()) {
   6265      // Disallow fragmentation-imposed block-size growth for replaced elements
   6266      // since they are monolithic, and cannot be fragmented.
   6267      return false;
   6268    }
   6269    if (aItem.HasAspectRatio()) {
   6270      // Aspect-ratio's automatic content-based minimum size doesn't work
   6271      // properly in a fragmented context (Bug 1868284) when we use 'auto'
   6272      // block-size to apply the fragmentation-imposed block-size growth.
   6273      // Disable it for now so that items with aspect-ratios can still use their
   6274      // known block-sizes (from flex layout algorithm) in final reflow.
   6275      return false;
   6276    }
   6277    if (aItem.IsBlockAxisMainAxis()) {
   6278      if (aItem.IsFlexBaseSizeContentBSize()) {
   6279        // The flex item resolved its indefinite flex-basis to the content
   6280        // block-size.
   6281        if (aItem.IsMainMinSizeContentBSize()) {
   6282          // The item's flex base size and main min-size are both content
   6283          // block-size. We interpret this content-based block-size as
   6284          // permission to apply fragmentation-imposed block-size growth.
   6285          return true;
   6286        }
   6287        if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE) {
   6288          // The flex container has an indefinite block-size. We allow the
   6289          // item's to apply fragmentation-imposed block-size growth.
   6290          return true;
   6291        }
   6292      }
   6293      return false;
   6294    }
   6295 
   6296    MOZ_ASSERT(aItem.IsBlockAxisCrossAxis());
   6297    MOZ_ASSERT(aItem.IsStretched(),
   6298               "No need to override block-size with 'auto' if the item is not "
   6299               "stretched in the cross axis!");
   6300 
   6301    Maybe<nscoord> measuredBSize = aItem.MeasuredBSize();
   6302    if (measuredBSize && aItem.CrossSize() == *measuredBSize) {
   6303      // The item has a measured content-based block-size due to having an
   6304      // indefinite cross-size. If its cross-size is equal to the content-based
   6305      // block-size, then it is the tallest item that established the cross-size
   6306      // of the flex line. We allow it apply fragmentation-imposed block-size
   6307      // growth.
   6308      //
   6309      // Note: We only allow the tallest item to grow because it is likely to
   6310      // have the most impact on the overall flex container block-size growth.
   6311      // This is not a perfect solution since other shorter items in the same
   6312      // line might also have fragmentation-imposed block-size growth, but
   6313      // currently there is no reliable way to detect whether they will outgrow
   6314      // the tallest item.
   6315      return true;
   6316    }
   6317    return false;
   6318  };
   6319 
   6320  StyleSizeOverrides sizeOverrides;
   6321  bool overrideBSizeWithAuto = false;
   6322 
   6323  // Override flex item's main size.
   6324  if (aItem.IsInlineAxisMainAxis()) {
   6325    sizeOverrides.mStyleISize.emplace(aItem.StyleMainSize());
   6326    FLEX_LOGV("Main size (inline-size) override: %d", aItem.MainSize());
   6327  } else {
   6328    overrideBSizeWithAuto = ComputeBSizeOverrideWithAuto();
   6329    if (overrideBSizeWithAuto) {
   6330      sizeOverrides.mStyleBSize.emplace(StyleSize::Auto());
   6331      FLEX_LOGV("Main size (block-size) override: Auto");
   6332    } else {
   6333      sizeOverrides.mStyleBSize.emplace(aItem.StyleMainSize());
   6334      FLEX_LOGV("Main size (block-size) override: %d", aItem.MainSize());
   6335    }
   6336  }
   6337 
   6338  // Override flex item's cross size if it was stretched in the cross axis (in
   6339  // which case we're imposing a cross size).
   6340  if (aItem.IsStretched()) {
   6341    if (aItem.IsInlineAxisCrossAxis()) {
   6342      sizeOverrides.mStyleISize.emplace(aItem.StyleCrossSize());
   6343      FLEX_LOGV("Cross size (inline-size) override: %d", aItem.CrossSize());
   6344    } else {
   6345      overrideBSizeWithAuto = ComputeBSizeOverrideWithAuto();
   6346      if (overrideBSizeWithAuto) {
   6347        sizeOverrides.mStyleBSize.emplace(StyleSize::Auto());
   6348        FLEX_LOGV("Cross size (block-size) override: Auto");
   6349      } else {
   6350        sizeOverrides.mStyleBSize.emplace(aItem.StyleCrossSize());
   6351        FLEX_LOGV("Cross size (block-size) override: %d", aItem.CrossSize());
   6352      }
   6353    }
   6354  }
   6355  if (sizeOverrides.mStyleBSize) {
   6356    // We are overriding the block-size. For robustness, we always assume that
   6357    // this represents a block-axis resize for the frame. This may be
   6358    // conservative, but we do capture all the conditions in the block-axis
   6359    // (checked in NeedsFinalReflow()) that make this item require a final
   6360    // reflow. This sets relevant flags in ReflowInput::InitResizeFlags().
   6361    aItem.Frame()->SetHasBSizeChange(true);
   6362  }
   6363 
   6364  ReflowInput childReflowInput(PresContext(), aReflowInput, aItem.Frame(),
   6365                               aAvailableSize, Nothing(), {}, sizeOverrides,
   6366                               {ComputeSizeFlag::ShrinkWrap});
   6367  if (overrideBSizeWithAuto) {
   6368    // If we use 'auto' to override the item's block-size, set the item's
   6369    // original block-size to min-size as a lower bound.
   6370    childReflowInput.SetComputedMinBSize(aItem.BSize());
   6371 
   6372    // Set the item's block-size as the percentage basis so that its children
   6373    // can resolve percentage sizes correctly.
   6374    childReflowInput.SetPercentageBasisInBlockAxis(aItem.BSize());
   6375  }
   6376 
   6377  if (aItem.TreatBSizeAsIndefinite() && aItem.IsBlockAxisMainAxis()) {
   6378    childReflowInput.mFlags.mTreatBSizeAsIndefinite = true;
   6379  }
   6380 
   6381  if (aItem.IsStretched() && aItem.IsBlockAxisCrossAxis()) {
   6382    // This item is stretched (in the cross axis), and that axis is its block
   6383    // axis.  That stretching effectively gives it a relative BSize.
   6384    // XXXdholbert This flag only makes a difference if we use the flex items'
   6385    // frame-state when deciding whether to reflow them -- and we don't, as of
   6386    // the changes in bug 851607. So this has no effect right now, but it might
   6387    // make a difference if we optimize to use dirty bits in the
   6388    // future. (Reftests flexbox-resizeviewport-1.xhtml and -2.xhtml are
   6389    // intended to catch any regressions here, if we end up relying on this bit
   6390    // & neglecting to set it.)
   6391    aItem.Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
   6392  }
   6393 
   6394  if (!aIsAdjacentWithBStart) {
   6395    // mIsTopOfPage bit in childReflowInput is carried over from aReflowInput.
   6396    // However, if this item's position is not adjacent with the flex
   6397    // container's content-box block-start edge, we should clear it.
   6398    childReflowInput.mFlags.mIsTopOfPage = false;
   6399  }
   6400 
   6401  // NOTE: Be very careful about doing anything else with childReflowInput
   6402  // after this point, because some of its methods (e.g. SetComputedWidth)
   6403  // internally call InitResizeFlags and stomp on mVResize & mHResize.
   6404 
   6405  FLEX_ITEM_LOG(aItem.Frame(), "Reflowing item at its desired position %s",
   6406                ToString(aFramePos).c_str());
   6407 
   6408  // CachedFlexItemData is stored in item's writing mode, so we pass
   6409  // aChildReflowInput into ReflowOutput's constructor.
   6410  ReflowOutput childReflowOutput(childReflowInput);
   6411  nsReflowStatus childStatus;
   6412  WritingMode outerWM = aReflowInput.GetWritingMode();
   6413  ReflowChild(aItem.Frame(), PresContext(), childReflowOutput, childReflowInput,
   6414              outerWM, aFramePos, aContainerSize, ReflowChildFlags::Default,
   6415              childStatus);
   6416 
   6417  // XXXdholbert Perhaps we should call CheckForInterrupt here; see bug 1495532.
   6418 
   6419  FinishReflowChild(aItem.Frame(), PresContext(), childReflowOutput,
   6420                    &childReflowInput, outerWM, aFramePos, aContainerSize,
   6421                    ReflowChildFlags::ApplyRelativePositioning);
   6422 
   6423  aItem.SetAscent(childReflowOutput.BlockStartAscent());
   6424 
   6425  // Update our cached flex item info:
   6426  if (auto* cached = aItem.Frame()->GetProperty(CachedFlexItemData::Prop())) {
   6427    cached->Update(childReflowInput, childReflowOutput,
   6428                   FlexItemReflowType::Final);
   6429  } else {
   6430    cached = new CachedFlexItemData(childReflowInput, childReflowOutput,
   6431                                    FlexItemReflowType::Final);
   6432    aItem.Frame()->SetProperty(CachedFlexItemData::Prop(), cached);
   6433  }
   6434 
   6435  return childStatus;
   6436 }
   6437 
   6438 void nsFlexContainerFrame::ReflowPlaceholders(
   6439    const ReflowInput& aReflowInput, nsTArray<nsIFrame*>& aPlaceholders,
   6440    const LogicalPoint& aContentBoxOrigin, const nsSize& aContainerSize) {
   6441  WritingMode outerWM = aReflowInput.GetWritingMode();
   6442 
   6443  // As noted in this method's documentation, we'll reflow every entry in
   6444  // |aPlaceholders| at the container's content-box origin.
   6445  for (nsIFrame* placeholder : aPlaceholders) {
   6446    MOZ_ASSERT(placeholder->IsPlaceholderFrame(),
   6447               "placeholders array should only contain placeholder frames");
   6448    WritingMode wm = placeholder->GetWritingMode();
   6449    LogicalSize availSize = aReflowInput.ComputedSize(wm);
   6450    ReflowInput childReflowInput(PresContext(), aReflowInput, placeholder,
   6451                                 availSize);
   6452    // No need to set the -webkit-line-clamp related flags when reflowing
   6453    // a placeholder.
   6454    ReflowOutput childReflowOutput(outerWM);
   6455    nsReflowStatus childStatus;
   6456    ReflowChild(placeholder, PresContext(), childReflowOutput, childReflowInput,
   6457                outerWM, aContentBoxOrigin, aContainerSize,
   6458                ReflowChildFlags::Default, childStatus);
   6459 
   6460    FinishReflowChild(placeholder, PresContext(), childReflowOutput,
   6461                      &childReflowInput, outerWM, aContentBoxOrigin,
   6462                      aContainerSize, ReflowChildFlags::Default);
   6463 
   6464    // Mark the placeholder frame to indicate that it's not actually at the
   6465    // element's static position, because we need to apply CSS Alignment after
   6466    // we determine the OOF's size:
   6467    placeholder->AddStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN);
   6468  }
   6469 }
   6470 
   6471 nscoord nsFlexContainerFrame::ComputeIntrinsicISize(
   6472    const IntrinsicSizeInput& aInput, IntrinsicISizeType aType) {
   6473  FLEX_LOG("Compute %s isize for flex container frame %p",
   6474           aType == IntrinsicISizeType::MinISize ? "min" : "pref", this);
   6475 
   6476  if (Maybe<nscoord> containISize = ContainIntrinsicISize()) {
   6477    return *containISize;
   6478  }
   6479 
   6480  nscoord containerISize = 0;
   6481  const nsStylePosition* stylePos = StylePosition();
   6482  const FlexboxAxisTracker axisTracker(this);
   6483 
   6484  nscoord mainGapSize;
   6485  if (axisTracker.IsRowOriented()) {
   6486    mainGapSize = nsLayoutUtils::ResolveGapToLength(stylePos->mColumnGap,
   6487                                                    NS_UNCONSTRAINEDSIZE);
   6488  } else {
   6489    mainGapSize = nsLayoutUtils::ResolveGapToLength(stylePos->mRowGap,
   6490                                                    NS_UNCONSTRAINEDSIZE);
   6491  }
   6492 
   6493  const bool useMozBoxCollapseBehavior =
   6494      StyleVisibility()->UseLegacyCollapseBehavior();
   6495  const bool isSingleLine = IsSingleLine(this, stylePos);
   6496  const auto flexWM = GetWritingMode();
   6497 
   6498  // The loop below sets aside space for a gap before each item besides the
   6499  // first. This bool helps us handle that special-case.
   6500  bool onFirstChild = true;
   6501 
   6502  for (nsIFrame* childFrame : mFrames) {
   6503    // Skip out-of-flow children because they don't participate in flex layout.
   6504    if (childFrame->IsPlaceholderFrame()) {
   6505      continue;
   6506    }
   6507 
   6508    if (useMozBoxCollapseBehavior &&
   6509        childFrame->StyleVisibility()->IsCollapse()) {
   6510      // If we're using legacy "visibility:collapse" behavior, then we don't
   6511      // care about the sizes of any collapsed children.
   6512      continue;
   6513    }
   6514 
   6515    const auto childWM = childFrame->GetWritingMode();
   6516    const IntrinsicSizeInput childInput(aInput, childWM, flexWM);
   6517    const auto* styleFrame = nsLayoutUtils::GetStyleFrame(childFrame);
   6518    const auto childAnchorResolutionParams =
   6519        AnchorPosResolutionParams::From(styleFrame);
   6520    const auto* childStylePos = styleFrame->StylePosition();
   6521 
   6522    // A flex item with a definite block size can transfer its block size to the
   6523    // inline-axis via its own aspect-ratio or serve as a percentage basis for
   6524    // its children with aspect-ratios. Both can influence the item's intrinsic
   6525    // inline size contribution to the flex container's intrinsic inline size.
   6526    //
   6527    // This helper function determines whether we should "pre-stretch" a flex
   6528    // item's cross size (with that size considered to be definite) based on the
   6529    // flex container's definite cross size.
   6530    //
   6531    // Note: The logic here is similar to the "pre-stretch" in
   6532    // GenerateFlexItemForChild().
   6533    const bool childShouldStretchCrossSize = [&]() {
   6534      if (!isSingleLine || axisTracker.IsColumnOriented()) {
   6535        // We only perform "pre-stretch" for the item's cross size if the flex
   6536        // container is single-line and row-oriented.
   6537        return false;
   6538      }
   6539      if (!aInput.mPercentageBasisForChildren ||
   6540          aInput.mPercentageBasisForChildren->BSize(flexWM) ==
   6541              NS_UNCONSTRAINEDSIZE) {
   6542        // The flex container does not have a definite cross size to stretch the
   6543        // items.
   6544        //
   6545        // Note: if the flex container has a definite cross size (for items to
   6546        // pre-stretch to fill), it should be passed down in
   6547        // mPercentageBasisForChildren -- specifically in the BSize component,
   6548        // given that we know the flex container is row-oriented at this point.
   6549        return false;
   6550      }
   6551      [[maybe_unused]] auto [alignSelf, flags] =
   6552          UsedAlignSelfAndFlagsForItem(childFrame);
   6553      if (alignSelf != StyleAlignFlags::STRETCH ||
   6554          !childStylePos->BSize(flexWM, childAnchorResolutionParams)
   6555               ->IsAuto() ||
   6556          childFrame->StyleMargin()->HasBlockAxisAuto(
   6557              flexWM, childAnchorResolutionParams)) {
   6558        // Similar to FlexItem::ResolveStretchedCrossSize(), we only stretch
   6559        // the item if it satisfies all the following conditions:
   6560        // - used align-self value is 'stretch' (CSSAlignmentForFlexItem() has
   6561        //   converted 'normal' to 'stretch')
   6562        // - a cross-axis size property of value "auto"
   6563        // - no auto margins in the cross-axis
   6564        // https://drafts.csswg.org/css-flexbox-1/#valdef-align-items-stretch
   6565        return false;
   6566      }
   6567      // Let's stretch the item's cross size.
   6568      return true;
   6569    }();
   6570 
   6571    StyleSizeOverrides sizeOverrides;
   6572    if (childShouldStretchCrossSize) {
   6573      const auto offsetData = childFrame->IntrinsicBSizeOffsets();
   6574      const nscoord boxSizingToMarginEdgeSize =
   6575          childStylePos->mBoxSizing == StyleBoxSizing::Content
   6576              ? offsetData.MarginBorderPadding()
   6577              : offsetData.margin;
   6578      const nscoord stretchedCrossSize =
   6579          std::max(0, aInput.mPercentageBasisForChildren->BSize(flexWM) -
   6580                          boxSizingToMarginEdgeSize);
   6581      const auto stretchedStyleCrossSize = StyleSize::LengthPercentage(
   6582          LengthPercentage::FromAppUnits(stretchedCrossSize));
   6583      // The size override is in the child's own writing mode.
   6584      if (flexWM.IsOrthogonalTo(childWM)) {
   6585        sizeOverrides.mStyleISize.emplace(stretchedStyleCrossSize);
   6586      } else {
   6587        sizeOverrides.mStyleBSize.emplace(stretchedStyleCrossSize);
   6588      }
   6589    }
   6590    nscoord childISize = nsLayoutUtils::IntrinsicForContainer(
   6591        childInput.mContext, childFrame, aType,
   6592        childInput.mPercentageBasisForChildren, 0, sizeOverrides);
   6593 
   6594    // * For a row-oriented single-line flex container, the intrinsic
   6595    // {min/pref}-isize is the sum of its items' {min/pref}-isizes and
   6596    // (n-1) column gaps.
   6597    // * For a column-oriented flex container, the intrinsic min isize
   6598    // is the max of its items' min isizes.
   6599    // * For a row-oriented multi-line flex container, the intrinsic
   6600    // pref isize is former (sum), and its min isize is the latter (max).
   6601    if (axisTracker.IsRowOriented() &&
   6602        (isSingleLine || aType == IntrinsicISizeType::PrefISize)) {
   6603      containerISize += childISize;
   6604      if (!onFirstChild) {
   6605        containerISize += mainGapSize;
   6606      }
   6607      onFirstChild = false;
   6608    } else {  // (col-oriented, or MinISize for multi-line row flex container)
   6609      containerISize = std::max(containerISize, childISize);
   6610    }
   6611  }
   6612 
   6613  return containerISize;
   6614 }
   6615 
   6616 nscoord nsFlexContainerFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
   6617                                             IntrinsicISizeType aType) {
   6618  return mCachedIntrinsicSizes.GetOrSet(*this, aType, aInput, [&] {
   6619    return ComputeIntrinsicISize(aInput, aType);
   6620  });
   6621 }
   6622 
   6623 int32_t nsFlexContainerFrame::GetNumLines() const {
   6624  // TODO(emilio, bug 1793251): Treating all row oriented frames as single-lines
   6625  // might not be great for flex-wrap'd containers, consider trying to do
   6626  // better? We probably would need to persist more stuff than we do after
   6627  // layout.
   6628  return FlexboxAxisInfo(this).mIsRowOriented ? 1 : mFrames.GetLength();
   6629 }
   6630 
   6631 bool nsFlexContainerFrame::IsLineIteratorFlowRTL() {
   6632  FlexboxAxisInfo info(this);
   6633  if (info.mIsRowOriented) {
   6634    const bool isRtl = StyleVisibility()->mDirection == StyleDirection::Rtl;
   6635    return info.mIsMainAxisReversed != isRtl;
   6636  }
   6637  return false;
   6638 }
   6639 
   6640 Result<nsILineIterator::LineInfo, nsresult> nsFlexContainerFrame::GetLine(
   6641    int32_t aLineNumber) {
   6642  if (aLineNumber < 0 || aLineNumber >= GetNumLines()) {
   6643    return Err(NS_ERROR_FAILURE);
   6644  }
   6645  FlexboxAxisInfo info(this);
   6646  LineInfo lineInfo;
   6647  if (info.mIsRowOriented) {
   6648    lineInfo.mLineBounds = GetRect();
   6649    lineInfo.mFirstFrameOnLine = mFrames.FirstChild();
   6650    // This isn't quite ideal for multi-line row flexbox, see bug 1793251.
   6651    lineInfo.mNumFramesOnLine = mFrames.GetLength();
   6652  } else {
   6653    // TODO(emilio, bug 1793322): Deal with column-reverse (mIsMainAxisReversed)
   6654    nsIFrame* f = mFrames.FrameAt(aLineNumber);
   6655    lineInfo.mLineBounds = f->GetRect();
   6656    lineInfo.mFirstFrameOnLine = f;
   6657    lineInfo.mNumFramesOnLine = 1;
   6658  }
   6659  return lineInfo;
   6660 }
   6661 
   6662 int32_t nsFlexContainerFrame::FindLineContaining(const nsIFrame* aFrame,
   6663                                                 int32_t aStartLine) {
   6664  const int32_t index = mFrames.IndexOf(aFrame);
   6665  if (index < 0) {
   6666    return -1;
   6667  }
   6668  const FlexboxAxisInfo info(this);
   6669  if (info.mIsRowOriented) {
   6670    return 0;
   6671  }
   6672  if (index < aStartLine) {
   6673    return -1;
   6674  }
   6675  return index;
   6676 }
   6677 
   6678 NS_IMETHODIMP
   6679 nsFlexContainerFrame::CheckLineOrder(int32_t aLine, bool* aIsReordered,
   6680                                     nsIFrame** aFirstVisual,
   6681                                     nsIFrame** aLastVisual) {
   6682  *aIsReordered = false;
   6683  *aFirstVisual = nullptr;
   6684  *aLastVisual = nullptr;
   6685  return NS_OK;
   6686 }
   6687 
   6688 NS_IMETHODIMP
   6689 nsFlexContainerFrame::FindFrameAt(int32_t aLineNumber, nsPoint aPos,
   6690                                  nsIFrame** aFrameFound,
   6691                                  bool* aPosIsBeforeFirstFrame,
   6692                                  bool* aPosIsAfterLastFrame) {
   6693  const auto wm = GetWritingMode();
   6694  const LogicalPoint pos(wm, aPos, GetSize());
   6695  const FlexboxAxisInfo info(this);
   6696 
   6697  *aFrameFound = nullptr;
   6698  *aPosIsBeforeFirstFrame = true;
   6699  *aPosIsAfterLastFrame = false;
   6700 
   6701  if (!info.mIsRowOriented) {
   6702    nsIFrame* f = mFrames.FrameAt(aLineNumber);
   6703    if (!f) {
   6704      return NS_OK;
   6705    }
   6706 
   6707    auto rect = f->GetLogicalRect(wm, GetSize());
   6708    *aFrameFound = f;
   6709    *aPosIsBeforeFirstFrame = pos.I(wm) < rect.IStart(wm);
   6710    *aPosIsAfterLastFrame = pos.I(wm) > rect.IEnd(wm);
   6711    return NS_OK;
   6712  }
   6713 
   6714  LineFrameFinder finder(aPos, GetSize(), GetWritingMode(),
   6715                         IsLineIteratorFlowRTL());
   6716  for (nsIFrame* f : mFrames) {
   6717    finder.Scan(f);
   6718    if (finder.IsDone()) {
   6719      break;
   6720    }
   6721  }
   6722  finder.Finish(aFrameFound, aPosIsBeforeFirstFrame, aPosIsAfterLastFrame);
   6723  return NS_OK;
   6724 }