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

/* rendering object for CSS "display: flex" and "display: -webkit-box" */

#ifndef nsFlexContainerFrame_h___
#define nsFlexContainerFrame_h___

#include <tuple>

#include "mozilla/dom/FlexBinding.h"
#include "mozilla/UniquePtr.h"
#include "nsContainerFrame.h"
#include "nsILineIterator.h"

namespace mozilla {
class LogicalPoint;
class PresShell;
}  // namespace mozilla

nsContainerFrame* NS_NewFlexContainerFrame(mozilla::PresShell* aPresShell,
                                           mozilla::ComputedStyle* aStyle);

/**
 * These structures are used to capture data during reflow to be
 * extracted by devtools via Chrome APIs. The structures are only
 * created when requested in GetFlexFrameWithComputedInfo(), and
 * the structures are attached to the nsFlexContainerFrame via the
 * FlexContainerInfo property.
 */
struct ComputedFlexItemInfo {
  nsCOMPtr<nsINode> mNode;
  nsRect mFrameRect;
  /**
   * mMainBaseSize is a measure of the size of the item in the main
   * axis before the flex sizing algorithm is applied. In the spec,
   * this is called "flex base size", but we use this name to connect
   * the value to the other main axis sizes.
   */
  nscoord mMainBaseSize;
  /**
   * mMainDeltaSize is the amount that the flex sizing algorithm
   * adds to the mMainBaseSize, before clamping to mMainMinSize and
   * mMainMaxSize. This can be thought of as the amount by which the
   * flex layout algorithm "wants" to shrink or grow the item, and
   * would do, if it was unconstrained. Since the flex sizing
   * algorithm proceeds linearly, the mMainDeltaSize for an item only
   * respects the resolved size of items already frozen.
   */
  nscoord mMainDeltaSize;
  nscoord mMainMinSize;
  nscoord mMainMaxSize;
  nscoord mCrossMinSize;
  nscoord mCrossMaxSize;
  mozilla::dom::FlexItemClampState mClampState;
};

struct ComputedFlexLineInfo {
  nsTArray<ComputedFlexItemInfo> mItems;
  nscoord mCrossStart;
  nscoord mCrossSize;
  nscoord mFirstBaselineOffset;
  nscoord mLastBaselineOffset;
  mozilla::dom::FlexLineGrowthState mGrowthState;
};

struct ComputedFlexContainerInfo {
  nsTArray<ComputedFlexLineInfo> mLines;
  mozilla::dom::FlexPhysicalDirection mMainAxisDirection;
  mozilla::dom::FlexPhysicalDirection mCrossAxisDirection;
};

/**
 * Helper class to get the orientation of a flex container's axes.
 */
class MOZ_STACK_CLASS FlexboxAxisInfo final {
 public:
  explicit FlexboxAxisInfo(const nsIFrame* aFlexContainer);

  // Is our main axis the inline axis? (Are we 'flex-direction:row[-reverse]'?)
  bool mIsRowOriented = true;

  // Is our main axis in the opposite direction as mWM's corresponding axis?
  // (e.g. RTL vs LTR)
  bool mIsMainAxisReversed = false;

  // Is our cross axis in the opposite direction as mWM's corresponding axis?
  // (e.g. BTT vs TTB)
  bool mIsCrossAxisReversed = false;

 private:
  // Helpers for constructor which determine the orientation of our axes, based
  // on legacy box properties (-webkit-box-orient, -webkit-box-direction) or
  // modern flexbox properties (flex-direction, flex-wrap) depending on whether
  // the flex container is a "legacy box" (as determined by IsLegacyBox).
  void InitAxesFromLegacyProps(const nsIFrame* aFlexContainer);
  void InitAxesFromModernProps(const nsIFrame* aFlexContainer);
};

