/* -*- 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/. */

/* struct containing the input to nsIFrame::Reflow */

#ifndef mozilla_ReflowInput_h
#define mozilla_ReflowInput_h

#include "nsMargin.h"
#include "nsStyleConsts.h"
#include "mozilla/Assertions.h"
#include "mozilla/EnumSet.h"
#include "mozilla/Maybe.h"
#include "mozilla/WritingModes.h"
#include "LayoutConstants.h"
#include "ReflowOutput.h"
#include <algorithm>

class gfxContext;
class nsFloatManager;
struct nsHypotheticalPosition;
class nsIPercentBSizeObserver;
class nsLineLayout;
class nsPlaceholderFrame;
class nsPresContext;
class nsReflowStatus;

namespace mozilla {
enum class LayoutFrameType : uint8_t;

/**
 * A set of StyleSizes used as an input parameter to various functions that
 * compute sizes like nsIFrame::ComputeSize(). If any of the member fields has a
 * value, the function may use the value instead of retrieving it from the
 * frame's style.
 *
 * The logical sizes are assumed to be in the associated frame's writing-mode.
 */
struct StyleSizeOverrides {
  Maybe<StyleSize> mStyleISize;
  Maybe<StyleSize> mStyleBSize;
  Maybe<AspectRatio> mAspectRatio;

  bool HasAnyOverrides() const { return mStyleISize || mStyleBSize; }
  bool HasAnyLengthOverrides() const {
    return (mStyleISize && mStyleISize->ConvertsToLength()) ||
           (mStyleBSize && mStyleBSize->ConvertsToLength());
  }

  // By default, table wrapper frame considers the size overrides applied to
  // itself, so it creates any length size overrides for inner table frame by
  // subtracting the area occupied by the caption and border & padding according
  // to box-sizing.
  //
  // When this flag is true, table wrapper frame is required to apply the size
  // overrides to the inner table frame directly, without any modification,
  // which is useful for flex container to override the inner table frame's
  // preferred main size with 'flex-basis'.
  //
  // Note: if mStyleISize is a LengthPercentage, the inner table frame will
  // comply with the inline-size override without enforcing its min-content
  // inline-size in nsTableFrame::ComputeSize(). This is necessary so that small
  // flex-basis values like 'flex-basis:1%' can be resolved correctly; the
  // flexbox layout algorithm does still explicitly clamp to min-sizes *at a
  // later step*, after the flex-basis has been resolved -- so this flag won't
  // actually produce any user-visible tables whose final inline size is smaller
  // than their min-content inline size.
  bool mApplyOverridesVerbatim = false;
};
}  // namespace mozilla

/**
 * @return aValue clamped to [aMinValue, aMaxValue].
 *
 * @note This function needs to handle aMinValue > aMaxValue. In that case,
 *       aMinValue is returned. That's why we cannot use std::clamp() and
 *       mozilla::clamped() since they both assert max >= min.
 * @see http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
 * @see http://www.w3.org/TR/CSS21/visudet.html#min-max-heights
 */
template <class NumericType>
NumericType NS_CSS_MINMAX(NumericType aValue, NumericType aMinValue,
                          NumericType aMaxValue) {
  NumericType result = aValue;
  if (aMaxValue < result) result = aMaxValue;
  if (aMinValue > result) result = aMinValue;
  return result;
}

namespace mozilla {

// A base class of ReflowInput that computes only the padding,
// border, and margin, since those values are needed more often.
struct SizeComputationInput {
 public:
  // The frame being reflowed.
  nsIFrame* const mFrame;

  // Rendering context to use for measurement.
  gfxContext* mRenderingContext;

  nsMargin ComputedPhysicalMargin() const {
    return mComputedMargin.GetPhysicalMargin(mWritingMode);
  }
  nsMargin ComputedPhysicalBorderPadding() const {
    return mComputedBorderPadding.GetPhysicalMargin(mWritingMode);
  }
  nsMargin ComputedPhysicalPadding() const {
    return mComputedPadding.GetPhysicalMargin(mWritingMode);
  }

  mozilla::LogicalMargin ComputedLogicalMargin(mozilla::WritingMode aWM) const {
    return mComputedMargin.ConvertTo(aWM, mWritingMode);
  }
  mozilla::LogicalMargin ComputedLogicalBorderPadding(
      mozilla::WritingMode aWM) const {
    return mComputedBorderPadding.ConvertTo(aWM, mWritingMode);
  }
  mozilla::LogicalMargin ComputedLogicalPadding(
      mozilla::WritingMode aWM) const {
    return mComputedPadding.ConvertTo(aWM, mWritingMode);
  }
  mozilla::LogicalMargin ComputedLogicalBorder(mozilla::WritingMode aWM) const {
    return (mComputedBorderPadding - mComputedPadding)
        .ConvertTo(aWM, mWritingMode);
  }

  void SetComputedLogicalMargin(mozilla::WritingMode aWM,
                                const mozilla::LogicalMargin& aMargin) {
    mComputedMargin = aMargin.ConvertTo(mWritingMode, aWM);
  }
  void SetComputedLogicalBorderPadding(
      mozilla::WritingMode aWM, const mozilla::LogicalMargin& aBorderPadding) {
    mComputedBorderPadding = aBorderPadding.ConvertTo(mWritingMode, aWM);
  }
  void SetComputedLogicalPadding(mozilla::WritingMode aWM,
                                 const mozilla::LogicalMargin& aPadding) {
    mComputedPadding = aPadding.ConvertTo(mWritingMode, aWM);
  }

  mozilla::WritingMode GetWritingMode() const { return mWritingMode; }

