tor-browser

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

ReflowInput.cpp (130145B)


      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 /* struct containing the input to nsIFrame::Reflow */
      8 
      9 #include "mozilla/ReflowInput.h"
     10 
     11 #include <algorithm>
     12 
     13 #include "AnchorPositioningUtils.h"
     14 #include "CounterStyleManager.h"
     15 #include "LayoutLogging.h"
     16 #include "PresShell.h"
     17 #include "StickyScrollContainer.h"
     18 #include "mozilla/ScrollContainerFrame.h"
     19 #include "mozilla/WritingModes.h"
     20 #include "mozilla/dom/HTMLInputElement.h"
     21 #include "nsBlockFrame.h"
     22 #include "nsFlexContainerFrame.h"
     23 #include "nsFontInflationData.h"
     24 #include "nsFontMetrics.h"
     25 #include "nsGkAtoms.h"
     26 #include "nsGridContainerFrame.h"
     27 #include "nsIContent.h"
     28 #include "nsIFrame.h"
     29 #include "nsIFrameInlines.h"
     30 #include "nsIPercentBSizeObserver.h"
     31 #include "nsImageFrame.h"
     32 #include "nsLayoutUtils.h"
     33 #include "nsLineBox.h"
     34 #include "nsPresContext.h"
     35 #include "nsStyleConsts.h"
     36 #include "nsTableFrame.h"
     37 
     38 using namespace mozilla;
     39 using namespace mozilla::css;
     40 using namespace mozilla::dom;
     41 using namespace mozilla::layout;
     42 
     43 AnchorPosResolutionParams AnchorPosResolutionParams::From(
     44    const mozilla::SizeComputationInput* aSizingInput,
     45    bool aIgnorePositionArea) {
     46  auto override = AutoResolutionOverrideParams{
     47      aSizingInput->mFrame, aSizingInput->mAnchorPosResolutionCache};
     48  if (aIgnorePositionArea) {
     49    override.mPositionAreaInUse = false;
     50  }
     51  return {aSizingInput->mFrame, aSizingInput->mFrame->StyleDisplay()->mPosition,
     52          aSizingInput->mAnchorPosResolutionCache, override};
     53 }
     54 
     55 static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent) {
     56  nsIFrame* frameNext = aFrame->GetNextInFlow();
     57  nsIFrame* parentNext = aParent->GetNextInFlow();
     58  return frameNext && parentNext && frameNext->GetParent() == parentNext;
     59 }
     60 
     61 /**
     62 * Adjusts the margin for a list (ol, ul), if necessary, depending on
     63 * font inflation settings. Unfortunately, because bullets from a list are
     64 * placed in the margin area, we only have ~40px in which to place the
     65 * bullets. When they are inflated, however, this causes problems, since
     66 * the text takes up more space than is available in the margin.
     67 *
     68 * This method will return a small amount (in app units) by which the
     69 * margin can be adjusted, so that the space is available for list
     70 * bullets to be rendered with font inflation enabled.
     71 */
     72 static nscoord FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame) {
     73  // As an optimization we check this block frame specific bit up front before
     74  // we even check if the frame is a block frame. That's only valid so long as
     75  // we also have the `IsBlockFrameOrSubclass()` call below. Calling that is
     76  // expensive though, and we want to avoid it if we know `HasMarker()` would
     77  // return false.
     78  if (!aFrame->HasAnyStateBits(NS_BLOCK_HAS_MARKER)) {
     79    return 0;
     80  }
     81 
     82  // On desktop font inflation is disabled, so this will always early exit
     83  // quickly, but checking the frame state bit is still quicker then this call
     84  // and very likely to early exit on its own so we check this second.
     85  float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame);
     86  if (inflation <= 1.0f) {
     87    return 0;
     88  }
     89 
     90  if (!aFrame->IsBlockFrameOrSubclass()) {
     91    return 0;
     92  }
     93 
     94  // We only want to adjust the margins if we're dealing with an ordered list.
     95  // We already checked this above.
     96  MOZ_ASSERT(static_cast<const nsBlockFrame*>(aFrame)->HasMarker());
     97 
     98  const auto* list = aFrame->StyleList();
     99  if (list->mListStyleType.IsNone()) {
    100    return 0;
    101  }
    102 
    103  // The HTML spec states that the default padding for ordered lists
    104  // begins at 40px, indicating that we have 40px of space to place a
    105  // bullet. When performing font inflation calculations, we add space
    106  // equivalent to this, but simply inflated at the same amount as the
    107  // text, in app units.
    108  auto margin = nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1);
    109  if (!list->mListStyleType.IsName()) {
    110    return margin;
    111  }
    112 
    113  nsAtom* type = list->mListStyleType.AsName().AsAtom();
    114  if (type != nsGkAtoms::disc && type != nsGkAtoms::circle &&
    115      type != nsGkAtoms::square && type != nsGkAtoms::disclosure_closed &&
    116      type != nsGkAtoms::disclosure_open) {
    117    return margin;
    118  }
    119 
    120  return 0;
    121 }
    122 
    123 SizeComputationInput::SizeComputationInput(
    124    nsIFrame* aFrame, gfxContext* aRenderingContext,
    125    AnchorPosResolutionCache* aAnchorPosResolutionCache)
    126    : mFrame(aFrame),
    127      mRenderingContext(aRenderingContext),
    128      mAnchorPosResolutionCache(aAnchorPosResolutionCache),
    129      mWritingMode(aFrame->GetWritingMode()),
    130      mIsThemed(aFrame->IsThemed()),
    131      mComputedMargin(mWritingMode),
    132      mComputedBorderPadding(mWritingMode),
    133      mComputedPadding(mWritingMode) {
    134  MOZ_ASSERT(mFrame);
    135 }
    136 
    137 SizeComputationInput::SizeComputationInput(
    138    nsIFrame* aFrame, gfxContext* aRenderingContext,
    139    WritingMode aContainingBlockWritingMode, nscoord aContainingBlockISize,
    140    const Maybe<LogicalMargin>& aBorder, const Maybe<LogicalMargin>& aPadding)
    141    : SizeComputationInput(aFrame, aRenderingContext) {
    142  MOZ_ASSERT(!mFrame->IsTableColFrame());
    143  InitOffsets(aContainingBlockWritingMode, aContainingBlockISize,
    144              mFrame->Type(), {}, aBorder, aPadding);
    145 }
    146 
    147 // Initialize a <b>root</b> reflow input with a rendering context to
    148 // use for measuring things.
    149 ReflowInput::ReflowInput(nsPresContext* aPresContext, nsIFrame* aFrame,
    150                         gfxContext* aRenderingContext,
    151                         const LogicalSize& aAvailableSpace, InitFlags aFlags)
    152    : SizeComputationInput(aFrame, aRenderingContext),
    153      mAvailableSize(aAvailableSpace) {
    154  MOZ_ASSERT(aRenderingContext, "no rendering context");
    155  MOZ_ASSERT(aPresContext, "no pres context");
    156  MOZ_ASSERT(aFrame, "no frame");
    157  MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context");
    158 
    159  if (aFlags.contains(InitFlag::DummyParentReflowInput)) {
    160    mFlags.mDummyParentReflowInput = true;
    161  }
    162  if (aFlags.contains(InitFlag::StaticPosIsCBOrigin)) {
    163    mFlags.mStaticPosIsCBOrigin = true;
    164  }
    165 
    166  if (!aFlags.contains(InitFlag::CallerWillInit)) {
    167    Init(aPresContext);
    168  }
    169  // When we encounter a PageContent frame this will be set to true.
    170  mFlags.mCanHaveClassABreakpoints = false;
    171 }
    172 
    173 static nsSize GetICBSize(const nsPresContext* aPresContext,
    174                         const nsIFrame* aFrame) {
    175  if (!aPresContext->IsPaginated()) {
    176    return aPresContext->GetVisibleArea().Size();
    177  }
    178  for (const nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
    179    if (f->IsPageContentFrame()) {
    180      return f->GetSize();
    181    }
    182  }
    183  return aPresContext->GetPageSize();
    184 }
    185 
    186 // Initialize a reflow input for a child frame's reflow. Some state
    187 // is copied from the parent reflow input; the remaining state is
    188 // computed.
    189 ReflowInput::ReflowInput(nsPresContext* aPresContext,
    190                         const ReflowInput& aParentReflowInput,
    191                         nsIFrame* aFrame, const LogicalSize& aAvailableSpace,
    192                         const Maybe<LogicalSize>& aContainingBlockSize,
    193                         InitFlags aFlags,
    194                         const StyleSizeOverrides& aSizeOverrides,
    195                         ComputeSizeFlags aComputeSizeFlags,
    196                         AnchorPosResolutionCache* aAnchorPosResolutionCache)
    197    : SizeComputationInput(aFrame, aParentReflowInput.mRenderingContext,
    198                           aAnchorPosResolutionCache),
    199      mParentReflowInput(&aParentReflowInput),
    200      mFloatManager(aParentReflowInput.mFloatManager),
    201      mLineLayout(mFrame->IsLineParticipant() ? aParentReflowInput.mLineLayout
    202                                              : nullptr),
    203      mBreakType(aParentReflowInput.mBreakType),
    204      mPercentBSizeObserver(
    205          (aParentReflowInput.mPercentBSizeObserver &&
    206           aParentReflowInput.mPercentBSizeObserver->NeedsToObserve(*this))
    207              ? aParentReflowInput.mPercentBSizeObserver
    208              : nullptr),
    209      mFlags(aParentReflowInput.mFlags),
    210      mStyleSizeOverrides(aSizeOverrides),
    211      mComputeSizeFlags(aComputeSizeFlags),
    212      mReflowDepth(aParentReflowInput.mReflowDepth + 1),
    213      mAvailableSize(aAvailableSpace) {
    214  MOZ_ASSERT(aPresContext, "no pres context");
    215  MOZ_ASSERT(aFrame, "no frame");
    216  MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context");
    217  MOZ_ASSERT(!mFlags.mSpecialBSizeReflow || !aFrame->IsSubtreeDirty(),
    218             "frame should be clean when getting special bsize reflow");
    219 
    220  if (mWritingMode.IsOrthogonalTo(mParentReflowInput->GetWritingMode())) {
    221    // If the block establishes an orthogonal flow, set up its AvailableISize
    222    // per https://drafts.csswg.org/css-writing-modes/#orthogonal-auto
    223 
    224    auto GetISizeConstraint = [this](const nsIFrame* aFrame,
    225                                     bool* aFixed = nullptr) -> nscoord {
    226      nscoord limit = NS_UNCONSTRAINEDSIZE;
    227      const auto* pos = aFrame->StylePosition();
    228      // Don't add to anchor resolution cache, since this function is called for
    229      // other frames.
    230      const auto anchorResolutionParams =
    231          AnchorPosResolutionParams::From(aFrame);
    232      if (auto size = nsLayoutUtils::GetAbsoluteSize(
    233              *pos->ISize(mWritingMode, anchorResolutionParams))) {
    234        limit = size.value();
    235        if (aFixed) {
    236          *aFixed = true;
    237        }
    238      } else if (auto maxSize = nsLayoutUtils::GetAbsoluteSize(
    239                     *pos->MaxISize(mWritingMode, anchorResolutionParams))) {
    240        limit = maxSize.value();
    241      }
    242      if (limit != NS_UNCONSTRAINEDSIZE) {
    243        if (auto minSize = nsLayoutUtils::GetAbsoluteSize(
    244                *pos->MinISize(mWritingMode, anchorResolutionParams))) {
    245          limit = std::max(limit, minSize.value());
    246        }
    247      }
    248      return limit;
    249    };
    250 
    251    // See if the containing block has a fixed size we should respect:
    252    const nsIFrame* cb = mFrame->GetContainingBlock();
    253    bool isFixed = false;
    254    nscoord cbLimit = aContainingBlockSize
    255                          ? aContainingBlockSize->ISize(mWritingMode)
    256                          : NS_UNCONSTRAINEDSIZE;
    257    if (cbLimit != NS_UNCONSTRAINEDSIZE) {
    258      isFixed = true;
    259    } else {
    260      cbLimit = GetISizeConstraint(cb, &isFixed);
    261    }
    262 
    263    if (isFixed) {
    264      SetAvailableISize(cbLimit);
    265    } else {
    266      // If the CB size wasn't fixed, we consider the nearest scroll container
    267      // and the ICB.
    268 
    269      nscoord scLimit = NS_UNCONSTRAINEDSIZE;
    270      // If the containing block was not a scroll container itself, look up the
    271      // parent chain for a scroller size that we should respect.
    272      // XXX Could maybe use nsLayoutUtils::GetNearestScrollContainerFrame here,
    273      // but unsure if we need the additional complexity it supports?
    274      if (!cb->IsScrollContainerFrame()) {
    275        for (const nsIFrame* p = mFrame->GetParent(); p; p = p->GetParent()) {
    276          if (p->IsScrollContainerFrame()) {
    277            scLimit = GetISizeConstraint(p);
    278            // Only the closest ancestor scroller is relevant, so quit as soon
    279            // as we've found one (whether or not it had fixed sizing).
    280            break;
    281          }
    282        }
    283      }
    284 
    285      LogicalSize icbSize(mWritingMode, GetICBSize(aPresContext, mFrame));
    286      nscoord icbLimit = icbSize.ISize(mWritingMode);
    287 
    288      SetAvailableISize(std::min(icbLimit, std::min(scLimit, cbLimit)));
    289 
    290      // Record that this frame needs to be invalidated on a resize reflow.
    291      mFrame->PresShell()->AddOrthogonalFlow(mFrame);
    292    }
    293  }
    294 
    295  // Note: mFlags was initialized as a copy of aParentReflowInput.mFlags up in
    296  // this constructor's init list, so the only flags that we need to explicitly
    297  // initialize here are those that may need a value other than our parent's.
    298  mFlags.mNextInFlowUntouched =
    299      aParentReflowInput.mFlags.mNextInFlowUntouched &&
    300      CheckNextInFlowParenthood(aFrame, aParentReflowInput.mFrame);
    301  mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false;
    302  mFlags.mIsColumnBalancing = false;
    303  mFlags.mColumnSetWrapperHasNoBSizeLeft = false;
    304  mFlags.mTreatBSizeAsIndefinite = false;
    305  mFlags.mDummyParentReflowInput = false;
    306  mFlags.mStaticPosIsCBOrigin = aFlags.contains(InitFlag::StaticPosIsCBOrigin);
    307  mFlags.mIOffsetsNeedCSSAlign = mFlags.mBOffsetsNeedCSSAlign = false;
    308 
    309  // We don't want the mOrthogonalCellFinalReflow flag to be inherited; it's up
    310  // to the table row frame to set it for its direct children as needed.
    311  mFlags.mOrthogonalCellFinalReflow = false;
    312 
    313  // aPresContext->IsPaginated() and the named pages pref should have been
    314  // checked when constructing the root ReflowInput.
    315  if (aParentReflowInput.mFlags.mCanHaveClassABreakpoints) {
    316    MOZ_ASSERT(aPresContext->IsPaginated(),
    317               "mCanHaveClassABreakpoints set during non-paginated reflow.");
    318  }
    319 
    320  {
    321    switch (mFrame->Type()) {
    322      case LayoutFrameType::PageContent:
    323        // PageContent requires paginated reflow.
    324        MOZ_ASSERT(aPresContext->IsPaginated(),
    325                   "nsPageContentFrame should not be in non-paginated reflow");
    326        MOZ_ASSERT(!mFlags.mCanHaveClassABreakpoints,
    327                   "mFlags.mCanHaveClassABreakpoints should have been "
    328                   "initalized to false before we found nsPageContentFrame");
    329        mFlags.mCanHaveClassABreakpoints = true;
    330        break;
    331      case LayoutFrameType::Block:          // FALLTHROUGH
    332      case LayoutFrameType::Canvas:         // FALLTHROUGH
    333      case LayoutFrameType::FlexContainer:  // FALLTHROUGH
    334      case LayoutFrameType::GridContainer:
    335        if (mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
    336          // Never allow breakpoints inside of out-of-flow frames.
    337          mFlags.mCanHaveClassABreakpoints = false;
    338          break;
    339        }
    340        // This frame type can have class A breakpoints, inherit this flag
    341        // from the parent (this is done for all flags during construction).
    342        // This also includes Canvas frames, as each PageContent frame always
    343        // has exactly one child which is a Canvas frame.
    344        // Do NOT include the subclasses of BlockFrame here, as the ones for
    345        // which this could be applicable (ColumnSetWrapper and the MathML
    346        // frames) cannot have class A breakpoints.
    347        MOZ_ASSERT(mFlags.mCanHaveClassABreakpoints ==
    348                   aParentReflowInput.mFlags.mCanHaveClassABreakpoints);
    349        break;
    350      default:
    351        mFlags.mCanHaveClassABreakpoints = false;
    352        break;
    353    }
    354  }
    355 
    356  if (aFlags.contains(InitFlag::DummyParentReflowInput) ||
    357      (mParentReflowInput->mFlags.mDummyParentReflowInput &&
    358       mFrame->IsTableFrame())) {
    359    mFlags.mDummyParentReflowInput = true;
    360  }
    361 
    362  if (!aFlags.contains(InitFlag::CallerWillInit)) {
    363    Init(aPresContext, aContainingBlockSize);
    364  }
    365 }
    366 
    367 template <typename SizeOrMaxSize>
    368 nscoord SizeComputationInput::ComputeISizeValue(
    369    const LogicalSize& aContainingBlockSize, StyleBoxSizing aBoxSizing,
    370    const SizeOrMaxSize& aSize) const {
    371  WritingMode wm = GetWritingMode();
    372  const auto borderPadding = ComputedLogicalBorderPadding(wm);
    373  const auto margin = ComputedLogicalMargin(wm);
    374  const LogicalSize contentEdgeToBoxSizing =
    375      aBoxSizing == StyleBoxSizing::Border ? borderPadding.Size(wm)
    376                                           : LogicalSize(wm);
    377  const nscoord boxSizingToMarginEdgeISize = borderPadding.IStartEnd(wm) +
    378                                             margin.IStartEnd(wm) -
    379                                             contentEdgeToBoxSizing.ISize(wm);
    380 
    381  // Get the bSize with anchor functions resolved, and manually resolve
    382  // 'stretch'-like sizes as well (we should do this a bit cleaner,
    383  // see bug 2000035):
    384  auto bSize = mFrame->StylePosition()->BSize(
    385      wm, AnchorPosResolutionParams::From(mFrame, mAnchorPosResolutionCache));
    386  if (bSize->BehavesLikeStretchOnBlockAxis()) {
    387    if (NS_UNCONSTRAINEDSIZE == aContainingBlockSize.BSize(wm)) {
    388      bSize = AnchorResolvedSizeHelper::Auto();
    389    } else {
    390      nscoord stretchBSize = nsLayoutUtils::ComputeStretchBSize(
    391          aContainingBlockSize.BSize(wm), margin.BStartEnd(wm),
    392          borderPadding.BStartEnd(wm), aBoxSizing);
    393      bSize = AnchorResolvedSizeHelper::LengthPercentage(
    394          StyleLengthPercentage::FromAppUnits(stretchBSize));
    395    }
    396  }
    397 
    398  return mFrame
    399      ->ComputeISizeValue(mRenderingContext, wm, aContainingBlockSize,
    400                          contentEdgeToBoxSizing, boxSizingToMarginEdgeISize,
    401                          aSize, *bSize, mFrame->GetAspectRatio())
    402      .mISize;
    403 }
    404 
    405 template <typename SizeOrMaxSize>
    406 nscoord SizeComputationInput::ComputeBSizeValueHandlingStretch(
    407    nscoord aContainingBlockBSize, StyleBoxSizing aBoxSizing,
    408    const SizeOrMaxSize& aSize) const {
    409  if (aSize.BehavesLikeStretchOnBlockAxis()) {
    410    WritingMode wm = GetWritingMode();
    411    return nsLayoutUtils::ComputeStretchContentBoxBSize(
    412        aContainingBlockBSize, ComputedLogicalMargin(wm).Size(wm).BSize(wm),
    413        ComputedLogicalBorderPadding(wm).Size(wm).BSize(wm));
    414  }
    415  return ComputeBSizeValue(aContainingBlockBSize, aBoxSizing,
    416                           aSize.AsLengthPercentage());
    417 }
    418 
    419 nscoord SizeComputationInput::ComputeBSizeValue(
    420    nscoord aContainingBlockBSize, StyleBoxSizing aBoxSizing,
    421    const LengthPercentage& aSize) const {
    422  WritingMode wm = GetWritingMode();
    423  nscoord inside = 0;
    424  if (aBoxSizing == StyleBoxSizing::Border) {
    425    inside = ComputedLogicalBorderPadding(wm).BStartEnd(wm);
    426  }
    427  return nsLayoutUtils::ComputeBSizeValue(aContainingBlockBSize, inside, aSize);
    428 }
    429 
    430 WritingMode ReflowInput::GetCBWritingMode() const {
    431  return mCBReflowInput ? mCBReflowInput->GetWritingMode()
    432                        : mFrame->GetContainingBlock()->GetWritingMode();
    433 }
    434 
    435 // Return the physical border-box size by combining aContentBoxSize and
    436 // aBorderPadding, with unconstrained dimensions replaced by zero.
    437 static nsSize BorderBoxSizeAsContainerIfConstrained(
    438    WritingMode aWM, const LogicalSize& aContentBoxSize,
    439    const LogicalMargin& aBorderPadding) {
    440  LogicalSize size = aContentBoxSize;
    441  if (size.ISize(aWM) == NS_UNCONSTRAINEDSIZE) {
    442    size.ISize(aWM) = 0;
    443  } else {
    444    size.ISize(aWM) += aBorderPadding.IStartEnd(aWM);
    445  }
    446  if (size.BSize(aWM) == NS_UNCONSTRAINEDSIZE) {
    447    size.BSize(aWM) = 0;
    448  } else {
    449    size.BSize(aWM) += aBorderPadding.BStartEnd(aWM);
    450  }
    451  return size.GetPhysicalSize(aWM);
    452 }
    453 
    454 nsSize ReflowInput::ComputedSizeAsContainerIfConstrained() const {
    455  return BorderBoxSizeAsContainerIfConstrained(mWritingMode, ComputedSize(),
    456                                               mComputedBorderPadding);
    457 }
    458 
    459 bool ReflowInput::ShouldReflowAllKids() const {
    460  // Note that we could make a stronger optimization for IsBResize if
    461  // we use it in a ShouldReflowChild test that replaces the current
    462  // checks of NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN, if it
    463  // were tested there along with NS_FRAME_CONTAINS_RELATIVE_BSIZE.
    464  // This would need to be combined with a slight change in which
    465  // frames NS_FRAME_CONTAINS_RELATIVE_BSIZE is marked on.
    466  return mFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsIResize() ||
    467         (IsBResize() &&
    468          mFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) ||
    469         mFlags.mIsInLastColumnBalancingReflow;
    470 }
    471 
    472 void ReflowInput::SetComputedISize(nscoord aComputedISize,
    473                                   ResetResizeFlags aFlags) {
    474  // It'd be nice to assert that |frame| is not in reflow, but this fails
    475  // because viewport frames reset the computed isize on a copy of their reflow
    476  // input when reflowing fixed-pos kids.  In that case we actually don't want
    477  // to mess with the resize flags, because comparing the frame's rect to the
    478  // munged computed isize is pointless.
    479  NS_WARNING_ASSERTION(aComputedISize >= 0, "Invalid computed inline-size!");
    480  if (ComputedISize() != aComputedISize) {
    481    mComputedSize.ISize(mWritingMode) = std::max(0, aComputedISize);
    482    if (aFlags == ResetResizeFlags::Yes) {
    483      InitResizeFlags(mFrame->PresContext(), mFrame->Type());
    484    }
    485  }
    486 }
    487 
    488 void ReflowInput::SetComputedBSize(nscoord aComputedBSize,
    489                                   ResetResizeFlags aFlags) {
    490  // It'd be nice to assert that |frame| is not in reflow, but this fails
    491  // for the same reason as above.
    492  NS_WARNING_ASSERTION(aComputedBSize >= 0, "Invalid computed block-size!");
    493  if (ComputedBSize() != aComputedBSize) {
    494    mComputedSize.BSize(mWritingMode) = std::max(0, aComputedBSize);
    495    if (aFlags == ResetResizeFlags::Yes) {
    496      InitResizeFlags(mFrame->PresContext(), mFrame->Type());
    497    }
    498  }
    499 }
    500 
    501 void ReflowInput::Init(nsPresContext* aPresContext,
    502                       const Maybe<LogicalSize>& aContainingBlockSize,
    503                       const Maybe<LogicalMargin>& aBorder,
    504                       const Maybe<LogicalMargin>& aPadding) {
    505  LAYOUT_WARN_IF_FALSE(AvailableISize() != NS_UNCONSTRAINEDSIZE,
    506                       "have unconstrained inline-size; this should only "
    507                       "result from very large sizes, not attempts at "
    508                       "intrinsic inline-size calculation");
    509 
    510  mStylePosition = mFrame->StylePosition();
    511  mStyleDisplay = mFrame->StyleDisplay();
    512  mStyleBorder = mFrame->StyleBorder();
    513  mStyleMargin = mFrame->StyleMargin();
    514 
    515  InitCBReflowInput();
    516 
    517  LayoutFrameType type = mFrame->Type();
    518  if (type == LayoutFrameType::Placeholder) {
    519    // Placeholders have a no-op Reflow method that doesn't need the rest of
    520    // this initialization, so we bail out early.
    521    mComputedSize.SizeTo(mWritingMode, 0, 0);
    522    return;
    523  }
    524 
    525  mFlags.mIsReplaced = mFrame->IsReplaced();
    526 
    527  InitConstraints(aPresContext, aContainingBlockSize, aBorder, aPadding, type);
    528 
    529  InitResizeFlags(aPresContext, type);
    530  InitDynamicReflowRoot();
    531 
    532  nsIFrame* parent = mFrame->GetParent();
    533  if (parent && parent->HasAnyStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE) &&
    534      !(parent->IsScrollContainerFrame() &&
    535        parent->StyleDisplay()->mOverflowY != StyleOverflow::Hidden)) {
    536    mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
    537  } else if (type == LayoutFrameType::SVGForeignObject) {
    538    // An SVG foreignObject frame is inherently constrained block-size.
    539    mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
    540  } else {
    541    const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
    542    const auto bSizeCoord =
    543        mStylePosition->BSize(mWritingMode, anchorResolutionParams);
    544    const auto maxBSizeCoord =
    545        mStylePosition->MaxBSize(mWritingMode, anchorResolutionParams);
    546    if ((!bSizeCoord->BehavesLikeInitialValueOnBlockAxis() ||
    547         !maxBSizeCoord->BehavesLikeInitialValueOnBlockAxis()) &&
    548        // Don't set NS_FRAME_IN_CONSTRAINED_BSIZE on body or html elements.
    549        (mFrame->GetContent() && !(mFrame->GetContent()->IsAnyOfHTMLElements(
    550                                     nsGkAtoms::body, nsGkAtoms::html)))) {
    551      // If our block-size was specified as a percentage, then this could
    552      // actually resolve to 'auto', based on:
    553      // http://www.w3.org/TR/CSS21/visudet.html#the-height-property
    554      nsIFrame* containingBlk = mFrame;
    555      while (containingBlk) {
    556        const nsStylePosition* stylePos = containingBlk->StylePosition();
    557        // It's for containing block, so don't add to anchor resolution cache
    558        const auto containingBlkAnchorResolutionParams =
    559            AnchorPosResolutionParams::From(containingBlk);
    560        const auto bSizeCoord =
    561            stylePos->BSize(mWritingMode, containingBlkAnchorResolutionParams);
    562        const auto& maxBSizeCoord = stylePos->MaxBSize(
    563            mWritingMode, containingBlkAnchorResolutionParams);
    564        if ((bSizeCoord->IsLengthPercentage() && !bSizeCoord->HasPercent()) ||
    565            (maxBSizeCoord->IsLengthPercentage() &&
    566             !maxBSizeCoord->HasPercent())) {
    567          mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
    568          break;
    569        } else if (bSizeCoord->HasPercent() || maxBSizeCoord->HasPercent()) {
    570          if (!(containingBlk = containingBlk->GetContainingBlock())) {
    571            // If we've reached the top of the tree, then we don't have
    572            // a constrained block-size.
    573            mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
    574            break;
    575          }
    576 
    577          continue;
    578        } else {
    579          mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
    580          break;
    581        }
    582      }
    583    } else {
    584      mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
    585    }
    586  }
    587 
    588  if (mParentReflowInput &&
    589      mParentReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) {
    590    // Orthogonal frames are always reflowed with an unconstrained
    591    // dimension to avoid incomplete reflow across an orthogonal
    592    // boundary. Normally this is the block-size, but for column sets
    593    // with auto-height it's the inline-size, so that they can add
    594    // columns in the container's block direction
    595    if (type == LayoutFrameType::ColumnSet &&
    596        mStylePosition
    597            ->ISize(mWritingMode, AnchorPosResolutionParams::From(this))
    598            ->IsAuto()) {
    599      SetComputedISize(NS_UNCONSTRAINEDSIZE, ResetResizeFlags::No);
    600    } else {
    601      SetAvailableBSize(NS_UNCONSTRAINEDSIZE);
    602    }
    603  }
    604 
    605  if (mFrame->GetContainSizeAxes().mBContained) {
    606    // In the case that a box is size contained in block axis, we want to ensure
    607    // that it is also monolithic. We do this by setting AvailableBSize() to an
    608    // unconstrained size to avoid fragmentation.
    609    SetAvailableBSize(NS_UNCONSTRAINEDSIZE);
    610  }
    611 
    612  LAYOUT_WARN_IF_FALSE(
    613      (mStyleDisplay->IsInlineOutsideStyle() && !mFrame->IsReplaced()) ||
    614          type == LayoutFrameType::Text ||
    615          ComputedISize() != NS_UNCONSTRAINEDSIZE,
    616      "have unconstrained inline-size; this should only "
    617      "result from very large sizes, not attempts at "
    618      "intrinsic inline-size calculation");
    619 }
    620 
    621 static bool MightBeContainingBlockFor(nsIFrame* aMaybeContainingBlock,
    622                                      nsIFrame* aFrame,
    623                                      const nsStyleDisplay* aStyleDisplay) {
    624  // Keep this in sync with nsIFrame::GetContainingBlock.
    625  if (aFrame->IsAbsolutelyPositioned(aStyleDisplay) &&
    626      aMaybeContainingBlock == aFrame->GetParent()) {
    627    return true;
    628  }
    629  return aMaybeContainingBlock->IsBlockContainer();
    630 }
    631 
    632 void ReflowInput::InitCBReflowInput() {
    633  mCBReflowInput = mParentReflowInput;
    634  if (!mCBReflowInput || mParentReflowInput->mFlags.mDummyParentReflowInput) {
    635    return;
    636  }
    637  // To avoid a long walk up the frame tree check if the parent frame can be a
    638  // containing block for mFrame.
    639  if (MightBeContainingBlockFor(mCBReflowInput->mFrame, mFrame,
    640                                mStyleDisplay) &&
    641      mCBReflowInput->mFrame == mFrame->GetContainingBlock(0, mStyleDisplay)) {
    642    // Inner table frames need to use the containing block of the outer
    643    // table frame.
    644    if (mFrame->IsTableFrame()) {
    645      MOZ_ASSERT(mParentReflowInput->mCBReflowInput,
    646                 "Inner table frames shouldn't be reflow roots");
    647      mCBReflowInput = mParentReflowInput->mCBReflowInput;
    648    }
    649  } else if (mParentReflowInput->mCBReflowInput) {
    650    mCBReflowInput = mParentReflowInput->mCBReflowInput;
    651  }
    652 }
    653 
    654 /* Check whether CalcQuirkContainingBlockHeight would stop on the
    655 * given reflow input, using its block as a height.  (essentially
    656 * returns false for any case in which CalcQuirkContainingBlockHeight
    657 * has a "continue" in its main loop.)
    658 *
    659 * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses
    660 * this function as well
    661 */
    662 static bool IsQuirkContainingBlockHeight(const ReflowInput* rs,
    663                                         LayoutFrameType aFrameType) {
    664  if (LayoutFrameType::Block == aFrameType ||
    665      LayoutFrameType::ScrollContainer == aFrameType) {
    666    // Note: This next condition could change due to a style change,
    667    // but that would cause a style reflow anyway, which means we're ok.
    668    if (NS_UNCONSTRAINEDSIZE == rs->ComputedHeight()) {
    669      if (!rs->mFrame->IsAbsolutelyPositioned(rs->mStyleDisplay)) {
    670        return false;
    671      }
    672    }
    673  }
    674  return true;
    675 }
    676 
    677 void ReflowInput::InitResizeFlags(nsPresContext* aPresContext,
    678                                  LayoutFrameType aFrameType) {
    679  SetIResize(false);
    680  SetBResize(false);
    681  SetBResizeForPercentages(false);
    682 
    683  const WritingMode wm = mWritingMode;  // just a shorthand
    684  // We should report that we have a resize in the inline dimension if
    685  // *either* the border-box size or the content-box size in that
    686  // dimension has changed.  It might not actually be necessary to do
    687  // this if the border-box size has changed and the content-box size
    688  // has not changed, but since we've historically used the flag to mean
    689  // border-box size change, continue to do that. It's possible for
    690  // the content-box size to change without a border-box size change or
    691  // a style change given (1) a fixed width (possibly fixed by max-width
    692  // or min-width), box-sizing:border-box, and percentage padding;
    693  // (2) box-sizing:content-box, M% width, and calc(Npx - M%) padding.
    694  //
    695  // However, we don't actually have the information at this point to tell
    696  // whether the content-box size has changed, since both style data and the
    697  // UsedPaddingProperty() have already been updated in
    698  // SizeComputationInput::InitOffsets(). So, we check the HasPaddingChange()
    699  // bit for the cases where it's possible for the content-box size to have
    700  // changed without either (a) a change in the border-box size or (b) an
    701  // nsChangeHint_NeedDirtyReflow change hint due to change in border or
    702  // padding.
    703  //
    704  // We don't clear the HasPaddingChange() bit here, since sometimes we
    705  // construct reflow input (e.g. in nsBlockFrame::ReflowBlockFrame to compute
    706  // margin collapsing) without reflowing the frame. Instead, we clear it in
    707  // nsIFrame::DidReflow().
    708  bool isIResize =
    709      // is the border-box resizing?
    710      mFrame->ISize(wm) !=
    711          ComputedISize() + ComputedLogicalBorderPadding(wm).IStartEnd(wm) ||
    712      // or is the content-box resizing?  (see comment above)
    713      mFrame->HasPaddingChange();
    714 
    715  if (mFrame->HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT) &&
    716      nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
    717    // Create our font inflation data if we don't have it already, and
    718    // give it our current width information.
    719    bool dirty = nsFontInflationData::UpdateFontInflationDataISizeFor(*this) &&
    720                 // Avoid running this at the box-to-block interface
    721                 // (where we shouldn't be inflating anyway, and where
    722                 // reflow input construction is probably to construct a
    723                 // dummy parent reflow input anyway).
    724                 !mFlags.mDummyParentReflowInput;
    725 
    726    if (dirty || (!mFrame->GetParent() && isIResize)) {
    727      // When font size inflation is enabled, a change in either:
    728      //  * the effective width of a font inflation flow root
    729      //  * the width of the frame
    730      // needs to cause a dirty reflow since they change the font size
    731      // inflation calculations, which in turn change the size of text,
    732      // line-heights, etc.  This is relatively similar to a classic
    733      // case of style change reflow, except that because inflation
    734      // doesn't affect the intrinsic sizing codepath, there's no need
    735      // to invalidate intrinsic sizes.
    736      //
    737      // Note that this makes horizontal resizing a good bit more
    738      // expensive.  However, font size inflation is targeted at a set of
    739      // devices (zoom-and-pan devices) where the main use case for
    740      // horizontal resizing needing to be efficient (window resizing) is
    741      // not present.  It does still increase the cost of dynamic changes
    742      // caused by script where a style or content change in one place
    743      // causes a resize in another (e.g., rebalancing a table).
    744 
    745      // FIXME: This isn't so great for the cases where
    746      // ReflowInput::SetComputedWidth is called, if the first time
    747      // we go through InitResizeFlags we set IsHResize() to true, and then
    748      // the second time we'd set it to false even without the
    749      // NS_FRAME_IS_DIRTY bit already set.
    750      if (mFrame->IsSVGForeignObjectFrame()) {
    751        // Foreign object frames use dirty bits in a special way.
    752        mFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
    753        nsIFrame* kid = mFrame->PrincipalChildList().FirstChild();
    754        if (kid) {
    755          kid->MarkSubtreeDirty();
    756        }
    757      } else {
    758        mFrame->MarkSubtreeDirty();
    759      }
    760 
    761      // Mark intrinsic widths on all descendants dirty.  We need to do
    762      // this (1) since we're changing the size of text and need to
    763      // clear text runs on text frames and (2) since we actually are
    764      // changing some intrinsic widths, but only those that live inside
    765      // of containers.
    766 
    767      // It makes sense to do this for descendants but not ancestors
    768      // (which is unusual) because we're only changing the unusual
    769      // inflation-dependent intrinsic widths (i.e., ones computed with
    770      // nsPresContext::mInflationDisabledForShrinkWrap set to false),
    771      // which should never affect anything outside of their inflation
    772      // flow root (or, for that matter, even their inflation
    773      // container).
    774 
    775      // This is also different from what PresShell::FrameNeedsReflow
    776      // does because it doesn't go through placeholders.  It doesn't
    777      // need to because we're actually doing something that cares about
    778      // frame tree geometry (the width on an ancestor) rather than
    779      // style.
    780 
    781      AutoTArray<nsIFrame*, 32> stack;
    782      stack.AppendElement(mFrame);
    783 
    784      do {
    785        nsIFrame* f = stack.PopLastElement();
    786        for (const auto& childList : f->ChildLists()) {
    787          for (nsIFrame* kid : childList.mList) {
    788            kid->MarkIntrinsicISizesDirty();
    789            stack.AppendElement(kid);
    790          }
    791        }
    792      } while (stack.Length() != 0);
    793    }
    794  }
    795 
    796  SetIResize(!mFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY) && isIResize);
    797  const auto anchorResolutionParams =
    798      AnchorPosOffsetResolutionParams::UseCBFrameSize(
    799          AnchorPosResolutionParams::From(this));
    800 
    801  const auto bSize =
    802      mStylePosition->BSize(wm, anchorResolutionParams.mBaseParams);
    803  const auto minBSize =
    804      mStylePosition->MinBSize(wm, anchorResolutionParams.mBaseParams);
    805  const auto maxBSize =
    806      mStylePosition->MaxBSize(wm, anchorResolutionParams.mBaseParams);
    807  // XXX Should we really need to null check mCBReflowInput?  (We do for
    808  // at least nsBoxFrame).
    809  if (mFrame->HasBSizeChange()) {
    810    // When we have an nsChangeHint_UpdateComputedBSize, we'll set a bit
    811    // on the frame to indicate we're resizing.  This might catch cases,
    812    // such as a change between auto and a length, where the box doesn't
    813    // actually resize but children with percentages resize (since those
    814    // percentages become auto if their containing block is auto).
    815    SetBResize(true);
    816    SetBResizeForPercentages(true);
    817    // We don't clear the HasBSizeChange state here, since sometimes we
    818    // construct a ReflowInput (e.g. in nsBlockFrame::ReflowBlockFrame to
    819    // compute margin collapsing) without reflowing the frame. Instead, we
    820    // clear it in nsIFrame::DidReflow.
    821  } else if (mCBReflowInput &&
    822             mCBReflowInput->IsBResizeForPercentagesForWM(wm) &&
    823             (bSize->HasPercent() || minBSize->HasPercent() ||
    824              maxBSize->HasPercent())) {
    825    // We have a percentage (or calc-with-percentage) block-size, and the
    826    // value it's relative to has changed.
    827    SetBResize(true);
    828    SetBResizeForPercentages(true);
    829  } else if (aFrameType == LayoutFrameType::TableCell &&
    830             (mFlags.mSpecialBSizeReflow ||
    831              mFrame->FirstInFlow()->HasAnyStateBits(
    832                  NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) &&
    833             mFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
    834    // Need to set the bit on the cell so that
    835    // mCBReflowInput->IsBResize() is set correctly below when
    836    // reflowing descendant.
    837    SetBResize(true);
    838    SetBResizeForPercentages(true);
    839  } else if (mCBReflowInput && mFrame->IsBlockWrapper()) {
    840    // XXX Is this problematic for relatively positioned inlines acting
    841    // as containing block for absolutely positioned elements?
    842    // Possibly; in that case we should at least be checking
    843    // IsSubtreeDirty(), I'd think.
    844    SetBResize(mCBReflowInput->IsBResizeForWM(wm));
    845    SetBResizeForPercentages(mCBReflowInput->IsBResizeForPercentagesForWM(wm));
    846  } else if (ComputedBSize() == NS_UNCONSTRAINEDSIZE) {
    847    // We have an 'auto' block-size.
    848    if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
    849        mCBReflowInput) {
    850      // FIXME: This should probably also check IsIResize().
    851      SetBResize(mCBReflowInput->IsBResizeForWM(wm));
    852    } else {
    853      SetBResize(IsIResize());
    854    }
    855    SetBResize(IsBResize() || mFrame->IsSubtreeDirty() ||
    856               // For an inner table frame, copy IsBResize from its wrapper.
    857               (aFrameType == LayoutFrameType::Table &&
    858                mParentReflowInput->IsBResize()));
    859  } else {
    860    // We have a non-'auto' block-size, i.e., a length.  Set the BResize
    861    // flag to whether the size is actually different.
    862    SetBResize(mFrame->BSize(wm) !=
    863               ComputedBSize() +
    864                   ComputedLogicalBorderPadding(wm).BStartEnd(wm));
    865  }
    866 
    867  bool dependsOnCBBSize =
    868      (nsStylePosition::BSizeDependsOnContainer(bSize) &&
    869       // FIXME: condition this on not-abspos?
    870       !bSize->IsAuto()) ||
    871      nsStylePosition::MinBSizeDependsOnContainer(minBSize) ||
    872      nsStylePosition::MaxBSizeDependsOnContainer(maxBSize) ||
    873      mStylePosition
    874          ->GetAnchorResolvedInset(LogicalSide::BStart, wm,
    875                                   anchorResolutionParams)
    876          ->HasPercent() ||
    877      !mStylePosition
    878           ->GetAnchorResolvedInset(LogicalSide::BEnd, wm,
    879                                    anchorResolutionParams)
    880           ->IsAuto() ||
    881      // We assume orthogonal flows depend on the containing-block's BSize,
    882      // as that will commonly provide the available inline size. This is not
    883      // always strictly needed, but orthogonal flows are rare enough that
    884      // attempting to be more precise seems overly complex.
    885      (mCBReflowInput && mCBReflowInput->GetWritingMode().IsOrthogonalTo(wm));
    886 
    887  // If mFrame is a flex item, and mFrame's block axis is the flex container's
    888  // main axis (e.g. in a column-oriented flex container with same
    889  // writing-mode), then its block-size depends on its CB size, if its
    890  // flex-basis has a percentage.
    891  if (mFrame->IsFlexItem() &&
    892      !nsFlexContainerFrame::IsItemInlineAxisMainAxis(mFrame)) {
    893    const auto& flexBasis = mStylePosition->mFlexBasis;
    894    dependsOnCBBSize |= (flexBasis.IsSize() && flexBasis.AsSize().HasPercent());
    895  }
    896 
    897  if (mFrame->StyleFont()->mLineHeight.IsMozBlockHeight()) {
    898    // line-height depends on block bsize
    899    mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
    900    // but only on containing blocks if this frame is not a suitable block
    901    dependsOnCBBSize |= !nsLayoutUtils::IsNonWrapperBlock(mFrame);
    902  }
    903 
    904  // If we're the descendant of a table cell that performs special bsize
    905  // reflows and we could be the child that requires them, always set
    906  // the block-axis resize in case this is the first pass before the
    907  // special bsize reflow.  However, don't do this if it actually is
    908  // the special bsize reflow, since in that case it will already be
    909  // set correctly above if we need it set.
    910  if (!IsBResize() && mCBReflowInput &&
    911      (mCBReflowInput->mFrame->IsTableCellFrame() ||
    912       mCBReflowInput->mFlags.mHeightDependsOnAncestorCell) &&
    913      !mCBReflowInput->mFlags.mSpecialBSizeReflow && dependsOnCBBSize) {
    914    SetBResize(true);
    915    mFlags.mHeightDependsOnAncestorCell = true;
    916  }
    917 
    918  // Set NS_FRAME_CONTAINS_RELATIVE_BSIZE if it's needed.
    919 
    920  // It would be nice to check that |ComputedBSize != NS_UNCONSTRAINEDSIZE|
    921  // &&ed with the percentage bsize check.  However, this doesn't get
    922  // along with table special bsize reflows, since a special bsize
    923  // reflow (a quirk that makes such percentage height work on children
    924  // of table cells) can cause not just a single percentage height to
    925  // become fixed, but an entire descendant chain of percentage height
    926  // to become fixed.
    927  if (dependsOnCBBSize && mCBReflowInput) {
    928    const ReflowInput* rs = this;
    929    bool hitCBReflowInput = false;
    930    do {
    931      rs = rs->mParentReflowInput;
    932      if (!rs) {
    933        break;
    934      }
    935 
    936      if (rs->mFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
    937        break;  // no need to go further
    938      }
    939      rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
    940 
    941      // Keep track of whether we've hit the containing block, because
    942      // we need to go at least that far.
    943      if (rs == mCBReflowInput) {
    944        hitCBReflowInput = true;
    945      }
    946 
    947      // XXX What about orthogonal flows? It doesn't make sense to
    948      // keep propagating this bit across an orthogonal boundary,
    949      // where the meaning of BSize changes. Bug 1175517.
    950    } while (!hitCBReflowInput ||
    951             (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
    952              !IsQuirkContainingBlockHeight(rs, rs->mFrame->Type())));
    953    // Note: We actually don't need to set the
    954    // NS_FRAME_CONTAINS_RELATIVE_BSIZE bit for the cases
    955    // where we hit the early break statements in
    956    // CalcQuirkContainingBlockHeight. But it doesn't hurt
    957    // us to set the bit in these cases.
    958  }
    959  if (mFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
    960    // If we're reflowing everything, then we'll find out if we need
    961    // to re-set this.
    962    mFrame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
    963  }
    964 }
    965 
    966 void ReflowInput::InitDynamicReflowRoot() {
    967  if (mFrame->CanBeDynamicReflowRoot()) {
    968    mFrame->AddStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT);
    969  } else {
    970    mFrame->RemoveStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT);
    971  }
    972 }
    973 
    974 bool ReflowInput::ShouldApplyAutomaticMinimumOnBlockAxis() const {
    975  MOZ_ASSERT(!mFrame->HasReplacedSizing());
    976  return mFlags.mIsBSizeSetByAspectRatio &&
    977         !mStyleDisplay->IsScrollableOverflow() &&
    978         mStylePosition
    979             ->MinBSize(GetWritingMode(), AnchorPosResolutionParams::From(this))
    980             ->IsAuto() &&
    981         !mFrame->GetContainSizeAxes().mBContained;
    982 }
    983 
    984 bool ReflowInput::IsInFragmentedContext() const {
    985  // We consider mFrame with a prev-in-flow being in a fragmented context
    986  // because nsColumnSetFrame can reflow its last column with an unconstrained
    987  // available block-size.
    988  return AvailableBSize() != NS_UNCONSTRAINEDSIZE || mFrame->GetPrevInFlow();
    989 }
    990 
    991 /* static */
    992 LogicalMargin ReflowInput::ComputeRelativeOffsets(WritingMode aWM,
    993                                                  nsIFrame* aFrame,
    994                                                  const LogicalSize& aCBSize) {
    995  // In relative positioning, anchor functions are always invalid;
    996  // anchor-resolved insets should no longer contain any reference to anchor
    997  // functions.
    998  LogicalMargin offsets(aWM);
    999  const nsStylePosition* position = aFrame->StylePosition();
   1000  const auto anchorResolutionParams =
   1001      AnchorPosOffsetResolutionParams::UseCBFrameSize(
   1002          AnchorPosResolutionParams::From(aFrame));
   1003 
   1004  // Compute the 'inlineStart' and 'inlineEnd' values. 'inlineStart'
   1005  // moves the boxes to the end of the line, and 'inlineEnd' moves the
   1006  // boxes to the start of the line. The computed values are always:
   1007  // inlineStart=-inlineEnd
   1008  const auto inlineStart = position->GetAnchorResolvedInset(
   1009      LogicalSide::IStart, aWM, anchorResolutionParams);
   1010  const auto inlineEnd = position->GetAnchorResolvedInset(
   1011      LogicalSide::IEnd, aWM, anchorResolutionParams);
   1012  bool inlineStartIsAuto = inlineStart->IsAuto();
   1013  bool inlineEndIsAuto = inlineEnd->IsAuto();
   1014 
   1015  // If neither 'inlineStart' nor 'inlineEnd' is auto, then we're
   1016  // over-constrained and we ignore one of them
   1017  if (!inlineStartIsAuto && !inlineEndIsAuto) {
   1018    inlineEndIsAuto = true;
   1019  }
   1020 
   1021  if (inlineStartIsAuto) {
   1022    if (inlineEndIsAuto) {
   1023      // If both are 'auto' (their initial values), the computed values are 0
   1024      offsets.IStart(aWM) = offsets.IEnd(aWM) = 0;
   1025    } else {
   1026      // 'inlineEnd' isn't being treated as 'auto' so compute its value
   1027      offsets.IEnd(aWM) = inlineEnd->IsAuto()
   1028                              ? 0
   1029                              : nsLayoutUtils::ComputeCBDependentValue(
   1030                                    aCBSize.ISize(aWM), inlineEnd);
   1031 
   1032      // Computed value for 'inlineStart' is minus the value of 'inlineEnd'
   1033      offsets.IStart(aWM) = -offsets.IEnd(aWM);
   1034    }
   1035 
   1036  } else {
   1037    NS_ASSERTION(inlineEndIsAuto, "unexpected specified constraint");
   1038 
   1039    // 'InlineStart' isn't 'auto' so compute its value
   1040    offsets.IStart(aWM) =
   1041        nsLayoutUtils::ComputeCBDependentValue(aCBSize.ISize(aWM), inlineStart);
   1042 
   1043    // Computed value for 'inlineEnd' is minus the value of 'inlineStart'
   1044    offsets.IEnd(aWM) = -offsets.IStart(aWM);
   1045  }
   1046 
   1047  // Compute the 'blockStart' and 'blockEnd' values. The 'blockStart'
   1048  // and 'blockEnd' properties move relatively positioned elements in
   1049  // the block progression direction. They also must be each other's
   1050  // negative
   1051  const auto blockStart = position->GetAnchorResolvedInset(
   1052      LogicalSide::BStart, aWM, anchorResolutionParams);
   1053  const auto blockEnd = position->GetAnchorResolvedInset(
   1054      LogicalSide::BEnd, aWM, anchorResolutionParams);
   1055  bool blockStartIsAuto = blockStart->IsAuto();
   1056  bool blockEndIsAuto = blockEnd->IsAuto();
   1057 
   1058  // Check for percentage based values and a containing block block-size
   1059  // that depends on the content block-size. Treat them like 'auto'
   1060  if (NS_UNCONSTRAINEDSIZE == aCBSize.BSize(aWM)) {
   1061    if (blockStart->HasPercent()) {
   1062      blockStartIsAuto = true;
   1063    }
   1064    if (blockEnd->HasPercent()) {
   1065      blockEndIsAuto = true;
   1066    }
   1067  }
   1068 
   1069  // If neither is 'auto', 'block-end' is ignored
   1070  if (!blockStartIsAuto && !blockEndIsAuto) {
   1071    blockEndIsAuto = true;
   1072  }
   1073 
   1074  if (blockStartIsAuto) {
   1075    if (blockEndIsAuto) {
   1076      // If both are 'auto' (their initial values), the computed values are 0
   1077      offsets.BStart(aWM) = offsets.BEnd(aWM) = 0;
   1078    } else {
   1079      // 'blockEnd' isn't being treated as 'auto' so compute its value
   1080      offsets.BEnd(aWM) = blockEnd->IsAuto()
   1081                              ? 0
   1082                              : nsLayoutUtils::ComputeCBDependentValue(
   1083                                    aCBSize.BSize(aWM), blockEnd);
   1084 
   1085      // Computed value for 'blockStart' is minus the value of 'blockEnd'
   1086      offsets.BStart(aWM) = -offsets.BEnd(aWM);
   1087    }
   1088 
   1089  } else {
   1090    NS_ASSERTION(blockEndIsAuto, "unexpected specified constraint");
   1091 
   1092    // 'blockStart' isn't 'auto' so compute its value
   1093    offsets.BStart(aWM) =
   1094        nsLayoutUtils::ComputeCBDependentValue(aCBSize.BSize(aWM), blockStart);
   1095 
   1096    // Computed value for 'blockEnd' is minus the value of 'blockStart'
   1097    offsets.BEnd(aWM) = -offsets.BStart(aWM);
   1098  }
   1099 
   1100  // Convert the offsets to physical coordinates and store them on the frame
   1101  const nsMargin physicalOffsets = offsets.GetPhysicalMargin(aWM);
   1102  if (nsMargin* prop =
   1103          aFrame->GetProperty(nsIFrame::ComputedOffsetProperty())) {
   1104    *prop = physicalOffsets;
   1105  } else {
   1106    aFrame->AddProperty(nsIFrame::ComputedOffsetProperty(),
   1107                        new nsMargin(physicalOffsets));
   1108  }
   1109 
   1110  NS_ASSERTION(offsets.IStart(aWM) == -offsets.IEnd(aWM) &&
   1111                   offsets.BStart(aWM) == -offsets.BEnd(aWM),
   1112               "ComputeRelativeOffsets should return valid results!");
   1113 
   1114  return offsets;
   1115 }
   1116 
   1117 /* static */
   1118 void ReflowInput::ApplyRelativePositioning(nsIFrame* aFrame,
   1119                                           const nsMargin& aComputedOffsets,
   1120                                           nsPoint* aPosition) {
   1121  if (!aFrame->IsRelativelyOrStickyPositioned()) {
   1122    NS_ASSERTION(!aFrame->HasProperty(nsIFrame::NormalPositionProperty()),
   1123                 "We assume that changing the 'position' property causes "
   1124                 "frame reconstruction.  If that ever changes, this code "
   1125                 "should call "
   1126                 "aFrame->RemoveProperty(nsIFrame::NormalPositionProperty())");
   1127    return;
   1128  }
   1129 
   1130  // Store the normal position
   1131  aFrame->SetProperty(nsIFrame::NormalPositionProperty(), *aPosition);
   1132 
   1133  const nsStyleDisplay* display = aFrame->StyleDisplay();
   1134  if (StylePositionProperty::Relative == display->mPosition) {
   1135    *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top);
   1136  }
   1137  // For sticky positioned elements, we'll leave them until the scroll container
   1138  // reflows and calls StickyScrollContainer::UpdatePositions() to update their
   1139  // positions.
   1140 }
   1141 
   1142 // static
   1143 void ReflowInput::ComputeAbsPosInlineAutoMargin(nscoord aAvailMarginSpace,
   1144                                                WritingMode aContainingBlockWM,
   1145                                                bool aIsMarginIStartAuto,
   1146                                                bool aIsMarginIEndAuto,
   1147                                                LogicalMargin& aMargin) {
   1148  if (aIsMarginIStartAuto) {
   1149    if (aIsMarginIEndAuto) {
   1150      if (aAvailMarginSpace < 0) {
   1151        // Note that this case is different from the neither-'auto'
   1152        // case below, where the spec says to ignore 'left'/'right'.
   1153        // Ignore the specified value for 'margin-right'.
   1154        aMargin.IEnd(aContainingBlockWM) = aAvailMarginSpace;
   1155      } else {
   1156        // Both 'margin-left' and 'margin-right' are 'auto', so they get
   1157        // equal values
   1158        aMargin.IStart(aContainingBlockWM) = aAvailMarginSpace / 2;
   1159        aMargin.IEnd(aContainingBlockWM) =
   1160            aAvailMarginSpace - aMargin.IStart(aContainingBlockWM);
   1161      }
   1162    } else {
   1163      // Just 'margin-left' is 'auto'
   1164      aMargin.IStart(aContainingBlockWM) = aAvailMarginSpace;
   1165    }
   1166  } else {
   1167    if (aIsMarginIEndAuto) {
   1168      // Just 'margin-right' is 'auto'
   1169      aMargin.IEnd(aContainingBlockWM) = aAvailMarginSpace;
   1170    }
   1171    // Else, both margins are non-auto. This margin box would align to the
   1172    // inset-reduced containing block, so it's not overconstrained.
   1173  }
   1174 }
   1175 
   1176 // static
   1177 void ReflowInput::ComputeAbsPosBlockAutoMargin(nscoord aAvailMarginSpace,
   1178                                               WritingMode aContainingBlockWM,
   1179                                               bool aIsMarginBStartAuto,
   1180                                               bool aIsMarginBEndAuto,
   1181                                               LogicalMargin& aMargin) {
   1182  if (aIsMarginBStartAuto) {
   1183    if (aIsMarginBEndAuto) {
   1184      // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
   1185      // equal values
   1186      aMargin.BStart(aContainingBlockWM) = aAvailMarginSpace / 2;
   1187      aMargin.BEnd(aContainingBlockWM) =
   1188          aAvailMarginSpace - aMargin.BStart(aContainingBlockWM);
   1189    } else {
   1190      // Just margin-block-start is 'auto'
   1191      aMargin.BStart(aContainingBlockWM) = aAvailMarginSpace;
   1192    }
   1193  } else {
   1194    if (aIsMarginBEndAuto) {
   1195      // Just margin-block-end is 'auto'
   1196      aMargin.BEnd(aContainingBlockWM) = aAvailMarginSpace;
   1197    }
   1198    // Else, both margins are non-auto. See comment in the inline version.
   1199  }
   1200 }
   1201 
   1202 void ReflowInput::ApplyRelativePositioning(
   1203    nsIFrame* aFrame, WritingMode aWritingMode,
   1204    const LogicalMargin& aComputedOffsets, LogicalPoint* aPosition,
   1205    const nsSize& aContainerSize) {
   1206  // Subtract the size of the frame from the container size that we
   1207  // use for converting between the logical and physical origins of
   1208  // the frame. This accounts for the fact that logical origins in RTL
   1209  // coordinate systems are at the top right of the frame instead of
   1210  // the top left.
   1211  nsSize frameSize = aFrame->GetSize();
   1212  nsPoint pos =
   1213      aPosition->GetPhysicalPoint(aWritingMode, aContainerSize - frameSize);
   1214  ApplyRelativePositioning(
   1215      aFrame, aComputedOffsets.GetPhysicalMargin(aWritingMode), &pos);
   1216  *aPosition = LogicalPoint(aWritingMode, pos, aContainerSize - frameSize);
   1217 }
   1218 
   1219 ReflowInput::HypotheticalBoxContainerInfo
   1220 ReflowInput::GetHypotheticalBoxContainer(const nsIFrame* aFrame) const {
   1221  nsIFrame* cb = aFrame->GetContainingBlock();
   1222  NS_ASSERTION(cb != mFrame, "How did that happen?");
   1223 
   1224  // If cb is currently being reflowed, find its ReflowInput.
   1225  const ReflowInput* ri = nullptr;
   1226  if (cb->HasAnyStateBits(NS_FRAME_IN_REFLOW)) {
   1227    ri = mParentReflowInput;
   1228    while (ri && ri->mFrame != cb) {
   1229      ri = ri->mParentReflowInput;
   1230    }
   1231  }
   1232 
   1233  const WritingMode wm = cb->GetWritingMode();
   1234  if (ri) {
   1235    return {cb, ri->ComputedLogicalBorderPadding(wm), ri->ComputedSize(wm)};
   1236  }
   1237 
   1238  // Didn't find a ReflowInput for cb. Just compute the information we want, on
   1239  // the assumption that cb already knows its size. This really ought to be true
   1240  // by now.
   1241  NS_ASSERTION(!cb->HasAnyStateBits(NS_FRAME_IN_REFLOW),
   1242               "cb shouldn't be in reflow; we'll lie if it is");
   1243  return {cb, cb->GetLogicalUsedBorderAndPadding(wm), cb->ContentSize(wm)};
   1244 }
   1245 
   1246 struct nsHypotheticalPosition {
   1247  // offset from inline-start edge of containing block (which is a padding edge)
   1248  nscoord mIStart = 0;
   1249  // offset from block-start edge of containing block (which is a padding edge)
   1250  nscoord mBStart = 0;
   1251  WritingMode mWritingMode;
   1252 };
   1253 
   1254 /**
   1255 * aInsideBoxSizing returns the part of the padding, border, and margin
   1256 * in the aAxis dimension that goes inside the edge given by box-sizing;
   1257 * aOutsideBoxSizing returns the rest.
   1258 */
   1259 void ReflowInput::CalculateBorderPaddingMargin(
   1260    LogicalAxis aAxis, nscoord aContainingBlockSize, nscoord* aInsideBoxSizing,
   1261    nscoord* aOutsideBoxSizing) const {
   1262  WritingMode wm = GetWritingMode();
   1263  Side startSide = wm.PhysicalSide(MakeLogicalSide(aAxis, LogicalEdge::Start));
   1264  Side endSide = wm.PhysicalSide(MakeLogicalSide(aAxis, LogicalEdge::End));
   1265 
   1266  nsMargin styleBorder = mStyleBorder->GetComputedBorder();
   1267  nscoord borderStartEnd =
   1268      styleBorder.Side(startSide) + styleBorder.Side(endSide);
   1269 
   1270  nscoord paddingStartEnd, marginStartEnd;
   1271 
   1272  // See if the style system can provide us the padding directly
   1273  const auto* stylePadding = mFrame->StylePadding();
   1274  if (nsMargin padding; stylePadding->GetPadding(padding)) {
   1275    paddingStartEnd = padding.Side(startSide) + padding.Side(endSide);
   1276  } else {
   1277    // We have to compute the start and end values
   1278    const nscoord start = nsLayoutUtils::ComputeCBDependentValue(
   1279        aContainingBlockSize, stylePadding->mPadding.Get(startSide));
   1280    const nscoord end = nsLayoutUtils::ComputeCBDependentValue(
   1281        aContainingBlockSize, stylePadding->mPadding.Get(endSide));
   1282    paddingStartEnd = start + end;
   1283  }
   1284 
   1285  // See if the style system can provide us the margin directly
   1286  if (nsMargin margin; mStyleMargin->GetMargin(margin)) {
   1287    marginStartEnd = margin.Side(startSide) + margin.Side(endSide);
   1288  } else {
   1289    // If the margin is 'auto', ComputeCBDependentValue() will return 0. The
   1290    // correct margin value will be computed later in InitAbsoluteConstraints
   1291    // (which is caller of this function, via CalculateHypotheticalPosition).
   1292    const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   1293    const nscoord start = nsLayoutUtils::ComputeCBDependentValue(
   1294        aContainingBlockSize,
   1295        mStyleMargin->GetMargin(startSide, anchorResolutionParams));
   1296    const nscoord end = nsLayoutUtils::ComputeCBDependentValue(
   1297        aContainingBlockSize,
   1298        mStyleMargin->GetMargin(endSide, anchorResolutionParams));
   1299    marginStartEnd = start + end;
   1300  }
   1301 
   1302  nscoord outside = paddingStartEnd + borderStartEnd + marginStartEnd;
   1303  nscoord inside = 0;
   1304  if (mStylePosition->mBoxSizing == StyleBoxSizing::Border) {
   1305    inside = borderStartEnd + paddingStartEnd;
   1306  }
   1307  outside -= inside;
   1308  *aInsideBoxSizing = inside;
   1309  *aOutsideBoxSizing = outside;
   1310 }
   1311 
   1312 /**
   1313 * Returns true iff a pre-order traversal of the normal child
   1314 * frames rooted at aFrame finds no non-empty frame before aDescendant.
   1315 */
   1316 static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
   1317                                           nsIFrame* aDescendant,
   1318                                           bool* aFound) {
   1319  if (aFrame == aDescendant) {
   1320    *aFound = true;
   1321    return true;
   1322  }
   1323  if (aFrame->IsPlaceholderFrame()) {
   1324    auto ph = static_cast<nsPlaceholderFrame*>(aFrame);
   1325    MOZ_ASSERT(ph->IsSelfEmpty() && ph->PrincipalChildList().IsEmpty());
   1326    ph->SetLineIsEmptySoFar(true);
   1327  } else {
   1328    if (!aFrame->IsSelfEmpty()) {
   1329      *aFound = false;
   1330      return false;
   1331    }
   1332    for (nsIFrame* f : aFrame->PrincipalChildList()) {
   1333      bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
   1334      if (*aFound || !allEmpty) {
   1335        return allEmpty;
   1336      }
   1337    }
   1338  }
   1339  *aFound = false;
   1340  return true;
   1341 }
   1342 
   1343 // In the code below, |aCBReflowInput->mFrame| is the absolute containing block,
   1344 // while |blockContainer| is the nearest block container of the placeholder
   1345 // frame, which may be different from the absolute containing block.
   1346 void ReflowInput::CalculateHypotheticalPosition(
   1347    nsPlaceholderFrame* aPlaceholderFrame, const ReflowInput* aCBReflowInput,
   1348    const LogicalSize& aCBPaddingBoxSize,
   1349    nsHypotheticalPosition& aHypotheticalPos) const {
   1350  NS_ASSERTION(mStyleDisplay->mOriginalDisplay != StyleDisplay::None,
   1351               "mOriginalDisplay has not been properly initialized");
   1352 
   1353  WritingMode cbwm = aCBReflowInput->GetWritingMode();
   1354  const auto [blockContainer, blockContainerBP, blockContainerContentBoxSize] =
   1355      GetHypotheticalBoxContainer(aPlaceholderFrame);
   1356  WritingMode wm = blockContainer->GetWritingMode();
   1357  const nscoord blockContainerContentIStart = blockContainerBP.IStart(wm);
   1358 
   1359  const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   1360  const auto styleISize = mStylePosition->ISize(wm, anchorResolutionParams);
   1361  bool isAutoISize = styleISize->IsAuto();
   1362 
   1363  // If it's a replaced element and it has a 'auto' value for 'inline size', see
   1364  // if we can get the intrinsic size. This will allow us to exactly determine
   1365  // both the inline edges.
   1366  Maybe<nsSize> intrinsicSize;
   1367  if (mFlags.mIsReplaced && isAutoISize) {
   1368    intrinsicSize = mFrame->GetIntrinsicSize().ToSize();
   1369  }
   1370 
   1371  // See if we can calculate what the box inline size would have been if
   1372  // the element had been in the flow
   1373  Maybe<nscoord> boxISize;
   1374  if (mStyleDisplay->IsOriginalDisplayInlineOutside() && !mFlags.mIsReplaced) {
   1375    // For non-replaced inline-level elements the 'inline size' property
   1376    // doesn't apply, so we don't know what the inline size would have
   1377    // been without reflowing it
   1378  } else {
   1379    // It's either a replaced inline-level element or a block-level element
   1380 
   1381    // Determine the total amount of inline direction
   1382    // border/padding/margin that the element would have had if it had
   1383    // been in the flow. Note that we ignore any 'auto' and 'inherit'
   1384    // values
   1385    nscoord contentEdgeToBoxSizingISize, boxSizingToMarginEdgeISize;
   1386    CalculateBorderPaddingMargin(
   1387        LogicalAxis::Inline, blockContainerContentBoxSize.ISize(wm),
   1388        &contentEdgeToBoxSizingISize, &boxSizingToMarginEdgeISize);
   1389 
   1390    if (mFlags.mIsReplaced && isAutoISize) {
   1391      // It's a replaced element with an 'auto' inline size so the box inline
   1392      // size is its intrinsic size plus any border/padding/margin
   1393      if (intrinsicSize) {
   1394        boxISize.emplace(LogicalSize(wm, *intrinsicSize).ISize(wm) +
   1395                         contentEdgeToBoxSizingISize +
   1396                         boxSizingToMarginEdgeISize);
   1397      }
   1398    } else if (isAutoISize) {
   1399      // The box inline size is the block container's inline size
   1400      boxISize.emplace(blockContainerContentBoxSize.ISize(wm));
   1401    } else {
   1402      // We need to compute it. It's important we do this, because if it's
   1403      // percentage based this computed value may be different from the computed
   1404      // value calculated using the absolute containing block width
   1405      nscoord contentEdgeToBoxSizingBSize, dummy;
   1406      CalculateBorderPaddingMargin(LogicalAxis::Block,
   1407                                   blockContainerContentBoxSize.ISize(wm),
   1408                                   &contentEdgeToBoxSizingBSize, &dummy);
   1409 
   1410      const auto contentISize =
   1411          mFrame
   1412              ->ComputeISizeValue(
   1413                  mRenderingContext, wm, blockContainerContentBoxSize,
   1414                  LogicalSize(wm, contentEdgeToBoxSizingISize,
   1415                              contentEdgeToBoxSizingBSize),
   1416                  boxSizingToMarginEdgeISize, *styleISize,
   1417                  *mStylePosition->BSize(wm, anchorResolutionParams),
   1418                  mFrame->GetAspectRatio())
   1419              .mISize;
   1420      boxISize.emplace(contentISize + contentEdgeToBoxSizingISize +
   1421                       boxSizingToMarginEdgeISize);
   1422    }
   1423  }
   1424 
   1425  // Get the placeholder offset in the coordinate space of its block container.
   1426  // XXXbz the placeholder is not fully reflowed yet if our containing block is
   1427  // relatively positioned...
   1428  const nsSize blockContainerSize = BorderBoxSizeAsContainerIfConstrained(
   1429      wm, blockContainerContentBoxSize, blockContainerBP);
   1430  LogicalPoint placeholderOffset(
   1431      wm, aPlaceholderFrame->GetOffsetToIgnoringScrolling(blockContainer),
   1432      blockContainerSize);
   1433 
   1434  // First, determine the hypothetical box's mBStart.  We want to check the
   1435  // content insertion frame of blockContainer for block-ness, but make
   1436  // sure to compute all coordinates in the coordinate system of
   1437  // blockContainer.
   1438  nsBlockFrame* blockFrame =
   1439      do_QueryFrame(blockContainer->GetContentInsertionFrame());
   1440  if (blockFrame) {
   1441    // Use a null containerSize to convert a LogicalPoint functioning as a
   1442    // vector into a physical nsPoint vector.
   1443    const nsSize nullContainerSize;
   1444    LogicalPoint blockOffset(
   1445        wm, blockFrame->GetOffsetToIgnoringScrolling(blockContainer),
   1446        nullContainerSize);
   1447    bool isValid;
   1448    nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid);
   1449    if (!isValid) {
   1450      // Give up.  We're probably dealing with somebody using
   1451      // position:absolute inside native-anonymous content anyway.
   1452      aHypotheticalPos.mBStart = placeholderOffset.B(wm);
   1453    } else {
   1454      NS_ASSERTION(iter.GetContainer() == blockFrame,
   1455                   "Found placeholder in wrong block!");
   1456      nsBlockFrame::LineIterator lineBox = iter.GetLine();
   1457 
   1458      // How we determine the hypothetical box depends on whether the element
   1459      // would have been inline-level or block-level
   1460      LogicalRect lineBounds = lineBox->GetBounds().ConvertTo(
   1461          wm, lineBox->mWritingMode, lineBox->mContainerSize);
   1462      if (mStyleDisplay->IsOriginalDisplayInlineOutside()) {
   1463        // Use the block-start of the inline box which the placeholder lives in
   1464        // as the hypothetical box's block-start.
   1465        aHypotheticalPos.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm);
   1466      } else {
   1467        // The element would have been block-level which means it would
   1468        // be below the line containing the placeholder frame, unless
   1469        // all the frames before it are empty.  In that case, it would
   1470        // have been just before this line.
   1471        // XXXbz the line box is not fully reflowed yet if our
   1472        // containing block is relatively positioned...
   1473        if (lineBox != iter.End()) {
   1474          nsIFrame* firstFrame = lineBox->mFirstChild;
   1475          bool allEmpty = false;
   1476          if (firstFrame == aPlaceholderFrame) {
   1477            aPlaceholderFrame->SetLineIsEmptySoFar(true);
   1478            allEmpty = true;
   1479          } else {
   1480            auto* prev = aPlaceholderFrame->GetPrevSibling();
   1481            if (prev && prev->IsPlaceholderFrame()) {
   1482              auto* ph = static_cast<nsPlaceholderFrame*>(prev);
   1483              if (ph->GetLineIsEmptySoFar(&allEmpty)) {
   1484                aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty);
   1485              }
   1486            }
   1487          }
   1488          if (!allEmpty) {
   1489            bool found = false;
   1490            while (firstFrame) {  // See bug 223064
   1491              allEmpty = AreAllEarlierInFlowFramesEmpty(
   1492                  firstFrame, aPlaceholderFrame, &found);
   1493              if (found || !allEmpty) {
   1494                break;
   1495              }
   1496              firstFrame = firstFrame->GetNextSibling();
   1497            }
   1498            aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty);
   1499          }
   1500          NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
   1501 
   1502          if (allEmpty) {
   1503            // The top of the hypothetical box is the top of the line
   1504            // containing the placeholder, since there is nothing in the
   1505            // line before our placeholder except empty frames.
   1506            aHypotheticalPos.mBStart =
   1507                lineBounds.BStart(wm) + blockOffset.B(wm);
   1508          } else {
   1509            // The top of the hypothetical box is just below the line
   1510            // containing the placeholder.
   1511            aHypotheticalPos.mBStart = lineBounds.BEnd(wm) + blockOffset.B(wm);
   1512          }
   1513        } else {
   1514          // Just use the placeholder's block-offset wrt the containing block
   1515          aHypotheticalPos.mBStart = placeholderOffset.B(wm);
   1516        }
   1517      }
   1518    }
   1519  } else {
   1520    // blockContainer is not a block, so it's probably something like a XUL box,
   1521    // etc. Just use the placeholder's block-offset
   1522    aHypotheticalPos.mBStart = placeholderOffset.B(wm);
   1523  }
   1524 
   1525  // Second, determine the hypothetical box's mIStart.
   1526  // How we determine the hypothetical box depends on whether the element
   1527  // would have been inline-level or block-level
   1528  if (mStyleDisplay->IsOriginalDisplayInlineOutside() ||
   1529      mFlags.mIOffsetsNeedCSSAlign) {
   1530    // The placeholder represents the IStart edge of the hypothetical box.
   1531    // (Or if mFlags.mIOffsetsNeedCSSAlign is set, it represents the IStart
   1532    // edge of the Alignment Container.)
   1533    aHypotheticalPos.mIStart = placeholderOffset.I(wm);
   1534  } else {
   1535    aHypotheticalPos.mIStart = blockContainerContentIStart;
   1536  }
   1537 
   1538  // The current coordinate space is that of the nearest block to the
   1539  // placeholder. Convert to the coordinate space of the absolute containing
   1540  // block.
   1541  const nsIFrame* cbFrame = aCBReflowInput->mFrame;
   1542  nsPoint cbOffset = blockContainer->GetOffsetToIgnoringScrolling(cbFrame);
   1543  nsSize cbSize;
   1544  if (cbFrame->IsViewportFrame()) {
   1545    // When the containing block is the ViewportFrame, i.e. we are calculating
   1546    // the static position for a fixed-positioned frame, we need to adjust the
   1547    // origin to exclude the scrollbar or scrollbar-gutter area. The
   1548    // ViewportFrame's containing block rect is adjusted in
   1549    // AbsoluteContainingBlock::ReflowAbsoluteFrame(), and it will add the
   1550    // rect's origin to the fixed-positioned frame's final position if needed.
   1551    //
   1552    // Note: The origin of the containing block rect is adjusted in
   1553    // ViewportFrame::AdjustReflowInputForScrollbars(). Ensure the code there
   1554    // remains in sync with the logic here.
   1555    if (ScrollContainerFrame* sf =
   1556            do_QueryFrame(cbFrame->PrincipalChildList().FirstChild())) {
   1557      const nsMargin scrollbarSizes = sf->GetActualScrollbarSizes();
   1558      cbOffset.MoveBy(-scrollbarSizes.left, -scrollbarSizes.top);
   1559    }
   1560 
   1561    // ViewportFrame has no border or padding, so the padding-box size is equal
   1562    // to the border-box size (cbSize) that we are computing.
   1563    cbSize = aCBPaddingBoxSize.GetPhysicalSize(cbwm);
   1564  } else {
   1565    cbSize = aCBReflowInput->ComputedSizeAsContainerIfConstrained();
   1566  }
   1567 
   1568  LogicalPoint logCBOffs(wm, cbOffset, cbSize - blockContainerSize);
   1569  aHypotheticalPos.mIStart += logCBOffs.I(wm);
   1570  aHypotheticalPos.mBStart += logCBOffs.B(wm);
   1571 
   1572  // If block direction doesn't match (whether orthogonal or antiparallel),
   1573  // we'll have to convert aHypotheticalPos to be in terms of cbwm.
   1574  // This upcoming conversion must be taken into account for border offsets.
   1575  const bool hypotheticalPosWillUseCbwm =
   1576      cbwm.GetBlockDir() != wm.GetBlockDir();
   1577  // The specified offsets are relative to the absolute containing block's
   1578  // padding edge and our current values are relative to the border edge, so
   1579  // translate.
   1580  const LogicalMargin border = aCBReflowInput->ComputedLogicalBorder(wm);
   1581  if (hypotheticalPosWillUseCbwm &&
   1582      !wm.ParallelAxisStartsOnSameSide(LogicalAxis::Inline, cbwm)) {
   1583    aHypotheticalPos.mIStart += border.IEnd(wm);
   1584  } else {
   1585    aHypotheticalPos.mIStart -= border.IStart(wm);
   1586  }
   1587 
   1588  if (hypotheticalPosWillUseCbwm &&
   1589      !wm.ParallelAxisStartsOnSameSide(LogicalAxis::Block, cbwm)) {
   1590    aHypotheticalPos.mBStart += border.BEnd(wm);
   1591  } else {
   1592    aHypotheticalPos.mBStart -= border.BStart(wm);
   1593  }
   1594  // At this point, we have computed aHypotheticalPos using the writing mode
   1595  // of the placeholder's block container.
   1596 
   1597  if (hypotheticalPosWillUseCbwm) {
   1598    // If the block direction we used in calculating aHypotheticalPos does not
   1599    // match the absolute containing block's, we need to convert here so that
   1600    // aHypotheticalPos is usable in relation to the absolute containing block.
   1601    // This requires computing or measuring the abspos frame's block-size,
   1602    // which is not otherwise required/used here (as aHypotheticalPos
   1603    // records only the block-start coordinate).
   1604 
   1605    // This is similar to the inline-size calculation for a replaced
   1606    // inline-level element or a block-level element (above), except that
   1607    // 'auto' sizing is handled differently in the block direction for non-
   1608    // replaced elements and replaced elements lacking an intrinsic size.
   1609 
   1610    // Determine the total amount of block direction
   1611    // border/padding/margin that the element would have had if it had
   1612    // been in the flow. Note that we ignore any 'auto' and 'inherit'
   1613    // values.
   1614    nscoord insideBoxSizing, outsideBoxSizing;
   1615    CalculateBorderPaddingMargin(LogicalAxis::Block,
   1616                                 blockContainerContentBoxSize.BSize(wm),
   1617                                 &insideBoxSizing, &outsideBoxSizing);
   1618 
   1619    nscoord boxBSize;
   1620    const auto styleBSize = mStylePosition->BSize(wm, anchorResolutionParams);
   1621    const bool isAutoBSize = nsLayoutUtils::IsAutoBSize(
   1622        *styleBSize, blockContainerContentBoxSize.BSize(wm));
   1623    if (isAutoBSize) {
   1624      if (mFlags.mIsReplaced && intrinsicSize) {
   1625        // It's a replaced element with an 'auto' block size so the box
   1626        // block size is its intrinsic size plus any border/padding/margin
   1627        boxBSize = LogicalSize(wm, *intrinsicSize).BSize(wm) +
   1628                   outsideBoxSizing + insideBoxSizing;
   1629      } else {
   1630        // XXX Bug 1191801
   1631        // Figure out how to get the correct boxBSize here (need to reflow the
   1632        // positioned frame?)
   1633        boxBSize = 0;
   1634      }
   1635    } else if (styleBSize->BehavesLikeStretchOnBlockAxis()) {
   1636      MOZ_ASSERT(blockContainerContentBoxSize.BSize(wm) != NS_UNCONSTRAINEDSIZE,
   1637                 "If we're 'stretch' with unconstrained size, isAutoBSize "
   1638                 "should be true which should make us skip this code");
   1639      // TODO(dholbert) The 'insideBoxSizing' and 'outsideBoxSizing' usages
   1640      // here aren't quite right, because we're supposed to be passing margin
   1641      // and borderPadding specifically.  The arithmetic seems to work out in
   1642      // testcases though.
   1643      boxBSize = nsLayoutUtils::ComputeStretchContentBoxBSize(
   1644          blockContainerContentBoxSize.BSize(wm), outsideBoxSizing,
   1645          insideBoxSizing);
   1646    } else {
   1647      // We need to compute it. It's important we do this, because if it's
   1648      // percentage-based this computed value may be different from the
   1649      // computed value calculated using the absolute containing block height.
   1650      boxBSize = nsLayoutUtils::ComputeBSizeValue(
   1651                     blockContainerContentBoxSize.BSize(wm), insideBoxSizing,
   1652                     styleBSize->AsLengthPercentage()) +
   1653                 insideBoxSizing + outsideBoxSizing;
   1654    }
   1655 
   1656    LogicalSize boxSize(wm, boxISize.valueOr(0), boxBSize);
   1657 
   1658    LogicalPoint origin(wm, aHypotheticalPos.mIStart, aHypotheticalPos.mBStart);
   1659    origin = origin.ConvertRectOriginTo(cbwm, wm, boxSize.GetPhysicalSize(wm),
   1660                                        cbSize);
   1661 
   1662    aHypotheticalPos.mIStart = origin.I(cbwm);
   1663    aHypotheticalPos.mBStart = origin.B(cbwm);
   1664    aHypotheticalPos.mWritingMode = cbwm;
   1665  } else {
   1666    aHypotheticalPos.mWritingMode = wm;
   1667  }
   1668 }
   1669 
   1670 void ReflowInput::InitAbsoluteConstraints(const ReflowInput* aCBReflowInput,
   1671                                          const LogicalSize& aCBSize) {
   1672  WritingMode wm = GetWritingMode();
   1673  WritingMode cbwm = aCBReflowInput->GetWritingMode();
   1674  NS_WARNING_ASSERTION(aCBSize.BSize(cbwm) != NS_UNCONSTRAINEDSIZE,
   1675                       "containing block bsize must be constrained");
   1676 
   1677  NS_ASSERTION(!mFrame->IsTableFrame(),
   1678               "InitAbsoluteConstraints should not be called on table frames");
   1679  MOZ_ASSERT(
   1680      mFrame->IsAbsolutelyPositioned(mStyleDisplay),
   1681      "InitAbsoluteConstraints should be called on abspos or fixedpos frames!");
   1682 
   1683  const auto anchorResolutionParams =
   1684      AnchorPosOffsetResolutionParams::ExplicitCBFrameSize(
   1685          AnchorPosResolutionParams::From(this), &aCBSize);
   1686  const auto iStartOffset = mStylePosition->GetAnchorResolvedInset(
   1687      LogicalSide::IStart, cbwm, anchorResolutionParams);
   1688  const auto iEndOffset = mStylePosition->GetAnchorResolvedInset(
   1689      LogicalSide::IEnd, cbwm, anchorResolutionParams);
   1690  const auto bStartOffset = mStylePosition->GetAnchorResolvedInset(
   1691      LogicalSide::BStart, cbwm, anchorResolutionParams);
   1692  const auto bEndOffset = mStylePosition->GetAnchorResolvedInset(
   1693      LogicalSide::BEnd, cbwm, anchorResolutionParams);
   1694  bool iStartIsAuto = iStartOffset->IsAuto();
   1695  bool iEndIsAuto = iEndOffset->IsAuto();
   1696  bool bStartIsAuto = bStartOffset->IsAuto();
   1697  bool bEndIsAuto = bEndOffset->IsAuto();
   1698 
   1699  // If both 'inline-start' and 'inline-end' are 'auto' or both 'block-start'
   1700  // and 'block-end' are 'auto', then compute the hypothetical box position
   1701  // where the element would have if it were in the flow.
   1702  nsHypotheticalPosition hypotheticalPos;
   1703  if ((iStartIsAuto && iEndIsAuto) || (bStartIsAuto && bEndIsAuto)) {
   1704    nsPlaceholderFrame* placeholderFrame = mFrame->GetPlaceholderFrame();
   1705    MOZ_ASSERT(placeholderFrame, "no placeholder frame");
   1706    nsIFrame* placeholderParent = placeholderFrame->GetParent();
   1707    MOZ_ASSERT(placeholderParent, "shouldn't have unparented placeholders");
   1708 
   1709    if (placeholderFrame->HasAnyStateBits(
   1710            PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) {
   1711      MOZ_ASSERT(placeholderParent->IsFlexOrGridContainer(),
   1712                 "This flag should only be set on grid/flex children");
   1713      // If the (as-yet unknown) static position will determine the inline
   1714      // and/or block offsets, set flags to note those offsets aren't valid
   1715      // until we can do CSS Box Alignment on the OOF frame.
   1716      mFlags.mIOffsetsNeedCSSAlign = (iStartIsAuto && iEndIsAuto);
   1717      mFlags.mBOffsetsNeedCSSAlign = (bStartIsAuto && bEndIsAuto);
   1718    }
   1719 
   1720    if (mFlags.mStaticPosIsCBOrigin) {
   1721      hypotheticalPos.mWritingMode = cbwm;
   1722      if (placeholderParent->IsGridContainerFrame() &&
   1723          placeholderParent->HasAnyStateBits(NS_STATE_GRID_IS_COL_MASONRY |
   1724                                             NS_STATE_GRID_IS_ROW_MASONRY)) {
   1725        // Disable CSS alignment in Masonry layout since we don't have real grid
   1726        // areas in that axis.  We'll use the placeholder position instead as it
   1727        // was calculated by nsGridContainerFrame::MasonryLayout.
   1728        auto cbsz = aCBSize.GetPhysicalSize(cbwm);
   1729        LogicalPoint pos = placeholderFrame->GetLogicalPosition(cbwm, cbsz);
   1730        if (placeholderParent->HasAnyStateBits(NS_STATE_GRID_IS_COL_MASONRY)) {
   1731          mFlags.mIOffsetsNeedCSSAlign = false;
   1732          hypotheticalPos.mIStart = pos.I(cbwm);
   1733        } else {
   1734          mFlags.mBOffsetsNeedCSSAlign = false;
   1735          hypotheticalPos.mBStart = pos.B(cbwm);
   1736        }
   1737      }
   1738    } else {
   1739      // XXXmats all this is broken for orthogonal writing-modes: bug 1521988.
   1740      CalculateHypotheticalPosition(placeholderFrame, aCBReflowInput, aCBSize,
   1741                                    hypotheticalPos);
   1742      if (aCBReflowInput->mFrame->IsGridContainerFrame()) {
   1743        // 'hypotheticalPos' is relative to the padding rect of the CB *frame*.
   1744        // In grid layout the CB is the grid area rectangle, so we translate
   1745        // 'hypotheticalPos' to be relative that rectangle here.
   1746        nsRect cb = nsGridContainerFrame::GridItemCB(mFrame);
   1747        nscoord left(0);
   1748        nscoord right(0);
   1749        if (cbwm.IsBidiLTR()) {
   1750          left = cb.X();
   1751        } else {
   1752          right = aCBReflowInput->ComputedWidth() +
   1753                  aCBReflowInput->ComputedPhysicalPadding().LeftRight() -
   1754                  cb.XMost();
   1755        }
   1756        LogicalMargin offsets(cbwm, nsMargin(cb.Y(), right, nscoord(0), left));
   1757        hypotheticalPos.mIStart -= offsets.IStart(cbwm);
   1758        hypotheticalPos.mBStart -= offsets.BStart(cbwm);
   1759      }
   1760    }
   1761  }
   1762 
   1763  LogicalMargin offsets(cbwm);
   1764 
   1765  // Handle auto inset values, as per [1].
   1766  // Technically superceded by a new section [2], but none of the browsers seem
   1767  // to follow this behaviour.
   1768  //
   1769  // [1] https://drafts.csswg.org/css-position-3/#abspos-old
   1770  // [2] https://drafts.csswg.org/css-position-3/#resolving-insets
   1771  if (iStartIsAuto) {
   1772    offsets.IStart(cbwm) = 0;
   1773  } else {
   1774    offsets.IStart(cbwm) = nsLayoutUtils::ComputeCBDependentValue(
   1775        aCBSize.ISize(cbwm), iStartOffset);
   1776  }
   1777  if (iEndIsAuto) {
   1778    offsets.IEnd(cbwm) = 0;
   1779  } else {
   1780    offsets.IEnd(cbwm) =
   1781        nsLayoutUtils::ComputeCBDependentValue(aCBSize.ISize(cbwm), iEndOffset);
   1782  }
   1783 
   1784  if (iStartIsAuto && iEndIsAuto) {
   1785    if (cbwm.IsInlineReversed() !=
   1786        hypotheticalPos.mWritingMode.IsInlineReversed()) {
   1787      offsets.IEnd(cbwm) = hypotheticalPos.mIStart;
   1788      iEndIsAuto = false;
   1789    } else {
   1790      offsets.IStart(cbwm) = hypotheticalPos.mIStart;
   1791      iStartIsAuto = false;
   1792    }
   1793  }
   1794 
   1795  if (bStartIsAuto) {
   1796    offsets.BStart(cbwm) = 0;
   1797  } else {
   1798    offsets.BStart(cbwm) = nsLayoutUtils::ComputeCBDependentValue(
   1799        aCBSize.BSize(cbwm), bStartOffset);
   1800  }
   1801  if (bEndIsAuto) {
   1802    offsets.BEnd(cbwm) = 0;
   1803  } else {
   1804    offsets.BEnd(cbwm) =
   1805        nsLayoutUtils::ComputeCBDependentValue(aCBSize.BSize(cbwm), bEndOffset);
   1806  }
   1807 
   1808  if (bStartIsAuto && bEndIsAuto) {
   1809    // Treat 'top' like 'static-position'
   1810    offsets.BStart(cbwm) = hypotheticalPos.mBStart;
   1811    bStartIsAuto = false;
   1812  }
   1813 
   1814  SetComputedLogicalOffsets(cbwm, offsets);
   1815  const bool isOrthogonal = wm.IsOrthogonalTo(cbwm);
   1816  if (isOrthogonal) {
   1817    if (bStartIsAuto || bEndIsAuto) {
   1818      mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap;
   1819    }
   1820  } else {
   1821    if (iStartIsAuto || iEndIsAuto) {
   1822      mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap;
   1823    }
   1824  }
   1825 
   1826  {
   1827    AutoMaybeDisableFontInflation an(mFrame);
   1828 
   1829    auto size = mFrame->ComputeSize(
   1830        *this, wm, aCBSize.ConvertTo(wm, cbwm),
   1831        aCBSize.ConvertTo(wm, cbwm).ISize(wm),  // XXX or AvailableISize()?
   1832        ComputedLogicalMargin(wm).Size(wm) +
   1833            ComputedLogicalOffsets(wm).Size(wm),
   1834        ComputedLogicalBorderPadding(wm).Size(wm), {}, mComputeSizeFlags);
   1835    mComputedSize = size.mLogicalSize;
   1836    NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size");
   1837    NS_ASSERTION(
   1838        ComputedBSize() == NS_UNCONSTRAINEDSIZE || ComputedBSize() >= 0,
   1839        "Bogus block-size");
   1840 
   1841    mFlags.mIsBSizeSetByAspectRatio =
   1842        size.mAspectRatioUsage == nsIFrame::AspectRatioUsage::ToComputeBSize;
   1843  }
   1844 
   1845  LogicalMargin margin = ComputedLogicalMargin(cbwm);
   1846  const LogicalMargin borderPadding = ComputedLogicalBorderPadding(cbwm);
   1847  const LogicalSize computedSize = mComputedSize.ConvertTo(cbwm, wm);
   1848 
   1849  bool iSizeIsAuto =
   1850      mStylePosition->ISize(cbwm, anchorResolutionParams.mBaseParams)->IsAuto();
   1851  if (iStartIsAuto) {
   1852    // We know 'inset-inline-end' is not 'auto' anymore thanks to the
   1853    // hypothetical box code above. Solve for 'inset-inline-start'.
   1854    if (iSizeIsAuto) {
   1855      offsets.IStart(cbwm) = NS_AUTOOFFSET;
   1856    } else {
   1857      offsets.IStart(cbwm) = aCBSize.ISize(cbwm) - offsets.IEnd(cbwm) -
   1858                             computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) -
   1859                             borderPadding.IStartEnd(cbwm);
   1860    }
   1861  } else if (iEndIsAuto) {
   1862    // We know 'inset-inline-start' is not 'auto' anymore thanks to the
   1863    // hypothetical box code above. Solve for 'inset-inline-end'.
   1864    if (iSizeIsAuto) {
   1865      offsets.IEnd(cbwm) = NS_AUTOOFFSET;
   1866    } else {
   1867      offsets.IEnd(cbwm) = aCBSize.ISize(cbwm) - offsets.IStart(cbwm) -
   1868                           computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) -
   1869                           borderPadding.IStartEnd(cbwm);
   1870    }
   1871  }
   1872 
   1873  bool bSizeIsAuto =
   1874      mStylePosition->BSize(cbwm, anchorResolutionParams.mBaseParams)
   1875          ->BehavesLikeInitialValueOnBlockAxis();
   1876  if (bStartIsAuto) {
   1877    // Solve for 'inset-block-start'.
   1878    if (bSizeIsAuto) {
   1879      offsets.BStart(cbwm) = NS_AUTOOFFSET;
   1880    } else {
   1881      offsets.BStart(cbwm) = aCBSize.BSize(cbwm) - margin.BStartEnd(cbwm) -
   1882                             borderPadding.BStartEnd(cbwm) -
   1883                             computedSize.BSize(cbwm) - offsets.BEnd(cbwm);
   1884    }
   1885  } else if (bEndIsAuto) {
   1886    // Solve for 'inset-block-end'.
   1887    if (bSizeIsAuto) {
   1888      offsets.BEnd(cbwm) = NS_AUTOOFFSET;
   1889    } else {
   1890      offsets.BEnd(cbwm) = aCBSize.BSize(cbwm) - margin.BStartEnd(cbwm) -
   1891                           borderPadding.BStartEnd(cbwm) -
   1892                           computedSize.BSize(cbwm) - offsets.BStart(cbwm);
   1893    }
   1894  }
   1895 
   1896  SetComputedLogicalOffsets(cbwm, offsets);
   1897 }
   1898 
   1899 // This will not be converted to abstract coordinates because it's only
   1900 // used in CalcQuirkContainingBlockHeight
   1901 static nscoord GetBlockMarginBorderPadding(const ReflowInput* aReflowInput) {
   1902  nscoord result = 0;
   1903  if (!aReflowInput) {
   1904    return result;
   1905  }
   1906 
   1907  // zero auto margins
   1908  nsMargin margin = aReflowInput->ComputedPhysicalMargin();
   1909  if (NS_AUTOMARGIN == margin.top) {
   1910    margin.top = 0;
   1911  }
   1912  if (NS_AUTOMARGIN == margin.bottom) {
   1913    margin.bottom = 0;
   1914  }
   1915 
   1916  result += margin.top + margin.bottom;
   1917  result += aReflowInput->ComputedPhysicalBorderPadding().top +
   1918            aReflowInput->ComputedPhysicalBorderPadding().bottom;
   1919 
   1920  return result;
   1921 }
   1922 
   1923 /* Get the height based on the viewport of the containing block specified
   1924 * in aReflowInput when the containing block has mComputedHeight ==
   1925 * NS_UNCONSTRAINEDSIZE This will walk up the chain of containing blocks looking
   1926 * for a computed height until it finds the canvas frame, or it encounters a
   1927 * frame that is not a block, area, or scroll frame. This handles compatibility
   1928 * with IE (see bug 85016 and bug 219693)
   1929 *
   1930 * When we encounter scrolledContent block frames, we skip over them,
   1931 * since they are guaranteed to not be useful for computing the containing
   1932 * block.
   1933 *
   1934 * See also IsQuirkContainingBlockHeight.
   1935 */
   1936 static nscoord CalcQuirkContainingBlockHeight(
   1937    const ReflowInput* aCBReflowInput) {
   1938  const ReflowInput* firstAncestorRI = nullptr;   // a candidate for html frame
   1939  const ReflowInput* secondAncestorRI = nullptr;  // a candidate for body frame
   1940 
   1941  // initialize the default to NS_UNCONSTRAINEDSIZE as this is the containings
   1942  // block computed height when this function is called. It is possible that we
   1943  // don't alter this height especially if we are restricted to one level
   1944  nscoord result = NS_UNCONSTRAINEDSIZE;
   1945 
   1946  const ReflowInput* ri = aCBReflowInput;
   1947  for (; ri; ri = ri->mParentReflowInput) {
   1948    LayoutFrameType frameType = ri->mFrame->Type();
   1949    // if the ancestor is auto height then skip it and continue up if it
   1950    // is the first block frame and possibly the body/html
   1951    if (LayoutFrameType::Block == frameType ||
   1952        LayoutFrameType::ScrollContainer == frameType) {
   1953      secondAncestorRI = firstAncestorRI;
   1954      firstAncestorRI = ri;
   1955 
   1956      // If the current frame we're looking at is positioned, we don't want to
   1957      // go any further (see bug 221784).  The behavior we want here is: 1) If
   1958      // not auto-height, use this as the percentage base.  2) If auto-height,
   1959      // keep looking, unless the frame is positioned.
   1960      if (NS_UNCONSTRAINEDSIZE == ri->ComputedHeight()) {
   1961        if (ri->mFrame->IsAbsolutelyPositioned(ri->mStyleDisplay)) {
   1962          break;
   1963        } else {
   1964          continue;
   1965        }
   1966      }
   1967    } else if (LayoutFrameType::Canvas == frameType) {
   1968      // Always continue on to the height calculation
   1969    } else if (LayoutFrameType::PageContent == frameType) {
   1970      nsIFrame* prevInFlow = ri->mFrame->GetPrevInFlow();
   1971      // only use the page content frame for a height basis if it is the first
   1972      // in flow
   1973      if (prevInFlow) {
   1974        break;
   1975      }
   1976    } else {
   1977      break;
   1978    }
   1979 
   1980    // if the ancestor is the page content frame then the percent base is
   1981    // the avail height, otherwise it is the computed height
   1982    result = (LayoutFrameType::PageContent == frameType) ? ri->AvailableHeight()
   1983                                                         : ri->ComputedHeight();
   1984    // if unconstrained - don't sutract borders - would result in huge height
   1985    if (NS_UNCONSTRAINEDSIZE == result) {
   1986      return result;
   1987    }
   1988 
   1989    // if we got to the canvas or page content frame, then subtract out
   1990    // margin/border/padding for the BODY and HTML elements
   1991    if ((LayoutFrameType::Canvas == frameType) ||
   1992        (LayoutFrameType::PageContent == frameType)) {
   1993      result -= GetBlockMarginBorderPadding(firstAncestorRI);
   1994      result -= GetBlockMarginBorderPadding(secondAncestorRI);
   1995 
   1996 #ifdef DEBUG
   1997      // make sure the first ancestor is the HTML and the second is the BODY
   1998      if (firstAncestorRI) {
   1999        nsIContent* frameContent = firstAncestorRI->mFrame->GetContent();
   2000        if (frameContent) {
   2001          NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::html),
   2002                       "First ancestor is not HTML");
   2003        }
   2004      }
   2005      if (secondAncestorRI) {
   2006        nsIContent* frameContent = secondAncestorRI->mFrame->GetContent();
   2007        if (frameContent) {
   2008          NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::body),
   2009                       "Second ancestor is not BODY");
   2010        }
   2011      }
   2012 #endif
   2013 
   2014    }
   2015    // if we got to the html frame (a block child of the canvas) ...
   2016    else if (LayoutFrameType::Block == frameType && ri->mParentReflowInput &&
   2017             ri->mParentReflowInput->mFrame->IsCanvasFrame()) {
   2018      // ... then subtract out margin/border/padding for the BODY element
   2019      result -= GetBlockMarginBorderPadding(secondAncestorRI);
   2020    }
   2021    break;
   2022  }
   2023 
   2024  // Make sure not to return a negative height here!
   2025  return std::max(result, 0);
   2026 }
   2027 
   2028 LogicalSize ReflowInput::ComputeContainingBlockRectangle(
   2029    nsPresContext* aPresContext, const ReflowInput* aContainingBlockRI) const {
   2030  LogicalSize cbSize = aContainingBlockRI->ComputedSize();
   2031  WritingMode wm = aContainingBlockRI->GetWritingMode();
   2032 
   2033  if (aContainingBlockRI->mFlags.mTreatBSizeAsIndefinite) {
   2034    cbSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
   2035  } else if (aContainingBlockRI->mPercentageBasisInBlockAxis) {
   2036    MOZ_ASSERT(cbSize.BSize(wm) == NS_UNCONSTRAINEDSIZE,
   2037               "Why provide a percentage basis when the containing block's "
   2038               "block-size is definite?");
   2039    cbSize.BSize(wm) = *aContainingBlockRI->mPercentageBasisInBlockAxis;
   2040  }
   2041 
   2042  auto IsQuirky = [](const StyleSize& aSize) -> bool {
   2043    return aSize.ConvertsToPercentage() ||
   2044           aSize.BehavesLikeStretchOnBlockAxis();
   2045  };
   2046  const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   2047  // In quirks mode, if an element has a percent height (or a 'stretch' height,
   2048  // which is kinda like a special version of 100%), then it gets its
   2049  // containing block by looking for an ancestor with a non-auto height.
   2050  // Note: We don't emulate this quirk for percents in calc(), or in vertical
   2051  // writing modes, or if the containing block is a flex or grid item.
   2052  if (!wm.IsVertical() && NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) {
   2053    if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
   2054        !aContainingBlockRI->mFrame->IsFlexOrGridItem() &&
   2055        (IsQuirky(*mStylePosition->GetHeight(anchorResolutionParams)) ||
   2056         (mFrame->IsTableWrapperFrame() &&
   2057          IsQuirky(*mFrame->PrincipalChildList()
   2058                        .FirstChild()
   2059                        ->StylePosition()
   2060                        ->GetHeight(anchorResolutionParams))))) {
   2061      cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(aContainingBlockRI);
   2062    }
   2063  }
   2064 
   2065  return cbSize.ConvertTo(GetWritingMode(), wm);
   2066 }
   2067 
   2068 // XXX refactor this code to have methods for each set of properties
   2069 // we are computing: width,height,line-height; margin; offsets
   2070 
   2071 void ReflowInput::InitConstraints(
   2072    nsPresContext* aPresContext, const Maybe<LogicalSize>& aContainingBlockSize,
   2073    const Maybe<LogicalMargin>& aBorder, const Maybe<LogicalMargin>& aPadding,
   2074    LayoutFrameType aFrameType) {
   2075  WritingMode wm = GetWritingMode();
   2076  LogicalSize cbSize = aContainingBlockSize.valueOr(
   2077      LogicalSize(mWritingMode, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE));
   2078 
   2079  // If this is a reflow root, then set the computed width and
   2080  // height equal to the available space
   2081  if (nullptr == mParentReflowInput || mFlags.mDummyParentReflowInput) {
   2082    // XXXldb This doesn't mean what it used to!
   2083    InitOffsets(wm, cbSize.ISize(wm), aFrameType, mComputeSizeFlags, aBorder,
   2084                aPadding, mStyleDisplay);
   2085    // Override mComputedMargin since reflow roots start from the
   2086    // frame's boundary, which is inside the margin.
   2087    SetComputedLogicalMargin(wm, LogicalMargin(wm));
   2088    SetComputedLogicalOffsets(wm, LogicalMargin(wm));
   2089 
   2090    const auto borderPadding = ComputedLogicalBorderPadding(wm);
   2091    SetComputedISize(
   2092        std::max(0, AvailableISize() - borderPadding.IStartEnd(wm)),
   2093        ResetResizeFlags::No);
   2094    SetComputedBSize(
   2095        AvailableBSize() != NS_UNCONSTRAINEDSIZE
   2096            ? std::max(0, AvailableBSize() - borderPadding.BStartEnd(wm))
   2097            : NS_UNCONSTRAINEDSIZE,
   2098        ResetResizeFlags::No);
   2099 
   2100    mComputedMinSize.SizeTo(mWritingMode, 0, 0);
   2101    mComputedMaxSize.SizeTo(mWritingMode, NS_UNCONSTRAINEDSIZE,
   2102                            NS_UNCONSTRAINEDSIZE);
   2103  } else {
   2104    // Get the containing block's reflow input
   2105    const ReflowInput* cbri = mCBReflowInput;
   2106    MOZ_ASSERT(cbri, "no containing block");
   2107    MOZ_ASSERT(mFrame->GetParent());
   2108 
   2109    // If we weren't given a containing block size, then compute one.
   2110    if (aContainingBlockSize.isNothing()) {
   2111      cbSize = ComputeContainingBlockRectangle(aPresContext, cbri);
   2112    } else if (aPresContext->FragmentainerAwarePositioningEnabled() &&
   2113               mFrame->IsAbsolutelyPositioned(mStyleDisplay) &&
   2114               mFrame->GetPrevInFlow()) {
   2115      // AbsoluteContainingBlock always provides a containing-block size to
   2116      // ReflowInput. However, if the delegating frame is a continuation or an
   2117      // overflow container (i.e. it has zero block-size), we'll need a
   2118      // containing-block size (padding-box size) suitable for resolving the
   2119      // abspos continuation's percentage block-size.
   2120      //
   2121      // Bug 1998818 is to fix the containing-block size for resolving
   2122      // percentage block-size for abspos's first-in-flow.
   2123      cbSize = ComputeContainingBlockRectangle(aPresContext, cbri) +
   2124               cbri->ComputedLogicalPadding(wm).Size(wm);
   2125    }
   2126 
   2127    // See if the containing block height is based on the size of its
   2128    // content
   2129    if (NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) {
   2130      // See if the containing block is a cell frame which needs
   2131      // to use the mComputedHeight of the cell instead of what the cell block
   2132      // passed in.
   2133      // XXX It seems like this could lead to bugs with min-height and friends
   2134      if (cbri->mParentReflowInput && cbri->mFrame->IsTableCellFrame()) {
   2135        cbSize.BSize(wm) = cbri->ComputedSize(wm).BSize(wm);
   2136      }
   2137    }
   2138 
   2139    // XXX Might need to also pass the CB height (not width) for page boxes,
   2140    // too, if we implement them.
   2141 
   2142    // For calculating positioning offsets, margins, borders and
   2143    // padding, we use the writing mode of the containing block
   2144    WritingMode cbwm = cbri->GetWritingMode();
   2145    InitOffsets(cbwm, cbSize.ConvertTo(cbwm, wm).ISize(cbwm), aFrameType,
   2146                mComputeSizeFlags, aBorder, aPadding, mStyleDisplay);
   2147 
   2148    // For calculating the size of this box, we use its own writing mode
   2149    auto blockSize =
   2150        mStylePosition->BSize(wm, AnchorPosResolutionParams::From(this));
   2151    if (blockSize->BehavesLikeStretchOnBlockAxis()) {
   2152      // Resolve 'stretch' to either 'auto' or the stretched bsize, depending
   2153      // on whether our containing block has a definite bsize.
   2154      // TODO(dholbert): remove this in bug 2000035.
   2155      if (NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) {
   2156        blockSize = AnchorResolvedSizeHelper::Auto();
   2157      } else {
   2158        nscoord stretchBSize = nsLayoutUtils::ComputeStretchBSize(
   2159            cbSize.BSize(wm), ComputedLogicalMargin(wm).BStartEnd(wm),
   2160            ComputedLogicalBorderPadding(wm).BStartEnd(wm),
   2161            mStylePosition->mBoxSizing);
   2162        blockSize = AnchorResolvedSizeHelper::LengthPercentage(
   2163            StyleLengthPercentage::FromAppUnits(stretchBSize));
   2164      }
   2165    }
   2166    bool isAutoBSize = blockSize->BehavesLikeInitialValueOnBlockAxis();
   2167 
   2168    // Check for a percentage based block size and a containing block
   2169    // block size that depends on the content block size
   2170    if (blockSize->HasPercent()) {
   2171      if (NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) {
   2172        // this if clause enables %-blockSize on replaced inline frames,
   2173        // such as images.  See bug 54119.  The else clause "blockSizeUnit =
   2174        // eStyleUnit_Auto;" used to be called exclusively.
   2175        if (mFlags.mIsReplaced && mStyleDisplay->IsInlineOutsideStyle()) {
   2176          // Get the containing block's reflow input
   2177          NS_ASSERTION(cbri, "no containing block");
   2178          // in quirks mode, get the cb height using the special quirk method
   2179          if (!wm.IsVertical() &&
   2180              eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
   2181            if (!cbri->mFrame->IsTableCellFrame() &&
   2182                !cbri->mFrame->IsFlexOrGridItem()) {
   2183              cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(cbri);
   2184              if (cbSize.BSize(wm) == NS_UNCONSTRAINEDSIZE) {
   2185                isAutoBSize = true;
   2186              }
   2187            } else {
   2188              isAutoBSize = true;
   2189            }
   2190          }
   2191          // in standard mode, use the cb block size.  if it's "auto",
   2192          // as will be the case by default in BODY, use auto block size
   2193          // as per CSS2 spec.
   2194          else {
   2195            nscoord computedBSize = cbri->ComputedSize(wm).BSize(wm);
   2196            if (NS_UNCONSTRAINEDSIZE != computedBSize) {
   2197              cbSize.BSize(wm) = computedBSize;
   2198            } else {
   2199              isAutoBSize = true;
   2200            }
   2201          }
   2202        } else {
   2203          // default to interpreting the blockSize like 'auto'
   2204          isAutoBSize = true;
   2205        }
   2206      }
   2207    }
   2208 
   2209    // Compute our offsets if the element is relatively positioned.  We
   2210    // need the correct containing block inline-size and block-size
   2211    // here, which is why we need to do it after all the quirks-n-such
   2212    // above. (If the element is sticky positioned, we need to wait
   2213    // until the scroll container knows its size, so we compute offsets
   2214    // from StickyScrollContainer::UpdatePositions.)
   2215    if (mStyleDisplay->IsRelativelyPositioned(mFrame)) {
   2216      const LogicalMargin offsets =
   2217          ComputeRelativeOffsets(cbwm, mFrame, cbSize.ConvertTo(cbwm, wm));
   2218      SetComputedLogicalOffsets(cbwm, offsets);
   2219    } else {
   2220      // Initialize offsets to 0
   2221      SetComputedLogicalOffsets(wm, LogicalMargin(wm));
   2222    }
   2223 
   2224    // Calculate the computed values for min and max properties.  Note that
   2225    // this MUST come after we've computed our border and padding.
   2226    ComputeMinMaxValues(cbSize);
   2227 
   2228    // Calculate the computed inlineSize and blockSize.
   2229    // This varies by frame type.
   2230 
   2231    if (IsInternalTableFrame()) {
   2232      // Internal table elements. The rules vary depending on the type.
   2233      // Calculate the computed isize
   2234      bool rowOrRowGroup = false;
   2235      const auto inlineSize =
   2236          mStylePosition->ISize(wm, AnchorPosResolutionParams::From(this));
   2237      bool isAutoISize = inlineSize->IsAuto();
   2238      if ((StyleDisplay::TableRow == mStyleDisplay->mDisplay) ||
   2239          (StyleDisplay::TableRowGroup == mStyleDisplay->mDisplay)) {
   2240        // 'inlineSize' property doesn't apply to table rows and row groups
   2241        isAutoISize = true;
   2242        rowOrRowGroup = true;
   2243      }
   2244 
   2245      // calc() with both percentages and lengths act like auto on internal
   2246      // table elements
   2247      if (isAutoISize || inlineSize->HasLengthAndPercentage()) {
   2248        if (AvailableISize() != NS_UNCONSTRAINEDSIZE && !rowOrRowGroup) {
   2249          // Internal table elements don't have margins. Only tables and
   2250          // cells have border and padding
   2251          SetComputedISize(
   2252              std::max(0, AvailableISize() -
   2253                              ComputedLogicalBorderPadding(wm).IStartEnd(wm)),
   2254              ResetResizeFlags::No);
   2255        } else {
   2256          SetComputedISize(AvailableISize(), ResetResizeFlags::No);
   2257        }
   2258        NS_ASSERTION(ComputedISize() >= 0, "Bogus computed isize");
   2259 
   2260      } else {
   2261        SetComputedISize(
   2262            ComputeISizeValue(cbSize, mStylePosition->mBoxSizing, *inlineSize),
   2263            ResetResizeFlags::No);
   2264      }
   2265 
   2266      // Calculate the computed block size
   2267      if (StyleDisplay::TableColumn == mStyleDisplay->mDisplay ||
   2268          StyleDisplay::TableColumnGroup == mStyleDisplay->mDisplay) {
   2269        // 'blockSize' property doesn't apply to table columns and column groups
   2270        isAutoBSize = true;
   2271      }
   2272      // calc() with both percentages and lengths acts like 'auto' on internal
   2273      // table elements
   2274      if (isAutoBSize || blockSize->HasLengthAndPercentage()) {
   2275        SetComputedBSize(NS_UNCONSTRAINEDSIZE, ResetResizeFlags::No);
   2276      } else {
   2277        SetComputedBSize(
   2278            ComputeBSizeValue(cbSize.BSize(wm), mStylePosition->mBoxSizing,
   2279                              blockSize->AsLengthPercentage()),
   2280            ResetResizeFlags::No);
   2281      }
   2282 
   2283      // Doesn't apply to internal table elements
   2284      mComputedMinSize.SizeTo(mWritingMode, 0, 0);
   2285      mComputedMaxSize.SizeTo(mWritingMode, NS_UNCONSTRAINEDSIZE,
   2286                              NS_UNCONSTRAINEDSIZE);
   2287    } else if (mFrame->IsAbsolutelyPositioned(mStyleDisplay) &&
   2288               // The absolute constraints are needed only for abspos
   2289               // first-in-flow, not continuations.
   2290               !mFrame->GetPrevInFlow()) {
   2291      InitAbsoluteConstraints(cbri,
   2292                              cbSize.ConvertTo(cbri->GetWritingMode(), wm));
   2293    } else {
   2294      AutoMaybeDisableFontInflation an(mFrame);
   2295 
   2296      nsIFrame* const alignCB = [&] {
   2297        nsIFrame* cb = mFrame->GetParent();
   2298        if (cb->IsTableWrapperFrame()) {
   2299          nsIFrame* alignCBParent = cb->GetParent();
   2300          if (alignCBParent && alignCBParent->IsGridContainerFrame()) {
   2301            return alignCBParent;
   2302          }
   2303        }
   2304        return cb;
   2305      }();
   2306 
   2307      const bool isInlineLevel = [&] {
   2308        if (mFrame->IsTableFrame()) {
   2309          // An inner table frame is not inline-level, even if it happens to
   2310          // have 'display:inline-table'. (That makes its table-wrapper frame be
   2311          // inline-level, but not the inner table frame)
   2312          return false;
   2313        }
   2314        if (mStyleDisplay->IsInlineOutsideStyle()) {
   2315          return true;
   2316        }
   2317        if (mFlags.mIsReplaced && (mStyleDisplay->IsInnerTableStyle() ||
   2318                                   mStyleDisplay->DisplayOutside() ==
   2319                                       StyleDisplayOutside::TableCaption)) {
   2320          // Internal table values on replaced elements behave as inline
   2321          // https://drafts.csswg.org/css-tables-3/#table-structure
   2322          //
   2323          //     ... it is handled instead as though the author had declared
   2324          //     either 'block' (for 'table' display) or 'inline' (for all
   2325          //     other values)"
   2326          //
   2327          // FIXME(emilio): The only test that covers this is
   2328          // table-anonymous-objects-211.xht, which fails on other browsers (but
   2329          // differently to us, if you just remove this condition).
   2330          return true;
   2331        }
   2332        if (mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
   2333            !mStyleDisplay->IsAbsolutelyPositionedStyle()) {
   2334          // Floats are treated as inline-level and also shrink-wrap.
   2335          return true;
   2336        }
   2337        return false;
   2338      }();
   2339 
   2340      if (mParentReflowInput->mFlags.mOrthogonalCellFinalReflow) {
   2341        // This is the "extra" reflow for the inner content of an orthogonal
   2342        // table cell, after the row size has been determined; so we want to
   2343        // respect the cell's size without further adjustment. Its rect may
   2344        // not yet be correct, however, so we base our size on the parent
   2345        // reflow input's available size, adjusted for border widths.
   2346        MOZ_ASSERT(mFrame->GetParent()->IsTableCellFrame(),
   2347                   "unexpected mOrthogonalCellFinalReflow flag!");
   2348        cbSize = mParentReflowInput->AvailableSize().ConvertTo(
   2349            wm, mParentReflowInput->GetWritingMode());
   2350        cbSize -= mParentReflowInput->ComputedLogicalBorder(wm).Size(wm);
   2351        SetAvailableISize(cbSize.ISize(wm));
   2352      } else {
   2353        const bool shouldShrinkWrap = [&] {
   2354          if (isInlineLevel) {
   2355            return true;
   2356          }
   2357          if (mFlags.mIsReplaced && !alignCB->IsFlexOrGridContainer()) {
   2358            // Shrink-wrap replaced elements when in-flow (out of flows are
   2359            // handled above). We exclude replaced elements in grid or flex
   2360            // contexts, where we don't want to shrink-wrap unconditionally (so
   2361            // that stretching can happen). When grid/flex explicitly want
   2362            // shrink-wrapping, they can request it directly using the relevant
   2363            // flag.
   2364            return true;
   2365          }
   2366          if (!alignCB->IsGridContainerFrame() &&
   2367              mWritingMode.IsOrthogonalTo(alignCB->GetWritingMode())) {
   2368            // Shrink-wrap blocks that are orthogonal to their container (unless
   2369            // we're in a grid?)
   2370            return true;
   2371          }
   2372          return false;
   2373        }();
   2374 
   2375        if (shouldShrinkWrap) {
   2376          mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap;
   2377        }
   2378 
   2379        if (cbSize.ISize(wm) == NS_UNCONSTRAINEDSIZE) {
   2380          // For orthogonal flows, where we found a parent orthogonal-limit for
   2381          // AvailableISize() in Init(), we'll use the same here as well.
   2382          cbSize.ISize(wm) = AvailableISize();
   2383        }
   2384      }
   2385 
   2386      auto size = mFrame->ComputeSize(*this, wm, cbSize, AvailableISize(),
   2387                                      ComputedLogicalMargin(wm).Size(wm),
   2388                                      ComputedLogicalBorderPadding(wm).Size(wm),
   2389                                      mStyleSizeOverrides, mComputeSizeFlags);
   2390 
   2391      mComputedSize = size.mLogicalSize;
   2392      NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size");
   2393      NS_ASSERTION(
   2394          ComputedBSize() == NS_UNCONSTRAINEDSIZE || ComputedBSize() >= 0,
   2395          "Bogus block-size");
   2396 
   2397      mFlags.mIsBSizeSetByAspectRatio =
   2398          size.mAspectRatioUsage == nsIFrame::AspectRatioUsage::ToComputeBSize;
   2399 
   2400      const bool shouldCalculateBlockSideMargins = [&]() {
   2401        if (isInlineLevel) {
   2402          return false;
   2403        }
   2404        if (mFrame->IsTableFrame()) {
   2405          return false;
   2406        }
   2407        if (alignCB->IsFlexOrGridContainer()) {
   2408          // Exclude flex and grid items.
   2409          return false;
   2410        }
   2411        const auto pseudoType = mFrame->Style()->GetPseudoType();
   2412        if (pseudoType == PseudoStyleType::marker &&
   2413            mFrame->GetParent()->StyleList()->mListStylePosition ==
   2414                StyleListStylePosition::Outside) {
   2415          // Exclude outside ::markers.
   2416          return false;
   2417        }
   2418        if (pseudoType == PseudoStyleType::columnContent) {
   2419          // Exclude -moz-column-content since it cannot have any margin.
   2420          return false;
   2421        }
   2422        return true;
   2423      }();
   2424 
   2425      if (shouldCalculateBlockSideMargins) {
   2426        CalculateBlockSideMargins();
   2427      }
   2428    }
   2429  }
   2430 
   2431  // Save our containing block dimensions
   2432  mContainingBlockSize = cbSize;
   2433 }
   2434 
   2435 static void UpdateProp(nsIFrame* aFrame,
   2436                       const FramePropertyDescriptor<nsMargin>* aProperty,
   2437                       bool aNeeded, const nsMargin& aNewValue) {
   2438  if (aNeeded) {
   2439    if (nsMargin* propValue = aFrame->GetProperty(aProperty)) {
   2440      *propValue = aNewValue;
   2441    } else {
   2442      aFrame->AddProperty(aProperty, new nsMargin(aNewValue));
   2443    }
   2444  } else {
   2445    aFrame->RemoveProperty(aProperty);
   2446  }
   2447 }
   2448 
   2449 void SizeComputationInput::InitOffsets(WritingMode aCBWM, nscoord aPercentBasis,
   2450                                       LayoutFrameType aFrameType,
   2451                                       ComputeSizeFlags aFlags,
   2452                                       const Maybe<LogicalMargin>& aBorder,
   2453                                       const Maybe<LogicalMargin>& aPadding,
   2454                                       const nsStyleDisplay* aDisplay) {
   2455  nsPresContext* presContext = mFrame->PresContext();
   2456 
   2457  // Compute margins from the specified margin style information. These
   2458  // become the default computed values, and may be adjusted below
   2459  // XXX fix to provide 0,0 for the top&bottom margins for
   2460  // inline-non-replaced elements
   2461  bool needMarginProp = ComputeMargin(aCBWM, aPercentBasis, aFrameType);
   2462  // Note that ComputeMargin() simplistically resolves 'auto' margins to 0.
   2463  // In formatting contexts where this isn't correct, some later code will
   2464  // need to update the UsedMargin() property with the actual resolved value.
   2465  // One example of this is ::CalculateBlockSideMargins().
   2466  ::UpdateProp(mFrame, nsIFrame::UsedMarginProperty(), needMarginProp,
   2467               ComputedPhysicalMargin());
   2468 
   2469  const WritingMode wm = GetWritingMode();
   2470  const nsStyleDisplay* disp = mFrame->StyleDisplayWithOptionalParam(aDisplay);
   2471  bool needPaddingProp;
   2472  LayoutDeviceIntMargin widgetPadding;
   2473  if (mIsThemed && presContext->Theme()->GetWidgetPadding(
   2474                       presContext->DeviceContext(), mFrame,
   2475                       disp->EffectiveAppearance(), &widgetPadding)) {
   2476    const nsMargin padding = LayoutDevicePixel::ToAppUnits(
   2477        widgetPadding, presContext->AppUnitsPerDevPixel());
   2478    SetComputedLogicalPadding(wm, LogicalMargin(wm, padding));
   2479    needPaddingProp = false;
   2480  } else if (mFrame->IsInSVGTextSubtree()) {
   2481    SetComputedLogicalPadding(wm, LogicalMargin(wm));
   2482    needPaddingProp = false;
   2483  } else if (aPadding) {  // padding is an input arg
   2484    SetComputedLogicalPadding(wm, *aPadding);
   2485    nsMargin stylePadding;
   2486    // If the caller passes a padding that doesn't match our style (like
   2487    // nsTextControlFrame might due due to theming), then we also need a
   2488    // padding prop.
   2489    needPaddingProp = !mFrame->StylePadding()->GetPadding(stylePadding) ||
   2490                      aPadding->GetPhysicalMargin(wm) != stylePadding;
   2491  } else {
   2492    needPaddingProp = ComputePadding(aCBWM, aPercentBasis, aFrameType);
   2493  }
   2494 
   2495  // Add [align|justify]-content:baseline padding contribution.
   2496  typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop;
   2497  auto ApplyBaselinePadding = [this, wm, &needPaddingProp](LogicalAxis aAxis,
   2498                                                           Prop aProp) {
   2499    bool found;
   2500    nscoord val = mFrame->GetProperty(aProp, &found);
   2501    if (found) {
   2502      NS_ASSERTION(val != nscoord(0), "zero in this property is useless");
   2503      LogicalSide side;
   2504      if (val > 0) {
   2505        side = MakeLogicalSide(aAxis, LogicalEdge::Start);
   2506      } else {
   2507        side = MakeLogicalSide(aAxis, LogicalEdge::End);
   2508        val = -val;
   2509      }
   2510      mComputedPadding.Side(side, wm) += val;
   2511      needPaddingProp = true;
   2512      if (aAxis == LogicalAxis::Block && val > 0) {
   2513        // We have a baseline-adjusted block-axis start padding, so
   2514        // we need this to mark lines dirty when mIsBResize is true:
   2515        this->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
   2516      }
   2517    }
   2518  };
   2519  if (!aFlags.contains(ComputeSizeFlag::IsGridMeasuringReflow)) {
   2520    ApplyBaselinePadding(LogicalAxis::Block, nsIFrame::BBaselinePadProperty());
   2521  }
   2522  if (!aFlags.contains(ComputeSizeFlag::ShrinkWrap)) {
   2523    ApplyBaselinePadding(LogicalAxis::Inline, nsIFrame::IBaselinePadProperty());
   2524  }
   2525 
   2526  LogicalMargin border(wm);
   2527  if (mIsThemed) {
   2528    const LayoutDeviceIntMargin widgetBorder =
   2529        presContext->Theme()->GetWidgetBorder(
   2530            presContext->DeviceContext(), mFrame, disp->EffectiveAppearance());
   2531    border = LogicalMargin(
   2532        wm, LayoutDevicePixel::ToAppUnits(widgetBorder,
   2533                                          presContext->AppUnitsPerDevPixel()));
   2534  } else if (mFrame->IsInSVGTextSubtree()) {
   2535    // Do nothing since the border local variable is initialized all zero.
   2536  } else if (aBorder) {  // border is an input arg
   2537    border = *aBorder;
   2538  } else {
   2539    border = LogicalMargin(wm, mFrame->StyleBorder()->GetComputedBorder());
   2540  }
   2541  SetComputedLogicalBorderPadding(wm, border + ComputedLogicalPadding(wm));
   2542 
   2543  if (aFrameType == LayoutFrameType::Scrollbar) {
   2544    // scrollbars may have had their width or height smashed to zero
   2545    // by the associated scrollframe, in which case we must not report
   2546    // any padding or border.
   2547    nsSize size(mFrame->GetSize());
   2548    if (size.width == 0 || size.height == 0) {
   2549      SetComputedLogicalPadding(wm, LogicalMargin(wm));
   2550      SetComputedLogicalBorderPadding(wm, LogicalMargin(wm));
   2551    }
   2552  }
   2553 
   2554  bool hasPaddingChange;
   2555  if (nsMargin* oldPadding =
   2556          mFrame->GetProperty(nsIFrame::UsedPaddingProperty())) {
   2557    // Note: If a padding change is already detectable without resolving the
   2558    // percentage, e.g. a padding is changing from 50px to 50%,
   2559    // nsIFrame::DidSetComputedStyle() will cache the old padding in
   2560    // UsedPaddingProperty().
   2561    hasPaddingChange = *oldPadding != ComputedPhysicalPadding();
   2562  } else {
   2563    // Our padding may have changed, but we can't tell at this point.
   2564    hasPaddingChange = needPaddingProp;
   2565  }
   2566  // Keep mHasPaddingChange bit set until we've done reflow. We'll clear it in
   2567  // nsIFrame::DidReflow()
   2568  mFrame->SetHasPaddingChange(mFrame->HasPaddingChange() || hasPaddingChange);
   2569 
   2570  ::UpdateProp(mFrame, nsIFrame::UsedPaddingProperty(), needPaddingProp,
   2571               ComputedPhysicalPadding());
   2572 }
   2573 
   2574 // This code enforces section 10.3.3 of the CSS2 spec for this formula:
   2575 //
   2576 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
   2577 //   'padding-right' + 'border-right-width' + 'margin-right'
   2578 //   = width of containing block
   2579 //
   2580 // Note: the width unit is not auto when this is called
   2581 void ReflowInput::CalculateBlockSideMargins() {
   2582  MOZ_ASSERT(!mFrame->IsTableFrame(),
   2583             "Inner table frame cannot have computed margins!");
   2584 
   2585  // Calculations here are done in the containing block's writing mode,
   2586  // which is where margins will eventually be applied: we're calculating
   2587  // margins that will be used by the container in its inline direction,
   2588  // which in the case of an orthogonal contained block will correspond to
   2589  // the block direction of this reflow input. So in the orthogonal-flow
   2590  // case, "CalculateBlock*Side*Margins" will actually end up adjusting
   2591  // the BStart/BEnd margins; those are the "sides" of the block from its
   2592  // container's point of view.
   2593  WritingMode cbWM = GetCBWritingMode();
   2594 
   2595  nscoord availISizeCBWM = AvailableSize(cbWM).ISize(cbWM);
   2596  nscoord computedISizeCBWM = ComputedSize(cbWM).ISize(cbWM);
   2597  if (availISizeCBWM == NS_UNCONSTRAINEDSIZE ||
   2598      computedISizeCBWM == NS_UNCONSTRAINEDSIZE) {
   2599    // For orthogonal flows, where we found a parent orthogonal-limit
   2600    // for AvailableISize() in Init(), we don't have meaningful sizes to
   2601    // adjust.  Act like the sum is already correct (below).
   2602    return;
   2603  }
   2604 
   2605  LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != computedISizeCBWM &&
   2606                           NS_UNCONSTRAINEDSIZE != availISizeCBWM,
   2607                       "have unconstrained inline-size; this should only "
   2608                       "result from very large sizes, not attempts at "
   2609                       "intrinsic inline-size calculation");
   2610 
   2611  LogicalMargin margin = ComputedLogicalMargin(cbWM);
   2612  LogicalMargin borderPadding = ComputedLogicalBorderPadding(cbWM);
   2613  nscoord sum = margin.IStartEnd(cbWM) + borderPadding.IStartEnd(cbWM) +
   2614                computedISizeCBWM;
   2615  if (sum == availISizeCBWM) {
   2616    // The sum is already correct
   2617    return;
   2618  }
   2619 
   2620  // Determine the start and end margin values. The isize value
   2621  // remains constant while we do this.
   2622 
   2623  // Calculate how much space is available for margins
   2624  nscoord availMarginSpace = availISizeCBWM - sum;
   2625 
   2626  // If the available margin space is negative, then don't follow the
   2627  // usual overconstraint rules.
   2628  if (availMarginSpace < 0) {
   2629    margin.IEnd(cbWM) += availMarginSpace;
   2630    SetComputedLogicalMargin(cbWM, margin);
   2631    return;
   2632  }
   2633 
   2634  const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   2635  // The css2 spec clearly defines how block elements should behave
   2636  // in section 10.3.3.
   2637  bool isAutoStartMargin =
   2638      mStyleMargin->GetMargin(LogicalSide::IStart, cbWM, anchorResolutionParams)
   2639          ->IsAuto();
   2640  bool isAutoEndMargin =
   2641      mStyleMargin->GetMargin(LogicalSide::IEnd, cbWM, anchorResolutionParams)
   2642          ->IsAuto();
   2643  if (!isAutoStartMargin && !isAutoEndMargin) {
   2644    // Neither margin is 'auto' so we're over constrained. Use the
   2645    // 'direction' property of the parent to tell which margin to
   2646    // ignore
   2647    // First check if there is an HTML alignment that we should honor
   2648    const StyleTextAlign* textAlign =
   2649        mParentReflowInput
   2650            ? &mParentReflowInput->mFrame->StyleText()->mTextAlign
   2651            : nullptr;
   2652    if (textAlign && (*textAlign == StyleTextAlign::MozLeft ||
   2653                      *textAlign == StyleTextAlign::MozCenter ||
   2654                      *textAlign == StyleTextAlign::MozRight)) {
   2655      if (mParentReflowInput->mWritingMode.IsBidiLTR()) {
   2656        isAutoStartMargin = *textAlign != StyleTextAlign::MozLeft;
   2657        isAutoEndMargin = *textAlign != StyleTextAlign::MozRight;
   2658      } else {
   2659        isAutoStartMargin = *textAlign != StyleTextAlign::MozRight;
   2660        isAutoEndMargin = *textAlign != StyleTextAlign::MozLeft;
   2661      }
   2662    }
   2663    // Otherwise apply the CSS rules, and ignore one margin by forcing
   2664    // it to 'auto', depending on 'direction'.
   2665    else {
   2666      isAutoEndMargin = true;
   2667    }
   2668  }
   2669 
   2670  // Logic which is common to blocks and tables
   2671  // The computed margins need not be zero because the 'auto' could come from
   2672  // overconstraint or from HTML alignment so values need to be accumulated
   2673 
   2674  if (isAutoStartMargin) {
   2675    if (isAutoEndMargin) {
   2676      // Both margins are 'auto' so the computed addition should be equal
   2677      nscoord forStart = availMarginSpace / 2;
   2678      margin.IStart(cbWM) += forStart;
   2679      margin.IEnd(cbWM) += availMarginSpace - forStart;
   2680    } else {
   2681      margin.IStart(cbWM) += availMarginSpace;
   2682    }
   2683  } else if (isAutoEndMargin) {
   2684    margin.IEnd(cbWM) += availMarginSpace;
   2685  }
   2686  SetComputedLogicalMargin(cbWM, margin);
   2687 
   2688  if (isAutoStartMargin || isAutoEndMargin) {
   2689    // Update the UsedMargin property if we were tracking it already.
   2690    nsMargin* propValue = mFrame->GetProperty(nsIFrame::UsedMarginProperty());
   2691    if (propValue) {
   2692      *propValue = margin.GetPhysicalMargin(cbWM);
   2693    }
   2694  }
   2695 }
   2696 
   2697 // For "normal" we use the font's normal line height (em height + leading).
   2698 // If both internal leading and  external leading specified by font itself are
   2699 // zeros, we should compensate this by creating extra (external) leading.
   2700 // This is necessary because without this compensation, normal line height might
   2701 // look too tight.
   2702 static nscoord GetNormalLineHeight(nsFontMetrics* aFontMetrics) {
   2703  MOZ_ASSERT(aFontMetrics, "no font metrics");
   2704  nscoord externalLeading = aFontMetrics->ExternalLeading();
   2705  nscoord internalLeading = aFontMetrics->InternalLeading();
   2706  nscoord emHeight = aFontMetrics->EmHeight();
   2707  if (!internalLeading && !externalLeading) {
   2708    return NSToCoordRound(static_cast<float>(emHeight) *
   2709                          ReflowInput::kNormalLineHeightFactor);
   2710  }
   2711  return emHeight + internalLeading + externalLeading;
   2712 }
   2713 
   2714 static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh,
   2715                                        const nsFont& aFont, nsAtom* aLanguage,
   2716                                        bool aExplicitLanguage,
   2717                                        nsPresContext* aPresContext,
   2718                                        bool aIsVertical, nscoord aBlockBSize,
   2719                                        float aFontSizeInflation) {
   2720  if (aLh.IsLength()) {
   2721    nscoord result = aLh.AsLength().ToAppUnits();
   2722    if (aFontSizeInflation != 1.0f) {
   2723      result = NSToCoordRound(static_cast<float>(result) * aFontSizeInflation);
   2724    }
   2725    return result;
   2726  }
   2727 
   2728  if (aLh.IsNumber()) {
   2729    // For factor units the computed value of the line-height property
   2730    // is found by multiplying the factor by the font's computed size
   2731    // (adjusted for min-size prefs and text zoom).
   2732    return aFont.size.ScaledBy(aLh.AsNumber() * aFontSizeInflation)
   2733        .ToAppUnits();
   2734  }
   2735 
   2736  MOZ_ASSERT(aLh.IsNormal() || aLh.IsMozBlockHeight());
   2737  if (aLh.IsMozBlockHeight() && aBlockBSize != NS_UNCONSTRAINEDSIZE) {
   2738    return aBlockBSize;
   2739  }
   2740 
   2741  auto size = aFont.size;
   2742  size.ScaleBy(aFontSizeInflation);
   2743 
   2744  if (aPresContext) {
   2745    nsFont font = aFont;
   2746    font.size = size;
   2747    nsFontMetrics::Params params;
   2748    params.language = aLanguage;
   2749    params.explicitLanguage = aExplicitLanguage;
   2750    params.orientation =
   2751        aIsVertical ? nsFontMetrics::eVertical : nsFontMetrics::eHorizontal;
   2752    params.userFontSet = aPresContext->GetUserFontSet();
   2753    params.textPerf = aPresContext->GetTextPerfMetrics();
   2754    params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
   2755    RefPtr<nsFontMetrics> fm = aPresContext->GetMetricsFor(font, params);
   2756    return GetNormalLineHeight(fm);
   2757  }
   2758  // If we don't have a pres context, use a 1.2em fallback.
   2759  size.ScaleBy(ReflowInput::kNormalLineHeightFactor);
   2760  return size.ToAppUnits();
   2761 }
   2762 
   2763 nscoord ReflowInput::GetLineHeight() const {
   2764  if (mLineHeight != NS_UNCONSTRAINEDSIZE) {
   2765    return mLineHeight;
   2766  }
   2767 
   2768  nscoord blockBSize = nsLayoutUtils::IsNonWrapperBlock(mFrame)
   2769                           ? ComputedBSize()
   2770                           : (mCBReflowInput ? mCBReflowInput->ComputedBSize()
   2771                                             : NS_UNCONSTRAINEDSIZE);
   2772  mLineHeight = CalcLineHeight(*mFrame->Style(), mFrame->PresContext(),
   2773                               mFrame->GetContent(), blockBSize,
   2774                               nsLayoutUtils::FontSizeInflationFor(mFrame));
   2775  return mLineHeight;
   2776 }
   2777 
   2778 void ReflowInput::SetLineHeight(nscoord aLineHeight) {
   2779  MOZ_ASSERT(aLineHeight >= 0, "aLineHeight must be >= 0!");
   2780 
   2781  if (mLineHeight != aLineHeight) {
   2782    mLineHeight = aLineHeight;
   2783    // Setting used line height can change a frame's block-size if mFrame's
   2784    // block-size behaves as auto.
   2785    InitResizeFlags(mFrame->PresContext(), mFrame->Type());
   2786  }
   2787 }
   2788 
   2789 /* static */
   2790 nscoord ReflowInput::CalcLineHeight(const ComputedStyle& aStyle,
   2791                                    nsPresContext* aPresContext,
   2792                                    const nsIContent* aContent,
   2793                                    nscoord aBlockBSize,
   2794                                    float aFontSizeInflation) {
   2795  const StyleLineHeight& lh = aStyle.StyleFont()->mLineHeight;
   2796  WritingMode wm(&aStyle);
   2797  const bool vertical = wm.IsVertical() && !wm.IsSideways();
   2798  return CalcLineHeight(lh, *aStyle.StyleFont(), aPresContext, vertical,
   2799                        aContent, aBlockBSize, aFontSizeInflation);
   2800 }
   2801 
   2802 nscoord ReflowInput::CalcLineHeight(
   2803    const StyleLineHeight& aLh, const nsStyleFont& aRelativeToFont,
   2804    nsPresContext* aPresContext, bool aIsVertical, const nsIContent* aContent,
   2805    nscoord aBlockBSize, float aFontSizeInflation) {
   2806  nscoord lineHeight =
   2807      ComputeLineHeight(aLh, aRelativeToFont.mFont, aRelativeToFont.mLanguage,
   2808                        aRelativeToFont.mExplicitLanguage, aPresContext,
   2809                        aIsVertical, aBlockBSize, aFontSizeInflation);
   2810 
   2811  NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");
   2812 
   2813  const auto* input = HTMLInputElement::FromNodeOrNull(aContent);
   2814  if (input && input->IsSingleLineTextControl()) {
   2815    // For Web-compatibility, single-line text input elements cannot
   2816    // have a line-height smaller than 'normal'.
   2817    if (!aLh.IsNormal()) {
   2818      nscoord normal = ComputeLineHeight(
   2819          StyleLineHeight::Normal(), aRelativeToFont.mFont,
   2820          aRelativeToFont.mLanguage, aRelativeToFont.mExplicitLanguage,
   2821          aPresContext, aIsVertical, aBlockBSize, aFontSizeInflation);
   2822      if (lineHeight < normal) {
   2823        lineHeight = normal;
   2824      }
   2825    }
   2826  }
   2827 
   2828  return lineHeight;
   2829 }
   2830 
   2831 nscoord ReflowInput::CalcLineHeightForCanvas(const StyleLineHeight& aLh,
   2832                                             const nsFont& aRelativeToFont,
   2833                                             nsAtom* aLanguage,
   2834                                             bool aExplicitLanguage,
   2835                                             nsPresContext* aPresContext,
   2836                                             WritingMode aWM) {
   2837  return ComputeLineHeight(aLh, aRelativeToFont, aLanguage, aExplicitLanguage,
   2838                           aPresContext, aWM.IsVertical() && !aWM.IsSideways(),
   2839                           NS_UNCONSTRAINEDSIZE, 1.0f);
   2840 }
   2841 
   2842 bool SizeComputationInput::ComputeMargin(WritingMode aCBWM,
   2843                                         nscoord aPercentBasis,
   2844                                         LayoutFrameType aFrameType) {
   2845  // SVG text frames have no margin.
   2846  if (mFrame->IsInSVGTextSubtree()) {
   2847    return false;
   2848  }
   2849 
   2850  if (aFrameType == LayoutFrameType::Table) {
   2851    // Table frame's margin is inherited to the table wrapper frame via the
   2852    // ::-moz-table-wrapper rule in ua.css, so don't set any margins for it.
   2853    SetComputedLogicalMargin(mWritingMode, LogicalMargin(mWritingMode));
   2854    return false;
   2855  }
   2856 
   2857  // If style style can provide us the margin directly, then use it.
   2858  const nsStyleMargin* styleMargin = mFrame->StyleMargin();
   2859  nsMargin margin;
   2860  const bool isLayoutDependent = !styleMargin->GetMargin(margin);
   2861  if (isLayoutDependent) {
   2862    // We have to compute the value. Note that this calculation is
   2863    // performed according to the writing mode of the containing block
   2864    // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows)
   2865    if (aPercentBasis == NS_UNCONSTRAINEDSIZE) {
   2866      aPercentBasis = 0;
   2867    }
   2868    LogicalMargin m(aCBWM);
   2869    const auto anchorResolutionParams =
   2870        AnchorPosResolutionParams::From(mFrame, mAnchorPosResolutionCache);
   2871    for (const LogicalSide side : LogicalSides::All) {
   2872      m.Side(side, aCBWM) = nsLayoutUtils::ComputeCBDependentValue(
   2873          aPercentBasis,
   2874          styleMargin->GetMargin(side, aCBWM, anchorResolutionParams));
   2875    }
   2876    SetComputedLogicalMargin(aCBWM, m);
   2877  } else {
   2878    SetComputedLogicalMargin(mWritingMode, LogicalMargin(mWritingMode, margin));
   2879  }
   2880 
   2881  // ... but font-size-inflation-based margin adjustment uses the
   2882  // frame's writing mode
   2883  nscoord marginAdjustment = FontSizeInflationListMarginAdjustment(mFrame);
   2884 
   2885  if (marginAdjustment > 0) {
   2886    LogicalMargin m = ComputedLogicalMargin(mWritingMode);
   2887    m.IStart(mWritingMode) += marginAdjustment;
   2888    SetComputedLogicalMargin(mWritingMode, m);
   2889  }
   2890 
   2891  return isLayoutDependent;
   2892 }
   2893 
   2894 bool SizeComputationInput::ComputePadding(WritingMode aCBWM,
   2895                                          nscoord aPercentBasis,
   2896                                          LayoutFrameType aFrameType) {
   2897  // If style can provide us the padding directly, then use it.
   2898  const nsStylePadding* stylePadding = mFrame->StylePadding();
   2899  nsMargin padding;
   2900  bool isCBDependent = !stylePadding->GetPadding(padding);
   2901  // a table row/col group, row/col doesn't have padding
   2902  // XXXldb Neither do border-collapse tables.
   2903  if (LayoutFrameType::TableRowGroup == aFrameType ||
   2904      LayoutFrameType::TableColGroup == aFrameType ||
   2905      LayoutFrameType::TableRow == aFrameType ||
   2906      LayoutFrameType::TableCol == aFrameType) {
   2907    SetComputedLogicalPadding(mWritingMode, LogicalMargin(mWritingMode));
   2908  } else if (isCBDependent) {
   2909    // We have to compute the value. This calculation is performed
   2910    // according to the writing mode of the containing block
   2911    // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows)
   2912    // clamp negative calc() results to 0
   2913    if (aPercentBasis == NS_UNCONSTRAINEDSIZE) {
   2914      aPercentBasis = 0;
   2915    }
   2916    LogicalMargin p(aCBWM);
   2917    for (const LogicalSide side : LogicalSides::All) {
   2918      p.Side(side, aCBWM) = std::max(
   2919          0, nsLayoutUtils::ComputeCBDependentValue(
   2920                 aPercentBasis, stylePadding->mPadding.Get(side, aCBWM)));
   2921    }
   2922    SetComputedLogicalPadding(aCBWM, p);
   2923  } else {
   2924    SetComputedLogicalPadding(mWritingMode,
   2925                              LogicalMargin(mWritingMode, padding));
   2926  }
   2927  return isCBDependent;
   2928 }
   2929 
   2930 void ReflowInput::ComputeMinMaxValues(const LogicalSize& aCBSize) {
   2931  WritingMode wm = GetWritingMode();
   2932 
   2933  const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   2934  const auto minISize = mStylePosition->MinISize(wm, anchorResolutionParams);
   2935  const auto maxISize = mStylePosition->MaxISize(wm, anchorResolutionParams);
   2936  const auto minBSize = mStylePosition->MinBSize(wm, anchorResolutionParams);
   2937  const auto maxBSize = mStylePosition->MaxBSize(wm, anchorResolutionParams);
   2938 
   2939  LogicalSize minWidgetSize(wm);
   2940  if (mIsThemed) {
   2941    nsPresContext* pc = mFrame->PresContext();
   2942    const LayoutDeviceIntSize widget = pc->Theme()->GetMinimumWidgetSize(
   2943        pc, mFrame, mStyleDisplay->EffectiveAppearance());
   2944 
   2945    // Convert themed widget's physical dimensions to logical coords.
   2946    minWidgetSize = {
   2947        wm, LayoutDeviceIntSize::ToAppUnits(widget, pc->AppUnitsPerDevPixel())};
   2948 
   2949    // GetMinimumWidgetSize() returns border-box; we need content-box.
   2950    minWidgetSize -= ComputedLogicalBorderPadding(wm).Size(wm);
   2951  }
   2952 
   2953  // NOTE: min-width:auto resolves to 0, except on a flex item. (But
   2954  // even there, it's supposed to be ignored (i.e. treated as 0) until
   2955  // the flex container explicitly resolves & considers it.)
   2956  if (minISize->IsAuto()) {
   2957    SetComputedMinISize(0);
   2958  } else {
   2959    SetComputedMinISize(
   2960        ComputeISizeValue(aCBSize, mStylePosition->mBoxSizing, *minISize));
   2961  }
   2962 
   2963  if (mIsThemed) {
   2964    SetComputedMinISize(std::max(ComputedMinISize(), minWidgetSize.ISize(wm)));
   2965  }
   2966 
   2967  if (maxISize->IsNone()) {
   2968    // Specified value of 'none'
   2969    SetComputedMaxISize(NS_UNCONSTRAINEDSIZE);
   2970  } else {
   2971    SetComputedMaxISize(
   2972        ComputeISizeValue(aCBSize, mStylePosition->mBoxSizing, *maxISize));
   2973  }
   2974 
   2975  // If the computed value of 'min-width' is greater than the value of
   2976  // 'max-width', 'max-width' is set to the value of 'min-width'
   2977  if (ComputedMinISize() > ComputedMaxISize()) {
   2978    SetComputedMaxISize(ComputedMinISize());
   2979  }
   2980 
   2981  // Check for percentage based values and a containing block height that
   2982  // depends on the content height. Treat them like the initial value.
   2983  // Likewise, check for calc() with percentages on internal table elements;
   2984  // that's treated as the initial value too.
   2985  const bool isInternalTableFrame = IsInternalTableFrame();
   2986  const nscoord& bPercentageBasis = aCBSize.BSize(wm);
   2987  auto BSizeBehavesAsInitialValue = [&](const auto& aBSize) {
   2988    if (nsLayoutUtils::IsAutoBSize(aBSize, bPercentageBasis)) {
   2989      return true;
   2990    }
   2991    if (isInternalTableFrame) {
   2992      return aBSize.HasLengthAndPercentage();
   2993    }
   2994    return false;
   2995  };
   2996 
   2997  // NOTE: min-height:auto resolves to 0, except on a flex item. (But
   2998  // even there, it's supposed to be ignored (i.e. treated as 0) until
   2999  // the flex container explicitly resolves & considers it.)
   3000  if (BSizeBehavesAsInitialValue(*minBSize)) {
   3001    SetComputedMinBSize(0);
   3002  } else {
   3003    SetComputedMinBSize(ComputeBSizeValueHandlingStretch(
   3004        bPercentageBasis, mStylePosition->mBoxSizing, *minBSize));
   3005  }
   3006 
   3007  if (mIsThemed) {
   3008    SetComputedMinBSize(std::max(ComputedMinBSize(), minWidgetSize.BSize(wm)));
   3009  }
   3010 
   3011  if (BSizeBehavesAsInitialValue(*maxBSize)) {
   3012    // Specified value of 'none'
   3013    SetComputedMaxBSize(NS_UNCONSTRAINEDSIZE);
   3014  } else {
   3015    SetComputedMaxBSize(ComputeBSizeValueHandlingStretch(
   3016        bPercentageBasis, mStylePosition->mBoxSizing, *maxBSize));
   3017  }
   3018 
   3019  // If the computed value of 'min-height' is greater than the value of
   3020  // 'max-height', 'max-height' is set to the value of 'min-height'
   3021  if (ComputedMinBSize() > ComputedMaxBSize()) {
   3022    SetComputedMaxBSize(ComputedMinBSize());
   3023  }
   3024 }
   3025 
   3026 bool ReflowInput::IsInternalTableFrame() const {
   3027  return mFrame->IsTableRowGroupFrame() || mFrame->IsTableColGroupFrame() ||
   3028         mFrame->IsTableRowFrame() || mFrame->IsTableCellFrame();
   3029 }