/**
 * This is the rendering object used for laying out elements with
 * "display: flex" or "display: inline-flex".
 *
 * We also use this class for elements with "display: -webkit-box" or
 * "display: -webkit-inline-box" (but not "-moz-box" / "-moz-inline-box" --
 * those are rendered with old-school XUL frame classes).
 *
 * Note: we represent the -webkit-box family of properties (-webkit-box-orient,
 * -webkit-box-flex, etc.) as aliases for their -moz equivalents.  And for
 * -webkit-{inline-}box containers, nsFlexContainerFrame will honor those
 * "legacy" properties for alignment/flexibility/etc. *instead of* honoring the
 * modern flexbox & alignment properties.  For brevity, many comments in
 * nsFlexContainerFrame.cpp simply refer to these properties using their
 * "-webkit" versions, since we're mostly expecting to encounter them in that
 * form. (Technically, the "-moz" versions of these properties *can* influence
 * layout here as well (since that's what the -webkit versions are aliased to)
 * -- but only inside of a "display:-webkit-{inline-}box" container.)
 */
class nsFlexContainerFrame final : public nsContainerFrame,
                                   public nsILineIterator {
 public:
  NS_DECL_FRAMEARENA_HELPERS(nsFlexContainerFrame)
  NS_DECL_QUERYFRAME

  // Factory method:
  friend nsContainerFrame* NS_NewFlexContainerFrame(
      mozilla::PresShell* aPresShell, ComputedStyle* aStyle);

  // Forward-decls of helper classes
  class FlexItem;
  class FlexLine;
  class FlexboxAxisTracker;
  struct StrutInfo;
  class CachedBAxisMeasurement;
  class CachedFlexItemData;
  struct SharedFlexData;
  struct PerFragmentFlexData;
  class FlexItemIterator;

  // nsIFrame overrides
  void Init(nsIContent* aContent, nsContainerFrame* aParent,
            nsIFrame* aPrevInFlow) override;

  bool IsFrameOfType(uint32_t aFlags) const override {
    return nsContainerFrame::IsFrameOfType(
        aFlags & ~(nsIFrame::eCanContainOverflowContainers));
  }

  void BuildDisplayList(nsDisplayListBuilder* aBuilder,
                        const nsDisplayListSet& aLists) override;

  void MarkIntrinsicISizesDirty() override;

  void Reflow(nsPresContext* aPresContext, ReflowOutput& aReflowOutput,
              const ReflowInput& aReflowInput,
              nsReflowStatus& aStatus) override;

  nscoord GetMinISize(gfxContext* aRenderingContext) override;
  nscoord GetPrefISize(gfxContext* aRenderingContext) override;

#ifdef DEBUG_FRAME_DUMP
  nsresult GetFrameName(nsAString& aResult) const override;
#endif

  Maybe<nscoord> GetNaturalBaselineBOffset(
      mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup,
      BaselineExportContext) const override;

  // Unions the child overflow from our in-flow children.
  void UnionInFlowChildOverflow(mozilla::OverflowAreas&);

  // Unions the child overflow from all our children, including out of flows.
  void UnionChildOverflow(mozilla::OverflowAreas&) final;

  // nsContainerFrame overrides
  bool DrainSelfOverflowList() override;
  void AppendFrames(ChildListID aListID, nsFrameList&& aFrameList) override;
  void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
                    const nsLineList::iterator* aPrevFrameLine,
                    nsFrameList&& aFrameList) override;
  void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) override;
  mozilla::StyleAlignFlags CSSAlignmentForAbsPosChild(
      const ReflowInput& aChildRI,
      mozilla::LogicalAxis aLogicalAxis) const override;

  /**
   * Helper function to calculate packing space and initial offset of alignment
   * subjects in MainAxisPositionTracker() and CrossAxisPositionTracker() for
   * space-between, space-around, and space-evenly.
   *    * @param aNumThingsToPack             Number of alignment subjects.
   * @param aAlignVal                    Value for align-content or
   *                                     justify-content.
   * @param aFirstSubjectOffset          Outparam for first subject offset.
   * @param aNumPackingSpacesRemaining   Outparam for number of equal-sized
   *                                     packing spaces to apply between each
   *                                     alignment subject.
   * @param aPackingSpaceRemaining       Outparam for total amount of packing
   *                                     space to be divided up.
   */
  static void CalculatePackingSpace(
      uint32_t aNumThingsToPack,
      const mozilla::StyleContentDistribution& aAlignVal,
      nscoord* aFirstSubjectOffset, uint32_t* aNumPackingSpacesRemaining,
      nscoord* aPackingSpaceRemaining);

  /**
   * This property is created by a call to
   * nsFlexContainerFrame::GetFlexFrameWithComputedInfo.
   */
  NS_DECLARE_FRAME_PROPERTY_DELETABLE(FlexContainerInfo,
                                      ComputedFlexContainerInfo)
  /**
   * This function should only be called on a nsFlexContainerFrame
   * that has just been returned by a call to
   * GetFlexFrameWithComputedInfo.
   */
  const ComputedFlexContainerInfo* GetFlexContainerInfo() {
    const ComputedFlexContainerInfo* info = GetProperty(FlexContainerInfo());
    NS_WARNING_ASSERTION(info,
                         "Property generation wasn't requested. "
                         "This is a known issue in Print Preview. "
                         "See Bug 1157012.");
    return info;
  }

  /**
   * Return aFrame as a flex frame after ensuring it has computed flex info.
   * @return nullptr if aFrame is null or doesn't have a flex frame
   *         as its content insertion frame.
   * @note this might destroy layout/style data since it may flush layout.
   */
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  static nsFlexContainerFrame* GetFlexFrameWithComputedInfo(nsIFrame* aFrame);

  /**
   * Given a frame for a flex item, this method returns true IFF that flex
   * item's inline axis is the same as (i.e. not orthogonal to) its flex
   * container's main axis.
   *
   * (This method is only intended to be used from external
   * callers. Inside of flex reflow code, FlexItem::IsInlineAxisMainAxis() is
   * equivalent & more optimal.)
   *
   * @param aFrame a flex item (must return true from IsFlexItem)
   * @return true iff aFrame's inline axis is the same as (i.e. not orthogonal
   *              to) its flex container's main axis. Otherwise, false.
   */
  static bool IsItemInlineAxisMainAxis(nsIFrame* aFrame);

  /**
   * Returns true iff the given computed 'flex-basis' & main-size property
   * values collectively represent a used flex-basis of 'content'.
   * See https://drafts.csswg.org/css-flexbox-1/#valdef-flex-basis-auto
   *
   * @param aFlexBasis the computed 'flex-basis' for a flex item.
   * @param aMainSize the computed main-size property for a flex item.
   */
  static bool IsUsedFlexBasisContent(const mozilla::StyleFlexBasis& aFlexBasis,
                                     const mozilla::StyleSize& aMainSize);

  /**
   * Callback for nsIFrame::MarkIntrinsicISizesDirty() on a flex item.
   */
  static void MarkCachedFlexMeasurementsDirty(nsIFrame* aItemFrame);

  bool CanProvideLineIterator() const final { return true; }
  nsILineIterator* GetLineIterator() final { return this; }
  int32_t GetNumLines() const final;
  bool IsLineIteratorFlowRTL() final;
  mozilla::Result<LineInfo, nsresult> GetLine(int32_t aLineNumber) final;
  int32_t FindLineContaining(nsIFrame* aFrame, int32_t aStartLine = 0) final;
  NS_IMETHOD FindFrameAt(int32_t aLineNumber, nsPoint aPos,
                         nsIFrame** aFrameFound, bool* aPosIsBeforeFirstFrame,
                         bool* aPosIsAfterLastFrame) final;
  NS_IMETHOD CheckLineOrder(int32_t aLine, bool* aIsReordered,
                            nsIFrame** aFirstVisual,
                            nsIFrame** aLastVisual) final;

 protected:
  // Protected constructor & destructor
  explicit nsFlexContainerFrame(ComputedStyle* aStyle,
                                nsPresContext* aPresContext)
      : nsContainerFrame(aStyle, aPresContext, kClassID) {}

  virtual ~nsFlexContainerFrame();

  // Protected flex-container-specific methods / member-vars

  /**
   * This method does the bulk of the flex layout, implementing the algorithm
   * described at: https://drafts.csswg.org/css-flexbox-1/#layout-algorithm
   * (with a few initialization pieces happening in the caller, Reflow().
   *
   * (The logic behind the division of work between Reflow and DoFlexLayout is
   * as follows: DoFlexLayout() begins at the step that we have to jump back
   * to, if we find any visibility:collapse children, and Reflow() does
   * everything before that point.)
   *
   * @param aTentativeContentBoxMainSize the "tentative" content-box main-size
   *                                     of the flex container; "tentative"
   *                                     because it may be unconstrained or may
   *                                     run off the page.
   * @param aTentativeContentBoxCrossSize the "tentative" content-box cross-size
   *                                      of the flex container; "tentative"
   *                                      because it may be unconstrained or may
   *                                      run off the page.
   */
  struct FlexLayoutResult final {
    // The flex lines of the flex container.
    nsTArray<FlexLine> mLines;

    // The absolutely-positioned flex children.
    nsTArray<nsIFrame*> mPlaceholders;

    bool mHasCollapsedItems = false;

    // The final content-box main-size of the flex container as if there's no
    // fragmentation.
    nscoord mContentBoxMainSize = NS_UNCONSTRAINEDSIZE;

    // The final content-box cross-size of the flex container as if there's no
    // fragmentation.
    nscoord mContentBoxCrossSize = NS_UNCONSTRAINEDSIZE;

    // The flex container's ascent for the "first baseline" alignment, derived
    // from any baseline-aligned flex items in the startmost (from the
    // perspective of the flex container's WM) flex line, if any such items
    // exist. Otherwise, nscoord_MIN.
    //
    // Note: this is a distance from the border-box block-start edge.
    nscoord mAscent = NS_UNCONSTRAINEDSIZE;

    // The flex container's ascent for the "last baseline" alignment, derived
    // from any baseline-aligned flex items in the endmost (from the perspective
    // of the flex container's WM) flex line, if any such items exist.
    // Otherwise, nscoord_MIN.
    //
    // Note: this is a distance from the border-box block-end edge. It's
    // different from the identically-named-member FlexItem::mAscentForLast,
    // which is a distance from the item frame's border-box block-start edge.
    nscoord mAscentForLast = NS_UNCONSTRAINEDSIZE;
  };
  FlexLayoutResult DoFlexLayout(
      const ReflowInput& aReflowInput,
      const nscoord aTentativeContentBoxMainSize,
      const nscoord aTentativeContentBoxCrossSize,
      const FlexboxAxisTracker& aAxisTracker, nscoord aMainGapSize,
      nscoord aCrossGapSize, nsTArray<StrutInfo>& aStruts,
      ComputedFlexContainerInfo* const aContainerInfo);

  /**
   * If our devtools have requested a ComputedFlexContainerInfo for this flex
   * container, this method ensures that we have one (and if one already exists,
   * this method reinitializes it to look like a freshly-created one).
   *
   * @return the pointer to a freshly created or reinitialized
   *         ComputedFlexContainerInfo if our devtools have requested it;
   *         otherwise nullptr.
   */
  ComputedFlexContainerInfo* CreateOrClearFlexContainerInfo();

  /**
   * Helpers for DoFlexLayout to computed fields in ComputedFlexContainerInfo.
   */
  static void CreateFlexLineAndFlexItemInfo(
      ComputedFlexContainerInfo& aContainerInfo,
      const nsTArray<FlexLine>& aLines);

  static void ComputeFlexDirections(ComputedFlexContainerInfo& aContainerInfo,
                                    const FlexboxAxisTracker& aAxisTracker);

  static void UpdateFlexLineAndItemInfo(
      ComputedFlexContainerInfo& aContainerInfo,
      const nsTArray<FlexLine>& aLines);