 protected:
  // cached copy of the frame's writing-mode, for logical coordinates
  const mozilla::WritingMode mWritingMode;

  // Cached mFrame->IsThemed().
  const bool mIsThemed = false;

  // Computed margin values
  mozilla::LogicalMargin mComputedMargin;

  // Cached copy of the border + padding values
  mozilla::LogicalMargin mComputedBorderPadding;

  // Computed padding values
  mozilla::LogicalMargin mComputedPadding;

 public:
  // Callers using this constructor must call InitOffsets on their own.
  SizeComputationInput(nsIFrame* aFrame, gfxContext* aRenderingContext);

  SizeComputationInput(nsIFrame* aFrame, gfxContext* aRenderingContext,
                       mozilla::WritingMode aContainingBlockWritingMode,
                       nscoord aContainingBlockISize,
                       const mozilla::Maybe<mozilla::LogicalMargin>& aBorder =
                           mozilla::Nothing(),
                       const mozilla::Maybe<mozilla::LogicalMargin>& aPadding =
                           mozilla::Nothing());

#ifdef DEBUG
  // Reflow trace methods.  Defined in nsFrame.cpp so they have access
  // to the display-reflow infrastructure.
  static void* DisplayInitOffsetsEnter(nsIFrame* aFrame,
                                       SizeComputationInput* aState,
                                       nscoord aPercentBasis,
                                       mozilla::WritingMode aCBWritingMode,
                                       const nsMargin* aBorder,
                                       const nsMargin* aPadding);
  static void DisplayInitOffsetsExit(nsIFrame* aFrame,
                                     SizeComputationInput* aState,
                                     void* aValue);
#endif

 private:
  /**
   * Computes margin values from the specified margin style information, and
   * fills in the mComputedMargin member.
   *
   * @param aCBWM Writing mode of the containing block
   * @param aPercentBasis
   *    Inline size of the containing block (in its own writing mode), to use
   *    for resolving percentage margin values in the inline and block axes.
   * @return true if the margin is dependent on the containing block size.
   */
  bool ComputeMargin(mozilla::WritingMode aCBWM, nscoord aPercentBasis,
                     mozilla::LayoutFrameType aFrameType);

  /**
   * Computes padding values from the specified padding style information, and
   * fills in the mComputedPadding member.
   *
   * @param aCBWM Writing mode of the containing block
   * @param aPercentBasis
   *    Inline size of the containing block (in its own writing mode), to use
   *    for resolving percentage padding values in the inline and block axes.
   * @return true if the padding is dependent on the containing block size.
   */
  bool ComputePadding(mozilla::WritingMode aCBWM, nscoord aPercentBasis,
                      mozilla::LayoutFrameType aFrameType);

 protected:
  void InitOffsets(mozilla::WritingMode aCBWM, nscoord aPercentBasis,
                   mozilla::LayoutFrameType aFrameType,
                   mozilla::ComputeSizeFlags aFlags,
                   const mozilla::Maybe<mozilla::LogicalMargin>& aBorder,
                   const mozilla::Maybe<mozilla::LogicalMargin>& aPadding,
                   const nsStyleDisplay* aDisplay = nullptr);

  /*
   * Convert StyleSize or StyleMaxSize to nscoord when percentages depend on the
   * inline size of the containing block, and enumerated values are for inline
   * size, min-inline-size, or max-inline-size.  Does not handle auto inline
   * sizes.
   */
  template <typename SizeOrMaxSize>
  inline nscoord ComputeISizeValue(const WritingMode aWM,
                                   const LogicalSize& aContainingBlockSize,
                                   const LogicalSize& aContentEdgeToBoxSizing,
                                   nscoord aBoxSizingToMarginEdge,
                                   const SizeOrMaxSize&) const;
  // same as previous, but using mComputedBorderPadding, mComputedPadding,
  // and mComputedMargin
  template <typename SizeOrMaxSize>
  inline nscoord ComputeISizeValue(const LogicalSize& aContainingBlockSize,
                                   mozilla::StyleBoxSizing aBoxSizing,
                                   const SizeOrMaxSize&) const;

  nscoord ComputeBSizeValue(nscoord aContainingBlockBSize,
                            mozilla::StyleBoxSizing aBoxSizing,
                            const mozilla::LengthPercentage& aCoord) const;
};

/**
 * State passed to a frame during reflow or intrinsic size calculation.
 *
 * XXX Refactor so only a base class (nsSizingState?) is used for intrinsic
 * size calculation.
 *
 * @see nsIFrame#Reflow()
 */
struct ReflowInput : public SizeComputationInput {
  // the reflow inputs are linked together. this is the pointer to the
  // parent's reflow input
  const ReflowInput* mParentReflowInput = nullptr;

  // A non-owning pointer to the float manager associated with this area,
  // which points to the object owned by nsAutoFloatManager::mNew.
  nsFloatManager* mFloatManager = nullptr;

  // LineLayout object (only for inline reflow; set to nullptr otherwise)
  nsLineLayout* mLineLayout = nullptr;

  // The appropriate reflow input for the containing block (for
  // percentage widths, etc.) of this reflow input's frame. It will be setup
  // properly in InitCBReflowInput().
  const ReflowInput* mCBReflowInput = nullptr;

  // The amount the in-flow position of the block is moving vertically relative
  // to its previous in-flow position (i.e. the amount the line containing the
  // block is moving).
  // This should be zero for anything which is not a block outside, and it
  // should be zero for anything which has a non-block parent.
  // The intended use of this value is to allow the accurate determination
  // of the potential impact of a float
  // This takes on an arbitrary value the first time a block is reflowed
  nscoord mBlockDelta = 0;

  // If a ReflowInput finds itself initialized with an unconstrained
  // inline-size, it will look up its parentReflowInput chain for a reflow input
  // with an orthogonal writing mode and a non-NS_UNCONSTRAINEDSIZE value for
  // orthogonal limit; when it finds such a reflow input, it will use its
  // orthogonal-limit value to constrain inline-size.
  // This is initialized to NS_UNCONSTRAINEDSIZE (so it will be ignored),
  // but reset to a suitable value for the reflow root by PresShell.
  nscoord mOrthogonalLimit = NS_UNCONSTRAINEDSIZE;

  // Physical accessors for the private fields. They are needed for
  // compatibility with not-yet-updated code. New code should use the accessors
  // for logical coordinates, unless the code really works on physical
  // coordinates.
  nscoord AvailableWidth() const { return mAvailableSize.Width(mWritingMode); }
  nscoord AvailableHeight() const {
    return mAvailableSize.Height(mWritingMode);
  }
  nscoord ComputedWidth() const { return mComputedSize.Width(mWritingMode); }
  nscoord ComputedHeight() const { return mComputedSize.Height(mWritingMode); }
  nscoord ComputedMinWidth() const {
    return mComputedMinSize.Width(mWritingMode);
  }
  nscoord ComputedMaxWidth() const {
    return mComputedMaxSize.Width(mWritingMode);
  }
  nscoord ComputedMinHeight() const {
    return mComputedMinSize.Height(mWritingMode);
  }
  nscoord ComputedMaxHeight() const {
    return mComputedMaxSize.Height(mWritingMode);
  }

  // Logical accessors for private fields in mWritingMode.
  nscoord AvailableISize() const { return mAvailableSize.ISize(mWritingMode); }
  nscoord AvailableBSize() const { return mAvailableSize.BSize(mWritingMode); }
  nscoord ComputedISize() const { return mComputedSize.ISize(mWritingMode); }
  nscoord ComputedBSize() const { return mComputedSize.BSize(mWritingMode); }
  nscoord ComputedMinISize() const {
    return mComputedMinSize.ISize(mWritingMode);
  }
  nscoord ComputedMaxISize() const {
    return mComputedMaxSize.ISize(mWritingMode);
  }
  nscoord ComputedMinBSize() const {
    return mComputedMinSize.BSize(mWritingMode);
  }
  nscoord ComputedMaxBSize() const {
    return mComputedMaxSize.BSize(mWritingMode);
  }

  // WARNING: In general, adjusting available inline-size or block-size is not
  // safe because ReflowInput has members whose values depend on the available
  // size passing through the constructor. For example,
  // CalculateBlockSideMargins() is called during initialization, and uses
  // AvailableSize(). Make sure your use case doesn't lead to stale member
  // values in ReflowInput!
  void SetAvailableISize(nscoord aAvailableISize) {
    mAvailableSize.ISize(mWritingMode) = aAvailableISize;
  }
  void SetAvailableBSize(nscoord aAvailableBSize) {
    mAvailableSize.BSize(mWritingMode) = aAvailableBSize;
  }

  void SetComputedMinISize(nscoord aMinISize) {
    mComputedMinSize.ISize(mWritingMode) = aMinISize;
  }
  void SetComputedMaxISize(nscoord aMaxISize) {
    mComputedMaxSize.ISize(mWritingMode) = aMaxISize;
  }
  void SetComputedMinBSize(nscoord aMinBSize) {
    mComputedMinSize.BSize(mWritingMode) = aMinBSize;
  }
  void SetComputedMaxBSize(nscoord aMaxBSize) {
    mComputedMaxSize.BSize(mWritingMode) = aMaxBSize;
  }

  mozilla::LogicalSize AvailableSize() const { return mAvailableSize; }
  mozilla::LogicalSize ComputedSize() const { return mComputedSize; }
  mozilla::LogicalSize ComputedMinSize() const { return mComputedMinSize; }
  mozilla::LogicalSize ComputedMaxSize() const { return mComputedMaxSize; }

  mozilla::LogicalSize AvailableSize(mozilla::WritingMode aWM) const {
    return AvailableSize().ConvertTo(aWM, mWritingMode);
  }
  mozilla::LogicalSize ComputedSize(mozilla::WritingMode aWM) const {
    return ComputedSize().ConvertTo(aWM, mWritingMode);
  }
  mozilla::LogicalSize ComputedMinSize(mozilla::WritingMode aWM) const {
    return ComputedMinSize().ConvertTo(aWM, mWritingMode);
  }
  mozilla::LogicalSize ComputedMaxSize(mozilla::WritingMode aWM) const {
    return ComputedMaxSize().ConvertTo(aWM, mWritingMode);
  }

  mozilla::LogicalSize ComputedSizeWithPadding(mozilla::WritingMode aWM) const {
    return ComputedSize(aWM) + ComputedLogicalPadding(aWM).Size(aWM);
  }

  mozilla::LogicalSize ComputedSizeWithBorderPadding(
      mozilla::WritingMode aWM) const {
    return ComputedSize(aWM) + ComputedLogicalBorderPadding(aWM).Size(aWM);
  }

  mozilla::LogicalSize ComputedSizeWithMarginBorderPadding(
      mozilla::WritingMode aWM) const {
    return ComputedSizeWithBorderPadding(aWM) +
           ComputedLogicalMargin(aWM).Size(aWM);
  }

  nsSize ComputedPhysicalSize() const {
    return nsSize(ComputedWidth(), ComputedHeight());
  }

  nsMargin ComputedPhysicalOffsets() const {
    return mComputedOffsets.GetPhysicalMargin(mWritingMode);
  }