#ifdef DEBUG
  void SanityCheckAnonymousFlexItems() const;
#endif  // DEBUG

  /**
   * Construct a new FlexItem for the given child frame, directly at the end of
   * aLine.
   *
   * Before returning, this method also processes the FlexItem to resolve its
   * flex basis (including e.g. auto-height) as well as to resolve
   * "min-height:auto", via ResolveAutoFlexBasisAndMinSize(). (Basically, the
   * constructed FlexItem will be ready to participate in the "Resolve the
   * Flexible Lengths" step of the Flex Layout Algorithm.)
   * https://drafts.csswg.org/css-flexbox-1/#algo-flex
   *
   * Note that this method **does not** update aLine's main-size bookkeeping to
   * account for the newly-constructed flex item. The caller is responsible for
   * determining whether this line is a good fit for the new item. If so, the
   * caller should update aLine's bookkeeping (via
   * FlexLine::AddLastItemToMainSizeTotals), or move the new item to a new line.
   */
  void GenerateFlexItemForChild(FlexLine& aLine, nsIFrame* aChildFrame,
                                const ReflowInput& aParentReflowInput,
                                const FlexboxAxisTracker& aAxisTracker,
                                const nscoord aTentativeContentBoxCrossSize);

  /**
   * This method looks up cached block-axis measurements for a flex item, or
   * does a measuring reflow and caches those measurements.
   *
   * This avoids exponential reflows - see the comment above the
   * CachedBAxisMeasurement struct.
   */
  const CachedBAxisMeasurement& MeasureBSizeForFlexItem(
      FlexItem& aItem, ReflowInput& aChildReflowInput);

  /**
   * This method performs a "measuring" reflow to get the content BSize of
   * aFlexItem.Frame() (treating it as if it had a computed BSize of "auto"),
   * and returns the resulting BSize measurement.
   * (Helper for ResolveAutoFlexBasisAndMinSize().)
   */
  nscoord MeasureFlexItemContentBSize(FlexItem& aFlexItem,
                                      bool aForceBResizeForMeasuringReflow,
                                      const ReflowInput& aParentReflowInput);

  /**
   * This method resolves an "auto" flex-basis and/or min-main-size value
   * on aFlexItem, if needed.
   * (Helper for GenerateFlexItemForChild().)
   */
  void ResolveAutoFlexBasisAndMinSize(FlexItem& aFlexItem,
                                      const ReflowInput& aItemReflowInput,
                                      const FlexboxAxisTracker& aAxisTracker);

  /**
   * This method:
   *  - Creates FlexItems for all of our child frames (except placeholders).
   *  - Groups those FlexItems into FlexLines.
   *  - Returns those FlexLines in the outparam |aLines|.
   *
   * This corresponds to "Collect flex items into flex lines" step in the spec.
   * https://drafts.csswg.org/css-flexbox-1/#algo-line-break
   *
   * For any child frames which are placeholders, this method will instead just
   * append that child to the outparam |aPlaceholders| for separate handling.
   * (Absolutely positioned children of a flex container are *not* flex items.)
   */
  void GenerateFlexLines(const ReflowInput& aReflowInput,
                         const nscoord aTentativeContentBoxMainSize,
                         const nscoord aTentativeContentBoxCrossSize,
                         const nsTArray<StrutInfo>& aStruts,
                         const FlexboxAxisTracker& aAxisTracker,
                         nscoord aMainGapSize,
                         nsTArray<nsIFrame*>& aPlaceholders,
                         nsTArray<FlexLine>& aLines, bool& aHasCollapsedItems);

  /**
   * Generates and returns a FlexLayoutResult that contains the FlexLines and
   * some sizing metrics that should be used to lay out a particular flex
   * container continuation (i.e. don't call this on the first-in-flow).
   */
  FlexLayoutResult GenerateFlexLayoutResult();

  /**
   * Resolves the content-box main-size of a flex container frame,
   * primarily based on:
   * - the "tentative" main size, taken from the reflow input ("tentative"
   *   because it may be unconstrained or may run off the page).
   * - the sizes of our lines of flex items.
   *
   * We assume the available block-size is always *unconstrained* because this
   * is called only in flex algorithm to measure the flex container's size
   * without regards to pagination.
   *
   * Guaranteed to return a definite length, i.e. not NS_UNCONSTRAINEDSIZE,
   * aside from cases with huge lengths which happen to compute to that value.
   *
   * This corresponds to "Determine the main size of the flex container" step in
   * the spec. https://drafts.csswg.org/css-flexbox-1/#algo-main-container
   *
   * (Note: This function should be structurally similar to
   *  ComputeCrossSize().)
   */
  nscoord ComputeMainSize(const ReflowInput& aReflowInput,
                          const FlexboxAxisTracker& aAxisTracker,
                          const nscoord aTentativeContentBoxMainSize,
                          nsTArray<FlexLine>& aLines) const;

  nscoord ComputeCrossSize(const ReflowInput& aReflowInput,
                           const FlexboxAxisTracker& aAxisTracker,
                           const nscoord aTentativeContentBoxCrossSize,
                           nscoord aSumLineCrossSizes, bool* aIsDefinite) const;

  /**
   * Compute the size of the available space that we'll give to our children to
   * reflow into. In particular, compute the available size that we would give
   * to a hypothetical child placed at the IStart/BStart corner of this flex
   * container's content-box.
   *
   * @param aReflowInput the flex container's reflow input.
   * @param aBorderPadding the border and padding of this frame with the
   *                       assumption that this is the last fragment.
   *
   * @return the size of the available space for our children to reflow into.
   */
  mozilla::LogicalSize ComputeAvailableSizeForItems(
      const ReflowInput& aReflowInput,
      const mozilla::LogicalMargin& aBorderPadding) const;

  void SizeItemInCrossAxis(ReflowInput& aChildReflowInput, FlexItem& aItem);

  /**
   * This method computes the metrics to be reported via the flex container's
   * ReflowOutput & nsReflowStatus output parameters in Reflow().
   *
   * @param aContentBoxSize the final content-box size for the flex container as
   *                        a whole, converted from the flex container's
   *                        main/cross sizes. The main/cross sizes are computed
   *                        by DoFlexLayout() if this frame is the
   *                        first-in-flow, or are the stored ones in
   *                        SharedFlexData if this frame is a not the
   *                        first-in-flow.
   * @param aBorderPadding the border and padding for this frame (possibly with
   *                       some sides skipped as-appropriate, if we're in a
   *                       continuation chain).
   * @param aConsumedBSize the sum of our content-box block-size consumed by our
   *                       prev-in-flows.
   * @param aMayNeedNextInFlow true if we may need a next-in-flow because our
   *                           effective content-box block-size exceeds the
   *                           available block-size.
   * @param aMaxBlockEndEdgeOfChildren the maximum block-end edge of the
   *                                   children of this fragment in this frame's
   *                                   coordinate space (as returned by
   *                                   ReflowChildren()).
   * @param aAnyChildIncomplete true if any child being reflowed is incomplete;
   *                            false otherwise (as returned by
   *                            ReflowChildren()).
   * @param aFlr the result returned by DoFlexLayout.
   *             Note: aFlr is mostly an "input" parameter, but we use
   *             aFlr.mAscent as an "in/out" parameter; it's initially the
   *             "tentative" flex container ascent computed in DoFlexLayout; or
   *             nscoord_MIN if the ascent hasn't been established yet. If the
   *             latter, this will be updated with an ascent derived from the
   *             (WM-relative) startmost flex item (if there are any flex
   *             items). Similar for aFlr.mAscentForLast.
   */
  void PopulateReflowOutput(
      ReflowOutput& aReflowOutput, const ReflowInput& aReflowInput,
      nsReflowStatus& aStatus, const mozilla::LogicalSize& aContentBoxSize,
      const mozilla::LogicalMargin& aBorderPadding,
      const nscoord aConsumedBSize, const bool aMayNeedNextInFlow,
      const nscoord aMaxBlockEndEdgeOfChildren, const bool aAnyChildIncomplete,
      const FlexboxAxisTracker& aAxisTracker, FlexLayoutResult& aFlr);

  /**
   * Perform a final Reflow for our child frames.
   *
   * @param aContainerSize this frame's tentative physical border-box size, used
   *                       only for logical to physical coordinate conversion.
   * @param aAvailableSizeForItems the size of the available space for our
   *                               children to reflow into.
   * @param aBorderPadding the border and padding for this frame (possibly with
   *                       some sides skipped as-appropriate, if we're in a
   *                       continuation chain).
   * @param aSumOfPrevInFlowsChildrenBlockSize See the comment for
   *                                           SumOfChildrenBlockSizeProperty.
   * @param aFlr the result returned by DoFlexLayout.
   * @param aFragmentData See the comment for PerFragmentFlexData.
   *                      Note: aFragmentData is an "in/out" parameter. It is
   *                      initialized by the data stored in our prev-in-flow's
   *                      PerFragmentFlexData::Prop(); its fields will then be
   *                      updated and become our PerFragmentFlexData.
   * @return nscoord the maximum block-end edge of children of this fragment in
   *                 flex container's coordinate space.
   * @return bool true if any child being reflowed is incomplete; false
   *              otherwise.
   */
  std::tuple<nscoord, bool> ReflowChildren(
      const ReflowInput& aReflowInput, const nsSize& aContainerSize,
      const mozilla::LogicalSize& aAvailableSizeForItems,
      const mozilla::LogicalMargin& aBorderPadding,
      const FlexboxAxisTracker& aAxisTracker, FlexLayoutResult& aFlr,
      PerFragmentFlexData& aFragmentData);

  /**
   * Moves the given flex item's frame to the given LogicalPosition (modulo any
   * relative positioning).
   *
   * This can be used in cases where we've already done a "measuring reflow"
   * for the flex item at the correct size, and hence can skip its final reflow
   * (but still need to move it to the right final position).
   *
   * @param aItem           The flex item whose frame should be moved.
   * @param aFramePos       The position where the flex item's frame should
   *                        be placed. (pre-relative positioning)
   * @param aContainerSize  The flex container's size (required by some methods
   *                        that we call, to interpret aFramePos correctly).
   */
  void MoveFlexItemToFinalPosition(const FlexItem& aItem,
                                   const mozilla::LogicalPoint& aFramePos,
                                   const nsSize& aContainerSize);
  /**
   * Helper-function to reflow a child frame, at its final position determined
   * by flex layout.
   *
   * @param aAxisTracker    A FlexboxAxisTracker with the flex container's axes.
   * @param aReflowInput    The flex container's reflow input.
   * @param aItem           The flex item to be reflowed.
   * @param aFramePos       The position where the flex item's frame should
   *                        be placed. (pre-relative positioning)
   * @param aAvailableSize  The available size to reflow the child frame (in the
   *                        child frame's writing-mode).
   * @param aContainerSize  The flex container's size (required by some methods
   *                        that we call, to interpret aFramePos correctly).
   * @return the child frame's reflow status.
   */
  nsReflowStatus ReflowFlexItem(const FlexboxAxisTracker& aAxisTracker,
                                const ReflowInput& aReflowInput,
                                const FlexItem& aItem,
                                const mozilla::LogicalPoint& aFramePos,
                                const mozilla::LogicalSize& aAvailableSize,
                                const nsSize& aContainerSize);

  /**
   * Helper-function to perform a "dummy reflow" on all our nsPlaceholderFrame
   * children, at the container's content-box origin.
   *
   * This doesn't actually represent the static position of the placeholders'
   * out-of-flow (OOF) frames -- we can't compute that until we've reflowed the
   * OOF, because (depending on the CSS Align properties) the static position
   * may be influenced by the OOF's size.  So for now, we just co-opt the
   * placeholder to store the flex container's logical content-box origin, and
   * we defer to nsAbsoluteContainingBlock to determine the OOF's actual static
   * position (using this origin, the OOF's size, and the CSS Align
   * properties).
   *
   * @param aReflowInput       The flex container's reflow input.
   * @param aPlaceholders      An array of all the flex container's
   *                           nsPlaceholderFrame children.
   * @param aContentBoxOrigin  The flex container's logical content-box
   *                           origin (in its own coordinate space).
   * @param aContainerSize     The flex container's size (required by some
   *                           reflow methods to interpret positions correctly).
   */
  void ReflowPlaceholders(const ReflowInput& aReflowInput,
                          nsTArray<nsIFrame*>& aPlaceholders,
                          const mozilla::LogicalPoint& aContentBoxOrigin,
                          const nsSize& aContainerSize);

  /**
   * Helper for GetMinISize / GetPrefISize.
   */
  nscoord IntrinsicISize(gfxContext* aRenderingContext,
                         mozilla::IntrinsicISizeType aType);

  /**
   * Cached values to optimize GetMinISize/GetPrefISize.
   */
  nscoord mCachedMinISize = NS_INTRINSIC_ISIZE_UNKNOWN;
  nscoord mCachedPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN;

  /**
   * Cached baselines computed in our last reflow to optimize
   * GetNaturalBaselineBOffset().
   */
  // Note: the first baseline is a distance from our border-box block-start
  // edge.
  nscoord mFirstBaseline = NS_INTRINSIC_ISIZE_UNKNOWN;
  // Note: the last baseline is a distance from our border-box block-end edge.
  nscoord mLastBaseline = NS_INTRINSIC_ISIZE_UNKNOWN;
};

#endif /* nsFlexContainerFrame_h___ */