  LogicalMargin ComputedLogicalOffsets(mozilla::WritingMode aWM) const {
    return mComputedOffsets.ConvertTo(aWM, mWritingMode);
  }

  void SetComputedLogicalOffsets(mozilla::WritingMode aWM,
                                 const LogicalMargin& aOffsets) {
    mComputedOffsets = aOffsets.ConvertTo(mWritingMode, aWM);
  }

  // Return ReflowInput's computed size including border-padding, with
  // unconstrained dimensions replaced by zero.
  nsSize ComputedSizeAsContainerIfConstrained() const {
    const nscoord wd = ComputedWidth();
    const nscoord ht = ComputedHeight();
    return nsSize(wd == NS_UNCONSTRAINEDSIZE
                      ? 0
                      : wd + ComputedPhysicalBorderPadding().LeftRight(),
                  ht == NS_UNCONSTRAINEDSIZE
                      ? 0
                      : ht + ComputedPhysicalBorderPadding().TopBottom());
  }

  // Our saved containing block dimensions.
  LogicalSize mContainingBlockSize{mWritingMode};

  // Cached pointers to the various style structs used during initialization.
  const nsStyleDisplay* mStyleDisplay = nullptr;
  const nsStylePosition* mStylePosition = nullptr;
  const nsStyleBorder* mStyleBorder = nullptr;
  const nsStyleMargin* mStyleMargin = nullptr;

  enum class BreakType : uint8_t {
    Auto,
    Column,
    Page,
  };
  BreakType mBreakType = BreakType::Auto;

  // a frame (e.g. nsTableCellFrame) which may need to generate a special
  // reflow for percent bsize calculations
  nsIPercentBSizeObserver* mPercentBSizeObserver = nullptr;

  // CSS margin collapsing sometimes requires us to reflow
  // optimistically assuming that margins collapse to see if clearance
  // is required. When we discover that clearance is required, we
  // store the frame in which clearance was discovered to the location
  // requested here.
  nsIFrame** mDiscoveredClearance = nullptr;

  struct Flags {
    Flags() { memset(this, 0, sizeof(*this)); }

    // cached mFrame->IsFrameOfType(nsIFrame::eReplaced) ||
    //        mFrame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)
    bool mIsReplaced : 1;

    // used by tables to communicate special reflow (in process) to handle
    // percent bsize frames inside cells which may not have computed bsizes
    bool mSpecialBSizeReflow : 1;

    // nothing in the frame's next-in-flow (or its descendants) is changing
    bool mNextInFlowUntouched : 1;

    // Is the current context at the top of a page?  When true, we force
    // something that's too tall for a page/column to fit anyway to avoid
    // infinite loops.
    bool mIsTopOfPage : 1;

    // parent frame is an nsIScrollableFrame and it is assuming a horizontal
    // scrollbar
    bool mAssumingHScrollbar : 1;

    // parent frame is an nsIScrollableFrame and it is assuming a vertical
    // scrollbar
    bool mAssumingVScrollbar : 1;

    // Is frame a different inline-size than before?
    bool mIsIResize : 1;

    // Is frame (potentially) a different block-size than before?
    // This includes cases where the block-size is 'auto' and the
    // contents or width have changed.
    bool mIsBResize : 1;

    // Has this frame changed block-size in a way that affects
    // block-size percentages on frames for which it is the containing
    // block?  This includes a change between 'auto' and a length that
    // doesn't actually change the frame's block-size.  It does not
    // include cases where the block-size is 'auto' and the frame's
    // contents have changed.
    //
    // In the current code, this is only true when mIsBResize is also
    // true, although it doesn't necessarily need to be that way (e.g.,
    // in the case of a frame changing from 'auto' to a length that
    // produces the same height).
    bool mIsBResizeForPercentages : 1;

    // tables are splittable, this should happen only inside a page and never
    // insider a column frame
    bool mTableIsSplittable : 1;

    // Does frame height depend on an ancestor table-cell?
    bool mHeightDependsOnAncestorCell : 1;

    // nsColumnSetFrame is balancing columns
    bool mIsColumnBalancing : 1;

    // True if ColumnSetWrapperFrame has a constrained block-size, and is going
    // to consume all of its block-size in this fragment. This bit is passed to
    // nsColumnSetFrame to determine whether to give up balancing and create
    // overflow columns.
    bool mColumnSetWrapperHasNoBSizeLeft : 1;

    // If this flag is set, the BSize of this frame should be considered
    // indefinite for the purposes of percent resolution on child frames (we
    // should behave as if ComputedBSize() were NS_UNCONSTRAINEDSIZE when doing
    // percent resolution against this.ComputedBSize()).  For example: flex
    // items may have their ComputedBSize() resolved ahead-of-time by their
    // flex container, and yet their BSize might have to be considered
    // indefinite per https://drafts.csswg.org/css-flexbox/#definite-sizes
    bool mTreatBSizeAsIndefinite : 1;

    // a "fake" reflow input made in order to be the parent of a real one
    bool mDummyParentReflowInput : 1;

    // Should this frame reflow its place-holder children? If the available
    // height of this frame didn't change, but its in a paginated environment
    // (e.g. columns), it should always reflow its placeholder children.
    bool mMustReflowPlaceholders : 1;

    // the STATIC_POS_IS_CB_ORIGIN ctor flag
    bool mStaticPosIsCBOrigin : 1;

    // If set, the following two flags indicate that:
    // (1) this frame is absolutely-positioned (or fixed-positioned).
    // (2) this frame's static position depends on the CSS Box Alignment.
    // (3) we do need to compute the static position, because the frame's
    //     {Inline and/or Block} offsets actually depend on it.
    // When these bits are set, the offset values (IStart/IEnd, BStart/BEnd)
    // represent the "start" edge of the frame's CSS Box Alignment container
    // area, in that axis -- and these offsets need to be further-resolved
    // (with CSS Box Alignment) after we know the OOF frame's size.
    // NOTE: The "I" and "B" (for "Inline" and "Block") refer the axes of the
    // *containing block's writing-mode*, NOT mFrame's own writing-mode. This
    // is purely for convenience, since that's the writing-mode we're dealing
    // with when we set & react to these bits.
    bool mIOffsetsNeedCSSAlign : 1;
    bool mBOffsetsNeedCSSAlign : 1;

    // Is this frame or one of its ancestors being reflowed in a different
    // continuation than the one in which it was previously reflowed?  In
    // other words, has it moved to a different column or page than it was in
    // the previous reflow?
    //
    // FIXME: For now, we only ensure that this is set correctly for blocks.
    // This is okay because the only thing that uses it only cares about
    // whether there's been a fragment change within the same block formatting
    // context.
    bool mMovedBlockFragments : 1;

    // Is the block-size computed by aspect-ratio and inline size (i.e. block
    // axis is the ratio-dependent axis)? We set this flag so that we can check
    // whether to apply automatic content-based minimum sizes once we know the
    // children's block-size (after reflowing them).
    // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
    bool mIsBSizeSetByAspectRatio : 1;

    // If true, then children of this frame can generate class A breakpoints
    // for paginated reflow.
    bool mCanHaveClassABreakpoints : 1;
  };
  Flags mFlags;

  mozilla::StyleSizeOverrides mStyleSizeOverrides;

  mozilla::ComputeSizeFlags mComputeSizeFlags;

  // This value keeps track of how deeply nested a given reflow input
  // is from the top of the frame tree.
  int16_t mReflowDepth = 0;

  // Logical and physical accessors for the resize flags.
  bool IsHResize() const {
    return mWritingMode.IsVertical() ? mFlags.mIsBResize : mFlags.mIsIResize;
  }
  bool IsVResize() const {
    return mWritingMode.IsVertical() ? mFlags.mIsIResize : mFlags.mIsBResize;
  }
  bool IsIResize() const { return mFlags.mIsIResize; }
  bool IsBResize() const { return mFlags.mIsBResize; }
  bool IsBResizeForWM(mozilla::WritingMode aWM) const {
    return aWM.IsOrthogonalTo(mWritingMode) ? mFlags.mIsIResize
                                            : mFlags.mIsBResize;
  }
  bool IsBResizeForPercentagesForWM(mozilla::WritingMode aWM) const {
    // This uses the relatively-accurate mIsBResizeForPercentages flag
    // when the writing modes are parallel, and is a bit more
    // pessimistic when orthogonal.
    return !aWM.IsOrthogonalTo(mWritingMode) ? mFlags.mIsBResizeForPercentages
                                             : IsIResize();
  }
  void SetHResize(bool aValue) {
    if (mWritingMode.IsVertical()) {
      mFlags.mIsBResize = aValue;
    } else {
      mFlags.mIsIResize = aValue;
    }
  }
  void SetVResize(bool aValue) {
    if (mWritingMode.IsVertical()) {
      mFlags.mIsIResize = aValue;
    } else {
      mFlags.mIsBResize = aValue;
    }
  }
  void SetIResize(bool aValue) { mFlags.mIsIResize = aValue; }
  void SetBResize(bool aValue) { mFlags.mIsBResize = aValue; }

  // Values for |aFlags| passed to constructor
  enum class InitFlag : uint8_t {
    // Indicates that the parent of this reflow input is "fake" (see
    // mDummyParentReflowInput in mFlags).
    DummyParentReflowInput,

    // Indicates that the calling function will initialize the reflow input, and
    // that the constructor should not call Init().
    CallerWillInit,

    // The caller wants the abs.pos. static-position resolved at the origin of
    // the containing block, i.e. at LogicalPoint(0, 0). (Note that this
    // doesn't necessarily mean that (0, 0) is the *correct* static position
    // for the frame in question.)
    // @note In a Grid container's masonry axis we'll always use
    // the placeholder's position in that axis regardless of this flag.
    StaticPosIsCBOrigin,
  };
  using InitFlags = mozilla::EnumSet<InitFlag>;

  // Note: The copy constructor is written by the compiler automatically. You
  // can use that and then override specific values if you want, or you can
  // call Init as desired...

  /**
   * Initialize a ROOT reflow input.
   *
   * @param aPresContext Must be equal to aFrame->PresContext().
   * @param aFrame The frame for whose reflow input is being constructed.
   * @param aRenderingContext The rendering context to be used for measurements.
   * @param aAvailableSpace The available space to reflow aFrame (in aFrame's
   *        writing-mode). See comments for mAvailableSize for more information.
   * @param aFlags A set of flags used for additional boolean parameters (see
   *        InitFlags above).
   */
  ReflowInput(nsPresContext* aPresContext, nsIFrame* aFrame,
              gfxContext* aRenderingContext,
              const mozilla::LogicalSize& aAvailableSpace,
              InitFlags aFlags = {});

  /**
   * Initialize a reflow input for a child frame's reflow. Some parts of the
   * state are copied from the parent's reflow input. The remainder is computed.
   *
   * @param aPresContext Must be equal to aFrame->PresContext().
   * @param aParentReflowInput A reference to an ReflowInput object that
   *        is to be the parent of this object.
   * @param aFrame The frame for whose reflow input is being constructed.
   * @param aAvailableSpace The available space to reflow aFrame (in aFrame's
   *        writing-mode). See comments for mAvailableSize for more information.
   * @param aContainingBlockSize An optional size (in aFrame's writing mode),
   *        specifying the containing block size to use instead of the default
   *        size computed by ComputeContainingBlockRectangle(). If
   *        InitFlag::CallerWillInit is used, this is ignored. Pass it via
   *        Init() instead.
   * @param aFlags A set of flags used for additional boolean parameters (see
   *        InitFlags above).
   * @param aStyleSizeOverrides The style data used to override mFrame's when we
   *        call nsIFrame::ComputeSize() internally.
   * @param aComputeSizeFlags A set of flags used when we call
   *        nsIFrame::ComputeSize() internally.
   */
  ReflowInput(nsPresContext* aPresContext,
              const ReflowInput& aParentReflowInput, nsIFrame* aFrame,
              const mozilla::LogicalSize& aAvailableSpace,
              const mozilla::Maybe<mozilla::LogicalSize>& aContainingBlockSize =
                  mozilla::Nothing(),
              InitFlags aFlags = {},
              const mozilla::StyleSizeOverrides& aSizeOverrides = {},
              mozilla::ComputeSizeFlags aComputeSizeFlags = {});

  /**
   * This method initializes various data members. It is automatically called by
   * the constructors if InitFlags::CallerWillInit is *not* used.
   *
   * @param aContainingBlockSize An optional size (in mFrame's writing mode),
   *        specifying the containing block size to use instead of the default
   *        size computed by ComputeContainingBlockRectangle().
   * @param aBorder An optional border (in mFrame's writing mode). If given, use
   *        it instead of the border computed from mFrame's StyleBorder.
   * @param aPadding An optional padding (in mFrame's writing mode). If given,
   *        use it instead of the padding computing from mFrame's StylePadding.
   */
  void Init(nsPresContext* aPresContext,
            const mozilla::Maybe<mozilla::LogicalSize>& aContainingBlockSize =
                mozilla::Nothing(),
            const mozilla::Maybe<mozilla::LogicalMargin>& aBorder =
                mozilla::Nothing(),
            const mozilla::Maybe<mozilla::LogicalMargin>& aPadding =
                mozilla::Nothing());

  /**
   * Get the used line-height property. The return value will be >= 0.
   */
  nscoord GetLineHeight() const;

  /**
   * Set the used line-height. aLineHeight must be >= 0.
   */
  void SetLineHeight(nscoord aLineHeight);

  /**
   * Calculate the used line-height property without a reflow input instance.
   * The return value will be >= 0.
   *
   * @param aBlockBSize The computed block size of the content rect of the block
   *                    that the line should fill. Only used with
   *                    line-height:-moz-block-height. NS_UNCONSTRAINEDSIZE
   *                    results in a normal line-height for
   *                    line-height:-moz-block-height.
   * @param aFontSizeInflation The result of the appropriate
   *                           nsLayoutUtils::FontSizeInflationFor call,
   *                           or 1.0 if during intrinsic size
   *                           calculation.
   */
  static nscoord CalcLineHeight(const ComputedStyle&,
                                nsPresContext* aPresContext,
                                const nsIContent* aContent, nscoord aBlockBSize,
                                float aFontSizeInflation);

  static nscoord CalcLineHeight(const StyleLineHeight&,
                                const nsStyleFont& aRelativeToFont,
                                nsPresContext* aPresContext, bool aIsVertical,
                                const nsIContent* aContent, nscoord aBlockBSize,
                                float aFontSizeInflation);

  mozilla::LogicalSize ComputeContainingBlockRectangle(
      nsPresContext* aPresContext, const ReflowInput* aContainingBlockRI) const;

  /**
   * Apply the mComputed(Min/Max)Width constraints to the content
   * size computed so far.
   */
  nscoord ApplyMinMaxWidth(nscoord aWidth) const {
    if (NS_UNCONSTRAINEDSIZE != ComputedMaxWidth()) {
      aWidth = std::min(aWidth, ComputedMaxWidth());
    }
    return std::max(aWidth, ComputedMinWidth());
  }

  /**
   * Apply the mComputed(Min/Max)ISize constraints to the content
   * size computed so far.
   */
  nscoord ApplyMinMaxISize(nscoord aISize) const {
    if (NS_UNCONSTRAINEDSIZE != ComputedMaxISize()) {
      aISize = std::min(aISize, ComputedMaxISize());
    }
    return std::max(aISize, ComputedMinISize());
  }

  /**
   * Apply the mComputed(Min/Max)Height constraints to the content
   * size computed so far.
   *
   * @param aHeight The height that we've computed an to which we want to apply
   *        min/max constraints.
   * @param aConsumed The amount of the computed height that was consumed by
   *        our prev-in-flows.
   */
  nscoord ApplyMinMaxHeight(nscoord aHeight, nscoord aConsumed = 0) const {
    aHeight += aConsumed;

    if (NS_UNCONSTRAINEDSIZE != ComputedMaxHeight()) {
      aHeight = std::min(aHeight, ComputedMaxHeight());
    }

    if (NS_UNCONSTRAINEDSIZE != ComputedMinHeight()) {
      aHeight = std::max(aHeight, ComputedMinHeight());
    }

    return aHeight - aConsumed;
  }

  /**
   * Apply the mComputed(Min/Max)BSize constraints to the content
   * size computed so far.
   *
   * @param aBSize The block-size that we've computed an to which we want to
   *        apply min/max constraints.
   * @param aConsumed The amount of the computed block-size that was consumed by
   *        our prev-in-flows.
   */
  nscoord ApplyMinMaxBSize(nscoord aBSize, nscoord aConsumed = 0) const {
    aBSize += aConsumed;

    if (NS_UNCONSTRAINEDSIZE != ComputedMaxBSize()) {
      aBSize = std::min(aBSize, ComputedMaxBSize());
    }

    if (NS_UNCONSTRAINEDSIZE != ComputedMinBSize()) {
      aBSize = std::max(aBSize, ComputedMinBSize());
    }

    return aBSize - aConsumed;
  }

  bool ShouldReflowAllKids() const;

  // This method doesn't apply min/max computed widths to the value passed in.
  void SetComputedWidth(nscoord aComputedWidth) {
    if (mWritingMode.IsVertical()) {
      SetComputedBSize(aComputedWidth);
    } else {
      SetComputedISize(aComputedWidth);
    }
  }

  // This method doesn't apply min/max computed heights to the value passed in.
  void SetComputedHeight(nscoord aComputedHeight) {
    if (mWritingMode.IsVertical()) {
      SetComputedISize(aComputedHeight);
    } else {
      SetComputedBSize(aComputedHeight);
    }
  }

  // Use "No" to request SetComputedISize/SetComputedBSize not to reset resize
  // flags.
  enum class ResetResizeFlags : bool { No, Yes };

  // This method doesn't apply min/max computed inline-sizes to the value passed
  // in.
  void SetComputedISize(nscoord aComputedISize,
                        ResetResizeFlags aFlags = ResetResizeFlags::Yes);

  // These methods don't apply min/max computed block-sizes to the value passed
  // in.
  void SetComputedBSize(nscoord aComputedBSize,
                        ResetResizeFlags aFlags = ResetResizeFlags::Yes);

  bool WillReflowAgainForClearance() const {
    return mDiscoveredClearance && *mDiscoveredClearance;
  }

  // Returns true if we should apply automatic minimum on the block axis.
  //
  // The automatic minimum size in the ratio-dependent axis of a box with a
  // preferred aspect ratio that is neither a replaced element nor a scroll
  // container is its min-content size clamped from above by its maximum size.
  //
  // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
  bool ShouldApplyAutomaticMinimumOnBlockAxis() const;

  // Returns true if mFrame has a constrained available block-size, or if mFrame
  // is a continuation. When this method returns true, mFrame can be considered
  // to be in a "fragmented context."
  //
  // Note: this method usually returns true when mFrame is in a paged
  // environment (e.g. printing) or has a multi-column container ancestor.
  // However, this doesn't include several cases when we're intentionally
  // performing layout in a fragmentation-ignoring way, e.g. 1) mFrame is a flex
  // or grid item, and this ReflowInput is for a measuring reflow with an
  // unconstrained available block-size, or 2) mFrame is (or is inside of) an
  // element that forms an orthogonal writing-mode.
  bool IsInFragmentedContext() const;

  // Compute the offsets for a relative position element
  //
  // @param aWM the writing mode of aCBSize and the returned offsets.
  static mozilla::LogicalMargin ComputeRelativeOffsets(
      mozilla::WritingMode aWM, nsIFrame* aFrame,
      const mozilla::LogicalSize& aCBSize);

  // If aFrame is a relatively or sticky positioned element, adjust aPosition
  // appropriately.
  //
  // @param aComputedOffsets aFrame's relative offset, either from the cached
  //        nsIFrame::ComputedOffsetProperty() or ComputedPhysicalOffsets().
  //        Note: This parameter is used only when aFrame is relatively
  //        positioned, not sticky positioned.
  // @param aPosition [in/out] Pass aFrame's normal position (pre-relative
  //        positioning), and this method will update it to indicate aFrame's
  //        actual position.
  static void ApplyRelativePositioning(nsIFrame* aFrame,
                                       const nsMargin& aComputedOffsets,
                                       nsPoint* aPosition);

  static void ApplyRelativePositioning(
      nsIFrame* aFrame, mozilla::WritingMode aWritingMode,
      const mozilla::LogicalMargin& aComputedOffsets,
      mozilla::LogicalPoint* aPosition, const nsSize& aContainerSize);

  // Resolve any block-axis 'auto' margins (if any) for an absolutely positioned
  // frame. aMargin and aOffsets are both outparams (though we only touch
  // aOffsets if the position is overconstrained)
  static void ComputeAbsPosBlockAutoMargin(nscoord aAvailMarginSpace,
                                           WritingMode aContainingBlockWM,
                                           bool aIsMarginBStartAuto,
                                           bool aIsMarginBEndAuto,
                                           LogicalMargin& aMargin,
                                           LogicalMargin& aOffsets);

  // Resolve any inline-axis 'auto' margins (if any) for an absolutely
  // positioned frame. aMargin and aOffsets are both outparams (though we only
  // touch aOffsets if the position is overconstrained)
  static void ComputeAbsPosInlineAutoMargin(nscoord aAvailMarginSpace,
                                            WritingMode aContainingBlockWM,
                                            bool aIsMarginIStartAuto,
                                            bool aIsMarginIEndAuto,
                                            LogicalMargin& aMargin,
                                            LogicalMargin& aOffsets);

#ifdef DEBUG
  // Reflow trace methods.  Defined in nsFrame.cpp so they have access
  // to the display-reflow infrastructure.
  static void* DisplayInitConstraintsEnter(nsIFrame* aFrame,
                                           ReflowInput* aState,
                                           nscoord aCBISize, nscoord aCBBSize,
                                           const nsMargin* aBorder,
                                           const nsMargin* aPadding);
  static void DisplayInitConstraintsExit(nsIFrame* aFrame, ReflowInput* aState,
                                         void* aValue);
  static void* DisplayInitFrameTypeEnter(nsIFrame* aFrame, ReflowInput* aState);
  static void DisplayInitFrameTypeExit(nsIFrame* aFrame, ReflowInput* aState,
                                       void* aValue);
#endif

 protected:
  void InitCBReflowInput();
  void InitResizeFlags(nsPresContext* aPresContext,
                       mozilla::LayoutFrameType aFrameType);
  void InitDynamicReflowRoot();

  void InitConstraints(
      nsPresContext* aPresContext,
      const mozilla::Maybe<mozilla::LogicalSize>& aContainingBlockSize,
      const mozilla::Maybe<mozilla::LogicalMargin>& aBorder,
      const mozilla::Maybe<mozilla::LogicalMargin>& aPadding,
      mozilla::LayoutFrameType aFrameType);

  // Returns the nearest containing block or block frame (whether or not
  // it is a containing block) for the specified frame.  Also returns
  // the inline-start edge and logical size of the containing block's
  // content area.
  // These are returned in the coordinate space of the containing block.
  nsIFrame* GetHypotheticalBoxContainer(nsIFrame* aFrame,
                                        nscoord& aCBIStartEdge,
                                        mozilla::LogicalSize& aCBSize) const;

  // Calculate a "hypothetical box" position where the placeholder frame
  // (for a position:fixed/absolute element) would have been placed if it were
  // positioned statically. The hypothetical box position will have a writing
  // mode with the same block direction as the absolute containing block
  // (aCBReflowInput->frame), though it may differ in inline direction.
  void CalculateHypotheticalPosition(nsPresContext* aPresContext,
                                     nsPlaceholderFrame* aPlaceholderFrame,
                                     const ReflowInput* aCBReflowInput,
                                     nsHypotheticalPosition& aHypotheticalPos,
                                     mozilla::LayoutFrameType aFrameType) const;

  // Check if we can use the resolved auto block size (by insets) to compute
  // the inline size through aspect-ratio on absolute-positioned elements.
  // This is only needed for non-replaced elements.
  // https://drafts.csswg.org/css-position/#abspos-auto-size
  bool IsInlineSizeComputableByBlockSizeAndAspectRatio(
      nscoord aBlockSize) const;

  // This calculates the size by using the resolved auto block size (from
  // non-auto block insets), according to the writing mode of current block.
  LogicalSize CalculateAbsoluteSizeWithResolvedAutoBlockSize(
      nscoord aAutoBSize, const LogicalSize& aTentativeComputedSize);

  void InitAbsoluteConstraints(nsPresContext* aPresContext,
                               const ReflowInput* aCBReflowInput,
                               const mozilla::LogicalSize& aContainingBlockSize,
                               mozilla::LayoutFrameType aFrameType);

  // Calculates the computed values for the 'min-inline-size',
  // 'max-inline-size', 'min-block-size', and 'max-block-size' properties, and
  // stores them in the assorted data members
  void ComputeMinMaxValues(const mozilla::LogicalSize& aCBSize);

  // aInsideBoxSizing returns the part of the padding, border, and margin
  // in the aAxis dimension that goes inside the edge given by box-sizing;
  // aOutsideBoxSizing returns the rest.
  void CalculateBorderPaddingMargin(mozilla::LogicalAxis aAxis,
                                    nscoord aContainingBlockSize,
                                    nscoord* aInsideBoxSizing,
                                    nscoord* aOutsideBoxSizing) const;

  void CalculateBlockSideMargins();

  /**
   * @return true if mFrame is an internal table frame, i.e. an
   * ns[RowGroup|ColGroup|Row|Cell]Frame.  (We exclude nsTableColFrame
   * here since we never setup a ReflowInput for those.)
   */
  bool IsInternalTableFrame() const;

 private:
  // The available size in which to reflow the frame. The space represents the
  // amount of room for the frame's margin, border, padding, and content area.
  //
  // The available inline-size should be constrained. The frame's inline-size
  // you choose should fit within it.

  // In galley mode, the available block-size is always unconstrained, and only
  // page mode or multi-column layout involves a constrained available
  // block-size.
  //
  // An unconstrained available block-size means you can choose whatever size
  // you want. If the value is constrained, the frame's block-start border,
  // padding, and content, must fit. If a frame is fully-complete after reflow,
  // then its block-end border, padding, and margin (and similar for its
  // fully-complete ancestors) will need to fit within this available
  // block-size. However, if a frame is monolithic, it may choose a block-size
  // larger than the available block-size.
  mozilla::LogicalSize mAvailableSize{mWritingMode};

  // The computed size specifies the frame's content area, and it does not apply
  // to inline non-replaced elements.
  //
  // For block-level frames, the computed inline-size is based on the
  // inline-size of the containing block, the margin/border/padding areas, and
  // the min/max inline-size.
  //
  // For non-replaced block-level frames in the flow and floated, if the
  // computed block-size is NS_UNCONSTRAINEDSIZE, you should choose a block-size
  // to shrink wrap around the normal flow child frames. The block-size must be
  // within the limit of the min/max block-size if there is such a limit.
  mozilla::LogicalSize mComputedSize{mWritingMode};

  // Computed values for 'inset' properties. Only applies to 'positioned'
  // elements.
  mozilla::LogicalMargin mComputedOffsets{mWritingMode};

  // Computed value for 'min-inline-size'/'min-block-size'.
  mozilla::LogicalSize mComputedMinSize{mWritingMode};

  // Computed value for 'max-inline-size'/'max-block-size'.
  mozilla::LogicalSize mComputedMaxSize{mWritingMode, NS_UNCONSTRAINEDSIZE,
                                        NS_UNCONSTRAINEDSIZE};

  // Cache the used line-height property.
  mutable nscoord mLineHeight = NS_UNCONSTRAINEDSIZE;
};

}  // namespace mozilla

#endif  // mozilla_ReflowInput_h
