tor-browser

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

nsContainerFrame.cpp (114733B)


      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 /* base class #1 for rendering objects that have child lists */
      8 
      9 #include "nsContainerFrame.h"
     10 
     11 #include <algorithm>
     12 
     13 #include "AnchorPositioningUtils.h"
     14 #include "CSSAlignUtils.h"
     15 #include "mozilla/AbsoluteContainingBlock.h"
     16 #include "mozilla/AutoRestore.h"
     17 #include "mozilla/ComputedStyle.h"
     18 #include "mozilla/PresShell.h"
     19 #include "mozilla/dom/Document.h"
     20 #include "mozilla/dom/HTMLSummaryElement.h"
     21 #include "mozilla/gfx/2D.h"
     22 #include "mozilla/gfx/Types.h"
     23 #include "mozilla/webrender/WebRenderAPI.h"
     24 #include "mozilla/widget/InitData.h"
     25 #include "nsAttrValue.h"
     26 #include "nsAttrValueInlines.h"
     27 #include "nsBlockFrame.h"
     28 #include "nsCOMPtr.h"
     29 #include "nsCSSFrameConstructor.h"
     30 #include "nsCSSRendering.h"
     31 #include "nsCanvasFrame.h"
     32 #include "nsContainerFrameInlines.h"
     33 #include "nsDisplayList.h"
     34 #include "nsError.h"
     35 #include "nsFlexContainerFrame.h"
     36 #include "nsFrameSelection.h"
     37 #include "nsGkAtoms.h"
     38 #include "nsIBaseWindow.h"
     39 #include "nsIFrameInlines.h"
     40 #include "nsIWidget.h"
     41 #include "nsPlaceholderFrame.h"
     42 #include "nsPoint.h"
     43 #include "nsPresContext.h"
     44 #include "nsPrintfCString.h"
     45 #include "nsRect.h"
     46 #include "nsStyleConsts.h"
     47 
     48 using namespace mozilla;
     49 using namespace mozilla::dom;
     50 using namespace mozilla::layout;
     51 
     52 using mozilla::gfx::ColorPattern;
     53 using mozilla::gfx::DeviceColor;
     54 using mozilla::gfx::DrawTarget;
     55 using mozilla::gfx::Rect;
     56 using mozilla::gfx::sRGBColor;
     57 using mozilla::gfx::ToDeviceColor;
     58 
     59 nsContainerFrame::~nsContainerFrame() = default;
     60 
     61 NS_QUERYFRAME_HEAD(nsContainerFrame)
     62  NS_QUERYFRAME_ENTRY(nsContainerFrame)
     63 NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame)
     64 
     65 void nsContainerFrame::SetInitialChildList(ChildListID aListID,
     66                                           nsFrameList&& aChildList) {
     67 #ifdef DEBUG
     68  nsIFrame::VerifyDirtyBitSet(aChildList);
     69  for (nsIFrame* f : aChildList) {
     70    MOZ_ASSERT(f->GetParent() == this, "Unexpected parent");
     71  }
     72 #endif
     73  if (aListID == FrameChildListID::Principal) {
     74    MOZ_ASSERT(mFrames.IsEmpty(),
     75               "unexpected second call to SetInitialChildList");
     76    mFrames = std::move(aChildList);
     77  } else {
     78    MOZ_ASSERT_UNREACHABLE("Unexpected child list");
     79  }
     80 }
     81 
     82 void nsContainerFrame::AppendFrames(ChildListID aListID,
     83                                    nsFrameList&& aFrameList) {
     84  MOZ_ASSERT(aListID == FrameChildListID::Principal ||
     85                 aListID == FrameChildListID::NoReflowPrincipal,
     86             "unexpected child list");
     87 
     88  if (MOZ_UNLIKELY(aFrameList.IsEmpty())) {
     89    return;
     90  }
     91 
     92  DrainSelfOverflowList();  // ensure the last frame is in mFrames
     93  mFrames.AppendFrames(this, std::move(aFrameList));
     94 
     95  if (aListID != FrameChildListID::NoReflowPrincipal) {
     96    PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
     97                                  NS_FRAME_HAS_DIRTY_CHILDREN);
     98  }
     99 }
    100 
    101 void nsContainerFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
    102                                    const nsLineList::iterator* aPrevFrameLine,
    103                                    nsFrameList&& aFrameList) {
    104  MOZ_ASSERT(aListID == FrameChildListID::Principal ||
    105                 aListID == FrameChildListID::NoReflowPrincipal,
    106             "unexpected child list");
    107  NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
    108               "inserting after sibling frame with different parent");
    109 
    110  if (MOZ_UNLIKELY(aFrameList.IsEmpty())) {
    111    return;
    112  }
    113 
    114  DrainSelfOverflowList();  // ensure aPrevFrame is in mFrames
    115  mFrames.InsertFrames(this, aPrevFrame, std::move(aFrameList));
    116 
    117  if (aListID != FrameChildListID::NoReflowPrincipal) {
    118    PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
    119                                  NS_FRAME_HAS_DIRTY_CHILDREN);
    120  }
    121 }
    122 
    123 void nsContainerFrame::RemoveFrame(DestroyContext& aContext,
    124                                   ChildListID aListID, nsIFrame* aOldFrame) {
    125  MOZ_ASSERT(aListID == FrameChildListID::Principal ||
    126                 aListID == FrameChildListID::NoReflowPrincipal,
    127             "unexpected child list");
    128 
    129  AutoTArray<nsIFrame*, 10> continuations;
    130  {
    131    nsIFrame* continuation = aOldFrame;
    132    while (continuation) {
    133      continuations.AppendElement(continuation);
    134      continuation = continuation->GetNextContinuation();
    135    }
    136  }
    137 
    138  mozilla::PresShell* presShell = PresShell();
    139  nsContainerFrame* lastParent = nullptr;
    140 
    141  // Loop and destroy aOldFrame and all of its continuations.
    142  //
    143  // Request a reflow on the parent frames involved unless we were explicitly
    144  // told not to (FrameChildListID::NoReflowPrincipal).
    145  const bool generateReflowCommand =
    146      aListID != FrameChildListID::NoReflowPrincipal;
    147  for (nsIFrame* continuation : Reversed(continuations)) {
    148    nsContainerFrame* parent = continuation->GetParent();
    149 
    150    // Please note that 'parent' may not actually be where 'continuation' lives.
    151    // We really MUST use StealFrame() and nothing else here.
    152    // @see nsInlineFrame::StealFrame for details.
    153    parent->StealFrame(continuation);
    154    continuation->Destroy(aContext);
    155    if (generateReflowCommand && parent != lastParent) {
    156      presShell->FrameNeedsReflow(parent, IntrinsicDirty::FrameAndAncestors,
    157                                  NS_FRAME_HAS_DIRTY_CHILDREN);
    158      lastParent = parent;
    159    }
    160  }
    161 }
    162 
    163 void nsContainerFrame::DestroyAbsoluteFrames(DestroyContext& aContext) {
    164  if (auto* absCB = GetAbsoluteContainingBlock()) {
    165    absCB->DestroyFrames(aContext);
    166    MarkAsNotAbsoluteContainingBlock();
    167  }
    168 }
    169 
    170 void nsContainerFrame::SafelyDestroyFrameListProp(
    171    DestroyContext& aContext, mozilla::PresShell* aPresShell,
    172    FrameListPropertyDescriptor aProp) {
    173  // Note that the last frame can be removed through another route and thus
    174  // delete the property -- that's why we fetch the property again before
    175  // removing each frame rather than fetching it once and iterating the list.
    176  while (nsFrameList* frameList = GetProperty(aProp)) {
    177    // Note: Similar to nsFrameList::DestroyFrames(), we remove the frames in
    178    // reverse order to avoid unnecessary updates to the first-continuation and
    179    // first-in-flow cache. If we delete them from front to back, updating the
    180    // cache has a O(n^2) time complexity.
    181    nsIFrame* frame = frameList->RemoveLastChild();
    182    if (MOZ_LIKELY(frame)) {
    183      frame->Destroy(aContext);
    184    } else {
    185      (void)TakeProperty(aProp);
    186      frameList->Delete(aPresShell);
    187      return;
    188    }
    189  }
    190 }
    191 
    192 void nsContainerFrame::Destroy(DestroyContext& aContext) {
    193  DestroyAbsoluteFrames(aContext);
    194 
    195  // Destroy frames on the principal child list.
    196  mFrames.DestroyFrames(aContext);
    197 
    198  // If we have any IB split siblings, clear their references to us.
    199  if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
    200    // Delete previous sibling's reference to me.
    201    if (nsIFrame* prevSib = GetProperty(nsIFrame::IBSplitPrevSibling())) {
    202      NS_WARNING_ASSERTION(
    203          this == prevSib->GetProperty(nsIFrame::IBSplitSibling()),
    204          "IB sibling chain is inconsistent");
    205      prevSib->RemoveProperty(nsIFrame::IBSplitSibling());
    206    }
    207 
    208    // Delete next sibling's reference to me.
    209    if (nsIFrame* nextSib = GetProperty(nsIFrame::IBSplitSibling())) {
    210      NS_WARNING_ASSERTION(
    211          this == nextSib->GetProperty(nsIFrame::IBSplitPrevSibling()),
    212          "IB sibling chain is inconsistent");
    213      nextSib->RemoveProperty(nsIFrame::IBSplitPrevSibling());
    214    }
    215 
    216 #ifdef DEBUG
    217    // This is just so we can assert it's not set in nsIFrame::DestroyFrom.
    218    RemoveStateBits(NS_FRAME_PART_OF_IBSPLIT);
    219 #endif
    220  }
    221 
    222  if (MOZ_UNLIKELY(!mProperties.IsEmpty())) {
    223    using T = mozilla::FrameProperties::UntypedDescriptor;
    224    bool hasO = false, hasOC = false, hasEOC = false;
    225    mProperties.ForEach([&](const T& aProp, uint64_t) {
    226      if (aProp == OverflowProperty()) {
    227        hasO = true;
    228      } else if (aProp == OverflowContainersProperty()) {
    229        hasOC = true;
    230      } else if (aProp == ExcessOverflowContainersProperty()) {
    231        hasEOC = true;
    232      }
    233      return true;
    234    });
    235 
    236    // Destroy frames on the auxiliary frame lists and delete the lists.
    237    mozilla::PresShell* presShell = PresShell();
    238    if (hasO) {
    239      SafelyDestroyFrameListProp(aContext, presShell, OverflowProperty());
    240    }
    241 
    242    MOZ_ASSERT(CanContainOverflowContainers() || !(hasOC || hasEOC),
    243               "this type of frame shouldn't have overflow containers");
    244    if (hasOC) {
    245      SafelyDestroyFrameListProp(aContext, presShell,
    246                                 OverflowContainersProperty());
    247    }
    248    if (hasEOC) {
    249      SafelyDestroyFrameListProp(aContext, presShell,
    250                                 ExcessOverflowContainersProperty());
    251    }
    252  }
    253 
    254  nsSplittableFrame::Destroy(aContext);
    255 }
    256 
    257 /////////////////////////////////////////////////////////////////////////////
    258 // Child frame enumeration
    259 
    260 const nsFrameList& nsContainerFrame::GetChildList(ChildListID aListID) const {
    261  // We only know about the principal child list, the overflow lists,
    262  // and the backdrop list.
    263  switch (aListID) {
    264    case FrameChildListID::Principal:
    265      return mFrames;
    266    case FrameChildListID::Overflow: {
    267      nsFrameList* list = GetOverflowFrames();
    268      return list ? *list : nsFrameList::EmptyList();
    269    }
    270    case FrameChildListID::OverflowContainers: {
    271      nsFrameList* list = GetOverflowContainers();
    272      return list ? *list : nsFrameList::EmptyList();
    273    }
    274    case FrameChildListID::ExcessOverflowContainers: {
    275      nsFrameList* list = GetExcessOverflowContainers();
    276      return list ? *list : nsFrameList::EmptyList();
    277    }
    278    default:
    279      return nsSplittableFrame::GetChildList(aListID);
    280  }
    281 }
    282 
    283 void nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
    284  mFrames.AppendIfNonempty(aLists, FrameChildListID::Principal);
    285 
    286  using T = mozilla::FrameProperties::UntypedDescriptor;
    287  mProperties.ForEach([this, aLists](const T& aProp, uint64_t aValue) {
    288    typedef const nsFrameList* L;
    289    if (aProp == OverflowProperty()) {
    290      reinterpret_cast<L>(aValue)->AppendIfNonempty(aLists,
    291                                                    FrameChildListID::Overflow);
    292    } else if (aProp == OverflowContainersProperty()) {
    293      MOZ_ASSERT(CanContainOverflowContainers(),
    294                 "found unexpected OverflowContainersProperty");
    295      (void)this;  // silence clang -Wunused-lambda-capture in opt builds
    296      reinterpret_cast<L>(aValue)->AppendIfNonempty(
    297          aLists, FrameChildListID::OverflowContainers);
    298    } else if (aProp == ExcessOverflowContainersProperty()) {
    299      MOZ_ASSERT(CanContainOverflowContainers(),
    300                 "found unexpected ExcessOverflowContainersProperty");
    301      (void)this;  // silence clang -Wunused-lambda-capture in opt builds
    302      reinterpret_cast<L>(aValue)->AppendIfNonempty(
    303          aLists, FrameChildListID::ExcessOverflowContainers);
    304    }
    305    return true;
    306  });
    307 
    308  nsSplittableFrame::GetChildLists(aLists);
    309 }
    310 
    311 /////////////////////////////////////////////////////////////////////////////
    312 // Painting/Events
    313 
    314 void nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
    315                                        const nsDisplayListSet& aLists) {
    316  DisplayBorderBackgroundOutline(aBuilder, aLists);
    317  BuildDisplayListForNonBlockChildren(aBuilder, aLists);
    318 }
    319 
    320 void nsContainerFrame::BuildDisplayListForNonBlockChildren(
    321    nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
    322    DisplayChildFlags aFlags) {
    323  nsIFrame* kid = mFrames.FirstChild();
    324  if (!kid || HidesContent()) {
    325    return;
    326  }
    327  // Put each child's background directly onto the content list
    328  nsDisplayListSet set(aLists, aLists.Content());
    329  // The children should be in content order
    330  while (kid) {
    331    BuildDisplayListForChild(aBuilder, kid, set, aFlags);
    332    kid = kid->GetNextSibling();
    333  }
    334 }
    335 
    336 class nsDisplaySelectionOverlay final : public nsPaintedDisplayItem {
    337 public:
    338  /**
    339   * @param aSelectionValue nsISelectionController::getDisplaySelection.
    340   */
    341  nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
    342                            int16_t aSelectionValue)
    343      : nsPaintedDisplayItem(aBuilder, aFrame),
    344        mSelectionValue(aSelectionValue) {
    345    MOZ_COUNT_CTOR(nsDisplaySelectionOverlay);
    346  }
    347 
    348  MOZ_COUNTED_DTOR_FINAL(nsDisplaySelectionOverlay)
    349 
    350  virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
    351  bool CreateWebRenderCommands(
    352      mozilla::wr::DisplayListBuilder& aBuilder,
    353      mozilla::wr::IpcResourceUpdateQueue& aResources,
    354      const StackingContextHelper& aSc,
    355      mozilla::layers::RenderRootStateManager* aManager,
    356      nsDisplayListBuilder* aDisplayListBuilder) override;
    357  NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY)
    358 private:
    359  DeviceColor ComputeColor() const;
    360 
    361  static DeviceColor ComputeColorFromSelectionStyle(ComputedStyle&);
    362  static DeviceColor ApplyTransparencyIfNecessary(nscolor);
    363 
    364  // nsISelectionController::getDisplaySelection.
    365  int16_t mSelectionValue;
    366 };
    367 
    368 DeviceColor nsDisplaySelectionOverlay::ApplyTransparencyIfNecessary(
    369    nscolor aColor) {
    370  // If it has already alpha, leave it like that.
    371  if (NS_GET_A(aColor) != 255) {
    372    return ToDeviceColor(aColor);
    373  }
    374 
    375  // NOTE(emilio): Blink and WebKit do something slightly different here, and
    376  // blend the color with white instead, both for overlays and text backgrounds.
    377  auto color = sRGBColor::FromABGR(aColor);
    378  color.a = 0.5;
    379  return ToDeviceColor(color);
    380 }
    381 
    382 DeviceColor nsDisplaySelectionOverlay::ComputeColorFromSelectionStyle(
    383    ComputedStyle& aStyle) {
    384  return ApplyTransparencyIfNecessary(
    385      aStyle.GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor));
    386 }
    387 
    388 DeviceColor nsDisplaySelectionOverlay::ComputeColor() const {
    389  LookAndFeel::ColorID colorID;
    390  if (RefPtr<ComputedStyle> style =
    391          mFrame->ComputeSelectionStyle(mSelectionValue)) {
    392    return ComputeColorFromSelectionStyle(*style);
    393  }
    394  if (mSelectionValue == nsISelectionController::SELECTION_ON) {
    395    colorID = LookAndFeel::ColorID::Highlight;
    396  } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) {
    397    colorID = LookAndFeel::ColorID::TextSelectAttentionBackground;
    398  } else {
    399    colorID = LookAndFeel::ColorID::TextSelectDisabledBackground;
    400  }
    401 
    402  return ApplyTransparencyIfNecessary(
    403      LookAndFeel::Color(colorID, mFrame, NS_RGB(255, 255, 255)));
    404 }
    405 
    406 void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder,
    407                                      gfxContext* aCtx) {
    408  DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
    409  ColorPattern color(ComputeColor());
    410 
    411  nsIntRect pxRect =
    412      GetPaintRect(aBuilder, aCtx)
    413          .ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel());
    414  Rect rect(pxRect.x, pxRect.y, pxRect.width, pxRect.height);
    415  MaybeSnapToDevicePixels(rect, aDrawTarget, true);
    416 
    417  aDrawTarget.FillRect(rect, color);
    418 }
    419 
    420 bool nsDisplaySelectionOverlay::CreateWebRenderCommands(
    421    mozilla::wr::DisplayListBuilder& aBuilder,
    422    mozilla::wr::IpcResourceUpdateQueue& aResources,
    423    const StackingContextHelper& aSc,
    424    mozilla::layers::RenderRootStateManager* aManager,
    425    nsDisplayListBuilder* aDisplayListBuilder) {
    426  wr::LayoutRect bounds = wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
    427      nsRect(ToReferenceFrame(), Frame()->GetSize()),
    428      mFrame->PresContext()->AppUnitsPerDevPixel()));
    429  aBuilder.PushRect(bounds, bounds, !BackfaceIsHidden(), false, false,
    430                    wr::ToColorF(ComputeColor()));
    431  return true;
    432 }
    433 
    434 void nsContainerFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
    435                                               nsDisplayList* aList,
    436                                               uint16_t aContentType) {
    437  if (!IsSelected() || !IsVisibleForPainting()) {
    438    return;
    439  }
    440 
    441  int16_t displaySelection = PresShell()->GetSelectionFlags();
    442  if (!(displaySelection & aContentType)) {
    443    return;
    444  }
    445 
    446  const nsFrameSelection* frameSelection = GetConstFrameSelection();
    447  int16_t selectionValue = frameSelection->GetDisplaySelection();
    448 
    449  if (selectionValue <= nsISelectionController::SELECTION_HIDDEN) {
    450    return;  // selection is hidden or off
    451  }
    452 
    453  nsIContent* newContent = mContent->GetParent();
    454 
    455  // check to see if we are anonymous content
    456  // XXXbz there has GOT to be a better way of determining this!
    457  int32_t offset =
    458      newContent ? newContent->ComputeIndexOf_Deprecated(mContent) : 0;
    459 
    460  // look up to see what selection(s) are on this frame
    461  UniquePtr<SelectionDetails> details = frameSelection->LookUpSelection(
    462      newContent, offset, 1,
    463      ShouldPaintNormalSelection()
    464          ? nsFrameSelection::IgnoreNormalSelection::No
    465          : nsFrameSelection::IgnoreNormalSelection::Yes);
    466  if (!details) {
    467    return;
    468  }
    469 
    470  bool normal = false;
    471  for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) {
    472    if (sd->mSelectionType == SelectionType::eNormal) {
    473      normal = true;
    474    }
    475  }
    476 
    477  if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) {
    478    // Don't overlay an image if it's not in the primary selection.
    479    return;
    480  }
    481 
    482  aList->AppendNewToTop<nsDisplaySelectionOverlay>(aBuilder, this,
    483                                                   selectionValue);
    484 }
    485 
    486 /* virtual */
    487 void nsContainerFrame::ChildIsDirty(nsIFrame* aChild) {
    488  NS_ASSERTION(aChild->IsSubtreeDirty(), "child isn't actually dirty");
    489 
    490  AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
    491 }
    492 
    493 nsIFrame::FrameSearchResult nsContainerFrame::PeekOffsetNoAmount(
    494    bool aForward, int32_t* aOffset) {
    495  NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
    496  // Don't allow the caret to stay in an empty (leaf) container frame.
    497  return CONTINUE_EMPTY;
    498 }
    499 
    500 nsIFrame::FrameSearchResult nsContainerFrame::PeekOffsetCharacter(
    501    bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) {
    502  NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
    503  // Don't allow the caret to stay in an empty (leaf) container frame.
    504  return CONTINUE_EMPTY;
    505 }
    506 
    507 /////////////////////////////////////////////////////////////////////////////
    508 // Helper member functions
    509 
    510 void nsContainerFrame::ReparentFrame(nsIFrame* aFrame,
    511                                     nsContainerFrame* aOldParent,
    512                                     nsContainerFrame* aNewParent) {
    513  NS_ASSERTION(aOldParent == aFrame->GetParent(),
    514               "Parent not consistent with expectations");
    515 
    516  aFrame->SetParent(aNewParent);
    517 }
    518 
    519 void nsContainerFrame::ReparentFrames(nsFrameList& aFrameList,
    520                                      nsContainerFrame* aOldParent,
    521                                      nsContainerFrame* aNewParent) {
    522  for (auto* f : aFrameList) {
    523    ReparentFrame(f, aOldParent, aNewParent);
    524  }
    525 }
    526 
    527 void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext,
    528                                          nsIWidget* aWidget,
    529                                          const nsSize& aMinSize,
    530                                          const nsSize& aMaxSize) {
    531  LayoutDeviceIntSize devMinSize(
    532      aPresContext->AppUnitsToDevPixels(aMinSize.width),
    533      aPresContext->AppUnitsToDevPixels(aMinSize.height));
    534  LayoutDeviceIntSize devMaxSize(
    535      aMaxSize.width == NS_UNCONSTRAINEDSIZE
    536          ? NS_MAXSIZE
    537          : aPresContext->AppUnitsToDevPixels(aMaxSize.width),
    538      aMaxSize.height == NS_UNCONSTRAINEDSIZE
    539          ? NS_MAXSIZE
    540          : aPresContext->AppUnitsToDevPixels(aMaxSize.height));
    541 
    542  // MinSize has a priority over MaxSize
    543  if (devMinSize.width > devMaxSize.width) {
    544    devMaxSize.width = devMinSize.width;
    545  }
    546  if (devMinSize.height > devMaxSize.height) {
    547    devMaxSize.height = devMinSize.height;
    548  }
    549 
    550  DesktopToLayoutDeviceScale constraintsScale(MOZ_WIDGET_INVALID_SCALE);
    551  if (nsIWidget* rootWidget = aPresContext->GetNearestWidget()) {
    552    constraintsScale = rootWidget->GetDesktopToDeviceScale();
    553  }
    554 
    555  widget::SizeConstraints constraints(devMinSize, devMaxSize, constraintsScale);
    556 
    557  // The sizes are in inner window sizes, so convert them into outer window
    558  // sizes. Use a size of (200, 200) as only the difference between the inner
    559  // and outer size is needed.
    560  const LayoutDeviceIntSize sizeDiff =
    561      aWidget->NormalSizeModeClientToWindowSizeDifference();
    562  if (constraints.mMinSize.width) {
    563    constraints.mMinSize.width += sizeDiff.width;
    564  }
    565  if (constraints.mMinSize.height) {
    566    constraints.mMinSize.height += sizeDiff.height;
    567  }
    568  if (constraints.mMaxSize.width != NS_MAXSIZE) {
    569    constraints.mMaxSize.width += sizeDiff.width;
    570  }
    571  if (constraints.mMaxSize.height != NS_MAXSIZE) {
    572    constraints.mMaxSize.height += sizeDiff.height;
    573  }
    574 
    575  aWidget->SetSizeConstraints(constraints);
    576 }
    577 
    578 void nsContainerFrame::DoInlineMinISize(const IntrinsicSizeInput& aInput,
    579                                        InlineMinISizeData* aData) {
    580  auto handleChildren = [&](auto frame, auto data) {
    581    for (nsIFrame* kid : frame->mFrames) {
    582      const IntrinsicSizeInput kidInput(aInput, kid->GetWritingMode(),
    583                                        GetWritingMode());
    584      kid->AddInlineMinISize(kidInput, data);
    585    }
    586  };
    587  DoInlineIntrinsicISize(aData, handleChildren);
    588 }
    589 
    590 void nsContainerFrame::DoInlinePrefISize(const IntrinsicSizeInput& aInput,
    591                                         InlinePrefISizeData* aData) {
    592  auto handleChildren = [&](auto frame, auto data) {
    593    for (nsIFrame* kid : frame->mFrames) {
    594      const IntrinsicSizeInput kidInput(aInput, kid->GetWritingMode(),
    595                                        GetWritingMode());
    596      kid->AddInlinePrefISize(kidInput, data);
    597    }
    598  };
    599  DoInlineIntrinsicISize(aData, handleChildren);
    600  aData->mLineIsEmpty = false;
    601 }
    602 
    603 /* virtual */
    604 LogicalSize nsContainerFrame::ComputeAutoSize(
    605    const SizeComputationInput& aSizingInput, WritingMode aWM,
    606    const LogicalSize& aCBSize, nscoord aAvailableISize,
    607    const LogicalSize& aMargin, const mozilla::LogicalSize& aBorderPadding,
    608    const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
    609  const bool isTableCaption = IsTableCaption();
    610  // Skip table caption, which requires special sizing - see bug 1109571.
    611  if (IsAbsolutelyPositionedWithDefiniteContainingBlock() && !isTableCaption) {
    612    return ComputeAbsolutePosAutoSize(aSizingInput, aWM, aCBSize,
    613                                      aAvailableISize, aMargin, aBorderPadding,
    614                                      aSizeOverrides, aFlags);
    615  }
    616  LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
    617  if (aFlags.contains(ComputeSizeFlag::ShrinkWrap)) {
    618    // Delegate to nsIFrame::ComputeAutoSize() for computing the shrink-wrapping
    619    // size.
    620    result = nsIFrame::ComputeAutoSize(aSizingInput, aWM, aCBSize,
    621                                       aAvailableISize, aMargin, aBorderPadding,
    622                                       aSizeOverrides, aFlags);
    623  } else {
    624    result.ISize(aWM) =
    625        aAvailableISize - aMargin.ISize(aWM) - aBorderPadding.ISize(aWM);
    626  }
    627 
    628  if (isTableCaption) {
    629    // If we're a container for font size inflation, then shrink
    630    // wrapping inside of us should not apply font size inflation.
    631    AutoMaybeDisableFontInflation an(this);
    632 
    633    WritingMode tableWM = GetParent()->GetWritingMode();
    634    const IntrinsicSizeInput input(
    635        aSizingInput.mRenderingContext,
    636        Some(aCBSize.ConvertTo(GetWritingMode(), aWM)), Nothing());
    637    if (aWM.IsOrthogonalTo(tableWM)) {
    638      // For an orthogonal caption on a block-dir side of the table, shrink-wrap
    639      // to min-isize.
    640      result.ISize(aWM) = GetMinISize(input);
    641    } else {
    642      // The outer frame constrains our available isize to the isize of
    643      // the table.  Grow if our min-isize is bigger than that, but not
    644      // larger than the containing block isize.  (It would really be nice
    645      // to transmit that information another way, so we could grow up to
    646      // the table's available isize, but that's harder.)
    647      nscoord min = GetMinISize(input);
    648      if (min > aCBSize.ISize(aWM)) {
    649        min = aCBSize.ISize(aWM);
    650      }
    651      if (min > result.ISize(aWM)) {
    652        result.ISize(aWM) = min;
    653      }
    654    }
    655  }
    656  return result;
    657 }
    658 
    659 void nsContainerFrame::ReflowChild(
    660    nsIFrame* aKidFrame, nsPresContext* aPresContext,
    661    ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput,
    662    const WritingMode& aWM, const LogicalPoint& aPos,
    663    const nsSize& aContainerSize, ReflowChildFlags aFlags,
    664    nsReflowStatus& aStatus, nsOverflowContinuationTracker* aTracker) {
    665  MOZ_ASSERT(aReflowInput.mFrame == aKidFrame, "bad reflow input");
    666  if (aWM.IsPhysicalRTL()) {
    667    NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
    668                 "ReflowChild with unconstrained container width!");
    669  }
    670  MOZ_ASSERT(aDesiredSize.InkOverflow() == nsRect(0, 0, 0, 0) &&
    671                 aDesiredSize.ScrollableOverflow() == nsRect(0, 0, 0, 0),
    672             "please reset the overflow areas before calling ReflowChild");
    673 
    674  // Position the child frame and its view if requested.
    675  if (ReflowChildFlags::NoMoveFrame !=
    676      (aFlags & ReflowChildFlags::NoMoveFrame)) {
    677    aKidFrame->SetPosition(aWM, aPos, aContainerSize);
    678  }
    679 
    680  // Reflow the child frame
    681  aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
    682 
    683  // If the child frame is complete, delete any next-in-flows,
    684  // but only if the NoDeleteNextInFlowChild flag isn't set.
    685  if (!aStatus.IsInlineBreakBefore() && aStatus.IsFullyComplete() &&
    686      !(aFlags & ReflowChildFlags::NoDeleteNextInFlowChild)) {
    687    if (nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow()) {
    688      // Remove all of the childs next-in-flows. Make sure that we ask
    689      // the right parent to do the removal (it's possible that the
    690      // parent is not this because we are executing pullup code)
    691      nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
    692      DestroyContext context(PresShell());
    693      kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, kidNextInFlow,
    694                                                        true);
    695    }
    696  }
    697 }
    698 
    699 // XXX temporary: hold on to a copy of the old physical version of
    700 //    ReflowChild so that we can convert callers incrementally.
    701 void nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
    702                                   nsPresContext* aPresContext,
    703                                   ReflowOutput& aDesiredSize,
    704                                   const ReflowInput& aReflowInput, nscoord aX,
    705                                   nscoord aY, ReflowChildFlags aFlags,
    706                                   nsReflowStatus& aStatus,
    707                                   nsOverflowContinuationTracker* aTracker) {
    708  MOZ_ASSERT(aReflowInput.mFrame == aKidFrame, "bad reflow input");
    709 
    710  // Position the child frame and its view if requested.
    711  if (ReflowChildFlags::NoMoveFrame !=
    712      (aFlags & ReflowChildFlags::NoMoveFrame)) {
    713    aKidFrame->SetPosition(nsPoint(aX, aY));
    714  }
    715 
    716  // Reflow the child frame
    717  aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
    718 
    719  // If the child frame is complete, delete any next-in-flows,
    720  // but only if the NoDeleteNextInFlowChild flag isn't set.
    721  if (aStatus.IsFullyComplete() &&
    722      !(aFlags & ReflowChildFlags::NoDeleteNextInFlowChild)) {
    723    if (nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow()) {
    724      // Remove all of the childs next-in-flows. Make sure that we ask
    725      // the right parent to do the removal (it's possible that the
    726      // parent is not this because we are executing pullup code)
    727      nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
    728      DestroyContext context(PresShell());
    729      kidNextInFlow->GetParent()->DeleteNextInFlowChild(context, kidNextInFlow,
    730                                                        true);
    731    }
    732  }
    733 }
    734 
    735 void nsContainerFrame::FinishReflowChild(
    736    nsIFrame* aKidFrame, nsPresContext* aPresContext,
    737    const ReflowOutput& aDesiredSize, const ReflowInput* aReflowInput,
    738    const WritingMode& aWM, const LogicalPoint& aPos,
    739    const nsSize& aContainerSize, nsIFrame::ReflowChildFlags aFlags) {
    740  MOZ_ASSERT(!aReflowInput || aReflowInput->mFrame == aKidFrame);
    741  MOZ_ASSERT(aReflowInput || aKidFrame->IsMathMLFrame() ||
    742                 aKidFrame->IsTableCellFrame(),
    743             "aReflowInput should be passed in almost all cases");
    744 
    745  if (aWM.IsPhysicalRTL()) {
    746    NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
    747                 "FinishReflowChild with unconstrained container width!");
    748  }
    749 
    750  const LogicalSize convertedSize = aDesiredSize.Size(aWM);
    751  LogicalPoint pos(aPos);
    752 
    753  if (aFlags & ReflowChildFlags::ApplyRelativePositioning) {
    754    MOZ_ASSERT(aReflowInput, "caller must have passed reflow input");
    755    // ApplyRelativePositioning in right-to-left writing modes needs to know
    756    // the updated frame width to set the normal position correctly.
    757    aKidFrame->SetSize(aWM, convertedSize);
    758 
    759    const LogicalMargin offsets = aReflowInput->ComputedLogicalOffsets(aWM);
    760    ReflowInput::ApplyRelativePositioning(aKidFrame, aWM, offsets, &pos,
    761                                          aContainerSize);
    762  }
    763 
    764  if (ReflowChildFlags::NoMoveFrame !=
    765      (aFlags & ReflowChildFlags::NoMoveFrame)) {
    766    aKidFrame->SetRect(aWM, LogicalRect(aWM, pos, convertedSize),
    767                       aContainerSize);
    768  } else {
    769    aKidFrame->SetSize(aWM, convertedSize);
    770  }
    771 
    772  aKidFrame->DidReflow(aPresContext, aReflowInput);
    773 }
    774 #if defined(_MSC_VER) && !defined(__clang__) && defined(_M_AMD64)
    775 #  pragma optimize("", on)
    776 #endif
    777 
    778 // XXX temporary: hold on to a copy of the old physical version of
    779 //    FinishReflowChild so that we can convert callers incrementally.
    780 void nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame,
    781                                         nsPresContext* aPresContext,
    782                                         const ReflowOutput& aDesiredSize,
    783                                         const ReflowInput* aReflowInput,
    784                                         nscoord aX, nscoord aY,
    785                                         ReflowChildFlags aFlags) {
    786  MOZ_ASSERT(!(aFlags & ReflowChildFlags::ApplyRelativePositioning),
    787             "only the logical version supports ApplyRelativePositioning "
    788             "since ApplyRelativePositioning requires the container size");
    789 
    790  nsPoint pos(aX, aY);
    791  nsSize size(aDesiredSize.PhysicalSize());
    792 
    793  if (ReflowChildFlags::NoMoveFrame !=
    794      (aFlags & ReflowChildFlags::NoMoveFrame)) {
    795    aKidFrame->SetRect(nsRect(pos, size));
    796  } else {
    797    aKidFrame->SetSize(size);
    798  }
    799 
    800  aKidFrame->DidReflow(aPresContext, aReflowInput);
    801 }
    802 
    803 void nsContainerFrame::FinishReflowWithAbsoluteFrames(
    804    nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
    805    const ReflowInput& aReflowInput, nsReflowStatus& aStatus) {
    806  ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
    807  FinishAndStoreOverflow(&aDesiredSize, aReflowInput.mStyleDisplay);
    808 }
    809 
    810 void nsContainerFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext,
    811                                            ReflowOutput& aDesiredSize,
    812                                            const ReflowInput& aReflowInput,
    813                                            nsReflowStatus& aStatus) {
    814  auto* absoluteContainer = GetAbsoluteContainingBlock();
    815  if (absoluteContainer && absoluteContainer->PrepareAbsoluteFrames(this)) {
    816    // The containing block for the abs pos kids is formed by our padding edge.
    817    const auto wm = GetWritingMode();
    818    LogicalRect cbRect(wm, LogicalPoint(wm), aDesiredSize.Size(wm));
    819    cbRect.Deflate(wm, GetLogicalUsedBorder(wm).ApplySkipSides(
    820                           PreReflowBlockLevelLogicalSkipSides()));
    821    // XXX: To optimize the performance, set the flags only when the CB width or
    822    // height actually changes.
    823    AbsPosReflowFlags flags{AbsPosReflowFlag::AllowFragmentation,
    824                            AbsPosReflowFlag::CBWidthChanged,
    825                            AbsPosReflowFlag::CBHeightChanged};
    826    absoluteContainer->Reflow(
    827        this, aPresContext, aReflowInput, aStatus,
    828        cbRect.GetPhysicalRect(wm, aDesiredSize.PhysicalSize()), flags,
    829        &aDesiredSize.mOverflowAreas);
    830  }
    831 }
    832 
    833 void nsContainerFrame::ReflowOverflowContainerChildren(
    834    nsPresContext* aPresContext, const ReflowInput& aReflowInput,
    835    OverflowAreas& aOverflowRects, ReflowChildFlags aFlags,
    836    nsReflowStatus& aStatus, ChildFrameMerger aMergeFunc,
    837    Maybe<nsSize> aContainerSize) {
    838  MOZ_ASSERT(aPresContext, "null pointer");
    839 
    840  nsFrameList* overflowContainers =
    841      DrainExcessOverflowContainersList(aMergeFunc);
    842  if (!overflowContainers) {
    843    return;  // nothing to reflow
    844  }
    845 
    846  nsOverflowContinuationTracker tracker(this, false, false);
    847  bool shouldReflowAllKids = aReflowInput.ShouldReflowAllKids();
    848 
    849  for (nsIFrame* frame : *overflowContainers) {
    850    if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) {
    851      // frame's prevInFlow has moved, skip reflowing this frame;
    852      // it will get reflowed once it's been placed
    853      if (GetNextInFlow()) {
    854        // We report OverflowIncomplete status in this case to avoid our parent
    855        // deleting our next-in-flows which might destroy non-empty frames.
    856        nsReflowStatus status;
    857        status.SetOverflowIncomplete();
    858        aStatus.MergeCompletionStatusFrom(status);
    859      }
    860      continue;
    861    }
    862 
    863    auto ScrollableOverflowExceedsAvailableBSize =
    864        [this, &aReflowInput](nsIFrame* aFrame) {
    865          if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
    866            return false;
    867          }
    868          const auto parentWM = GetWritingMode();
    869          const nscoord scrollableOverflowRectBEnd =
    870              LogicalRect(parentWM,
    871                          aFrame->ScrollableOverflowRectRelativeToParent(),
    872                          GetSize())
    873                  .BEnd(parentWM);
    874          return scrollableOverflowRectBEnd > aReflowInput.AvailableBSize();
    875        };
    876 
    877    // If the available block-size has changed, or the existing scrollable
    878    // overflow's block-end exceeds it, we need to reflow even if the frame
    879    // isn't dirty.
    880    if (shouldReflowAllKids || frame->IsSubtreeDirty() ||
    881        ScrollableOverflowExceedsAvailableBSize(frame)) {
    882      nsIFrame* prevInFlow = frame->GetPrevInFlow();
    883      NS_ASSERTION(prevInFlow,
    884                   "overflow container frame must have a prev-in-flow");
    885      NS_ASSERTION(
    886          frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
    887          "overflow container frame must have overflow container bit set");
    888      WritingMode wm = frame->GetWritingMode();
    889      // Note: aReflowInput's available inline-size is technically wrong for us
    890      // to hand off to children here, because it doesn't account for the space
    891      // that's been used for the container's margin/border/padding (and some
    892      // other space that a concrete container type, e.g. fieldset and grid [1],
    893      // might reserve before setting up the available space for their
    894      // children). Since we don't have a way to query the specific available
    895      // inline-size each container type used, nor do we know how the container
    896      // computes its non-overflow-container children's inline-size, we just
    897      // unconditionally override the frame's inline-size, so that the available
    898      // inline-size for the children doesn't really matter anyway.
    899      //
    900      // [1] For example, fieldset uses its computed inline-size with padding as
    901      // the available inline-size to reflow its inner child frame.
    902      // https://searchfox.org/mozilla-central/rev/04f7743d94691fa24212fb43099f9d84c3bfc890/layout/forms/nsFieldSetFrame.cpp#535-536
    903      const LogicalSize availSpace = aReflowInput.AvailableSize(wm);
    904 
    905      StyleSizeOverrides sizeOverride;
    906      // We override current continuation's inline-size by using the
    907      // prev-in-flow's inline-size since both should be the same.
    908      sizeOverride.mStyleISize.emplace(
    909          StyleSize::LengthPercentage(LengthPercentage::FromAppUnits(
    910              frame->StylePosition()->mBoxSizing == StyleBoxSizing::Border
    911                  ? prevInFlow->ISize(wm)
    912                  : prevInFlow->ContentISize(wm))));
    913 
    914      if (frame->IsFlexItem()) {
    915        // An overflow container's block-size must be 0.
    916        sizeOverride.mStyleBSize.emplace(
    917            StyleSize::LengthPercentage(LengthPercentage::FromAppUnits(0)));
    918      }
    919      ReflowOutput desiredSize(wm);
    920      ReflowInput reflowInput(aPresContext, aReflowInput, frame, availSpace,
    921                              Nothing(), {}, sizeOverride);
    922      const nsSize containerSize =
    923          aContainerSize ? *aContainerSize
    924                         : aReflowInput.AvailableSize(wm).GetPhysicalSize(wm);
    925      const LogicalPoint pos(wm, prevInFlow->IStart(wm, containerSize), 0);
    926      nsReflowStatus frameStatus;
    927 
    928      ReflowChild(frame, aPresContext, desiredSize, reflowInput, wm, pos,
    929                  containerSize, aFlags, frameStatus, &tracker);
    930      FinishReflowChild(frame, aPresContext, desiredSize, &reflowInput, wm, pos,
    931                        containerSize, aFlags);
    932 
    933      // Handle continuations
    934      if (!frameStatus.IsFullyComplete()) {
    935        if (frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
    936          // Abspos frames can't cause their parent to be incomplete,
    937          // only overflow incomplete.
    938          frameStatus.SetOverflowIncomplete();
    939        } else {
    940          NS_ASSERTION(frameStatus.IsComplete(),
    941                       "overflow container frames can't be incomplete, only "
    942                       "overflow-incomplete");
    943        }
    944 
    945        // Acquire a next-in-flow, creating it if necessary
    946        nsIFrame* nif = frame->GetNextInFlow();
    947        if (!nif) {
    948          NS_ASSERTION(frameStatus.NextInFlowNeedsReflow(),
    949                       "Someone forgot a NextInFlowNeedsReflow flag");
    950          nif = PresShell()->FrameConstructor()->CreateContinuingFrame(frame,
    951                                                                       this);
    952        } else if (!nif->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
    953          // used to be a normal next-in-flow; steal it from the child list
    954          nif->GetParent()->StealFrame(nif);
    955        }
    956 
    957        tracker.Insert(nif, frameStatus);
    958      }
    959      aStatus.MergeCompletionStatusFrom(frameStatus);
    960      // At this point it would be nice to assert
    961      // !frame->GetOverflowRect().IsEmpty(), but we have some unsplittable
    962      // frames that, when taller than availableHeight will push zero-height
    963      // content into a next-in-flow.
    964    } else {
    965      tracker.Skip(frame, aStatus);
    966      if (aReflowInput.mFloatManager) {
    967        nsBlockFrame::RecoverFloatsFor(frame, *aReflowInput.mFloatManager,
    968                                       aReflowInput.GetWritingMode(),
    969                                       aReflowInput.ComputedPhysicalSize());
    970      }
    971    }
    972    ConsiderChildOverflow(aOverflowRects, frame, /* aAsIfScrolled = */ false);
    973  }
    974 }
    975 
    976 void nsContainerFrame::DisplayOverflowContainers(
    977    nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
    978  if (nsFrameList* overflowconts = GetOverflowContainers()) {
    979    for (nsIFrame* frame : *overflowconts) {
    980      BuildDisplayListForChild(aBuilder, frame, aLists);
    981    }
    982  }
    983 }
    984 
    985 void nsContainerFrame::DisplayPushedAbsoluteFrames(
    986    nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
    987  for (nsIFrame* frame : GetChildList(FrameChildListID::Absolute)) {
    988    if (frame->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW)) {
    989      BuildDisplayListForChild(aBuilder, frame, aLists);
    990    }
    991  }
    992 }
    993 
    994 bool nsContainerFrame::TryRemoveFrame(FrameListPropertyDescriptor aProp,
    995                                      nsIFrame* aChildToRemove) {
    996  nsFrameList* list = GetProperty(aProp);
    997  if (list && list->StartRemoveFrame(aChildToRemove)) {
    998    // aChildToRemove *may* have been removed from this list.
    999    if (list->IsEmpty()) {
   1000      (void)TakeProperty(aProp);
   1001      list->Delete(PresShell());
   1002    }
   1003    return true;
   1004  }
   1005  return false;
   1006 }
   1007 
   1008 bool nsContainerFrame::MaybeStealOverflowContainerFrame(nsIFrame* aChild) {
   1009  bool removed = false;
   1010  if (MOZ_UNLIKELY(aChild->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER))) {
   1011    // Try removing from the overflow container list.
   1012    removed = TryRemoveFrame(OverflowContainersProperty(), aChild);
   1013    if (!removed) {
   1014      // It might be in the excess overflow container list.
   1015      removed = TryRemoveFrame(ExcessOverflowContainersProperty(), aChild);
   1016    }
   1017  }
   1018  return removed;
   1019 }
   1020 
   1021 void nsContainerFrame::StealFrame(nsIFrame* aChild) {
   1022 #ifdef DEBUG
   1023  if (!mFrames.ContainsFrame(aChild)) {
   1024    nsFrameList* list = GetOverflowFrames();
   1025    if (!list || !list->ContainsFrame(aChild)) {
   1026      list = GetOverflowContainers();
   1027      if (!list || !list->ContainsFrame(aChild)) {
   1028        list = GetExcessOverflowContainers();
   1029        MOZ_ASSERT(list && list->ContainsFrame(aChild),
   1030                   "aChild isn't our child"
   1031                   " or on a frame list not supported by StealFrame");
   1032      }
   1033    }
   1034  }
   1035 #endif
   1036 
   1037  if (MaybeStealOverflowContainerFrame(aChild)) {
   1038    return;
   1039  }
   1040 
   1041  // NOTE nsColumnSetFrame and nsCanvasFrame have their overflow containers
   1042  // on the normal lists so we might get here also if the frame bit
   1043  // NS_FRAME_IS_OVERFLOW_CONTAINER is set.
   1044  if (mFrames.StartRemoveFrame(aChild)) {
   1045    return;
   1046  }
   1047 
   1048  // We didn't find the child in our principal child list.
   1049  // Maybe it's on the overflow list?
   1050  nsFrameList* frameList = GetOverflowFrames();
   1051  if (frameList && frameList->ContinueRemoveFrame(aChild)) {
   1052    if (frameList->IsEmpty()) {
   1053      DestroyOverflowList();
   1054    }
   1055    return;
   1056  }
   1057 
   1058  MOZ_ASSERT_UNREACHABLE("StealFrame: can't find aChild");
   1059 }
   1060 
   1061 nsFrameList nsContainerFrame::StealFramesAfter(nsIFrame* aChild) {
   1062  NS_ASSERTION(
   1063      !aChild || !aChild->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
   1064      "StealFramesAfter doesn't handle overflow containers");
   1065  NS_ASSERTION(!IsBlockFrame(), "unexpected call");
   1066 
   1067  if (!aChild) {
   1068    return std::move(mFrames);
   1069  }
   1070 
   1071  for (nsIFrame* f : mFrames) {
   1072    if (f == aChild) {
   1073      return mFrames.TakeFramesAfter(f);
   1074    }
   1075  }
   1076 
   1077  // We didn't find the child in the principal child list.
   1078  // Maybe it's on the overflow list?
   1079  if (nsFrameList* overflowFrames = GetOverflowFrames()) {
   1080    for (nsIFrame* f : *overflowFrames) {
   1081      if (f == aChild) {
   1082        return mFrames.TakeFramesAfter(f);
   1083      }
   1084    }
   1085  }
   1086 
   1087  NS_ERROR("StealFramesAfter: can't find aChild");
   1088  return nsFrameList();
   1089 }
   1090 
   1091 /*
   1092 * Create a next-in-flow for aFrame. Will return the newly created
   1093 * frame <b>if and only if</b> a new frame is created; otherwise
   1094 * nullptr is returned.
   1095 */
   1096 nsIFrame* nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame) {
   1097  MOZ_ASSERT(
   1098      !IsBlockFrame(),
   1099      "you should have called nsBlockFrame::CreateContinuationFor instead");
   1100  MOZ_ASSERT(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame");
   1101 
   1102  nsIFrame* nextInFlow = aFrame->GetNextInFlow();
   1103  if (nullptr == nextInFlow) {
   1104    // Create a continuation frame for the child frame and insert it
   1105    // into our child list.
   1106    nextInFlow =
   1107        PresShell()->FrameConstructor()->CreateContinuingFrame(aFrame, this);
   1108    mFrames.InsertFrame(nullptr, aFrame, nextInFlow);
   1109 
   1110    NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES,
   1111                 ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p",
   1112                  aFrame, nextInFlow));
   1113 
   1114    return nextInFlow;
   1115  }
   1116  return nullptr;
   1117 }
   1118 
   1119 /**
   1120 * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and
   1121 * flow pointers
   1122 */
   1123 void nsContainerFrame::DeleteNextInFlowChild(DestroyContext& aContext,
   1124                                             nsIFrame* aNextInFlow,
   1125                                             bool aDeletingEmptyFrames) {
   1126 #ifdef DEBUG
   1127  nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow();
   1128 #endif
   1129  MOZ_ASSERT(prevInFlow, "bad prev-in-flow");
   1130 
   1131  // If the next-in-flow has a next-in-flow then delete it, too (and
   1132  // delete it first).
   1133  // Do this in a loop so we don't overflow the stack for frames
   1134  // with very many next-in-flows
   1135  nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow();
   1136  if (nextNextInFlow) {
   1137    AutoTArray<nsIFrame*, 8> frames;
   1138    for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) {
   1139      frames.AppendElement(f);
   1140    }
   1141    for (nsIFrame* delFrame : Reversed(frames)) {
   1142      nsContainerFrame* parent = delFrame->GetParent();
   1143      parent->DeleteNextInFlowChild(aContext, delFrame, aDeletingEmptyFrames);
   1144    }
   1145  }
   1146 
   1147  // Take the next-in-flow out of the parent's child list
   1148  StealFrame(aNextInFlow);
   1149 
   1150 #ifdef DEBUG
   1151  if (aDeletingEmptyFrames) {
   1152    nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
   1153  }
   1154 #endif
   1155 
   1156  // Delete the next-in-flow frame and its descendants. This will also
   1157  // remove it from its next-in-flow/prev-in-flow chain.
   1158  aNextInFlow->Destroy(aContext);
   1159 
   1160  MOZ_ASSERT(!prevInFlow->GetNextInFlow(), "non null next-in-flow");
   1161 }
   1162 
   1163 void nsContainerFrame::PushChildrenToOverflow(nsIFrame* aFromChild,
   1164                                              nsIFrame* aPrevSibling) {
   1165  MOZ_ASSERT(aFromChild, "null pointer");
   1166  MOZ_ASSERT(aPrevSibling, "pushing first child");
   1167  MOZ_ASSERT(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
   1168 
   1169  // Add the frames to our overflow list (let our next in flow drain
   1170  // our overflow list when it is ready)
   1171  SetOverflowFrames(mFrames.TakeFramesAfter(aPrevSibling));
   1172 }
   1173 
   1174 bool nsContainerFrame::PushIncompleteChildren(
   1175    const FrameHashtable& aPushedItems, const FrameHashtable& aIncompleteItems,
   1176    const FrameHashtable& aOverflowIncompleteItems) {
   1177  MOZ_ASSERT(IsFlexOrGridContainer(),
   1178             "Only Grid / Flex containers can call this!");
   1179 
   1180  if (aPushedItems.IsEmpty() && aIncompleteItems.IsEmpty() &&
   1181      aOverflowIncompleteItems.IsEmpty()) {
   1182    return false;
   1183  }
   1184 
   1185  // Iterate the children in normal document order and append them (or a NIF)
   1186  // to one of the following frame lists according to their status.
   1187  nsFrameList pushedList;
   1188  nsFrameList incompleteList;
   1189  nsFrameList overflowIncompleteList;
   1190  auto* fc = PresShell()->FrameConstructor();
   1191  for (nsIFrame* child = PrincipalChildList().FirstChild(); child;) {
   1192    MOZ_ASSERT((aPushedItems.Contains(child) ? 1 : 0) +
   1193                       (aIncompleteItems.Contains(child) ? 1 : 0) +
   1194                       (aOverflowIncompleteItems.Contains(child) ? 1 : 0) <=
   1195                   1,
   1196               "child should only be in one of these sets");
   1197    // Save the next-sibling so we can continue the loop if |child| is moved.
   1198    nsIFrame* next = child->GetNextSibling();
   1199    if (aPushedItems.Contains(child)) {
   1200      MOZ_ASSERT(child->GetParent() == this);
   1201      StealFrame(child);
   1202      pushedList.AppendFrame(nullptr, child);
   1203    } else if (aIncompleteItems.Contains(child)) {
   1204      nsIFrame* childNIF = child->GetNextInFlow();
   1205      if (!childNIF) {
   1206        childNIF = fc->CreateContinuingFrame(child, this);
   1207        incompleteList.AppendFrame(nullptr, childNIF);
   1208      } else {
   1209        auto* parent = childNIF->GetParent();
   1210        MOZ_ASSERT(parent != this || !mFrames.ContainsFrame(childNIF),
   1211                   "child's NIF shouldn't be in the same principal list");
   1212        // If child's existing NIF is an overflow container, convert it to an
   1213        // actual NIF, since now |child| has non-overflow stuff to give it.
   1214        // Or, if it's further away then our next-in-flow, then pull it up.
   1215        if (childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) ||
   1216            (parent != this && parent != GetNextInFlow())) {
   1217          parent->StealFrame(childNIF);
   1218          childNIF->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
   1219          if (parent == this) {
   1220            incompleteList.AppendFrame(nullptr, childNIF);
   1221          } else {
   1222            // If childNIF already lives on the next fragment, then we
   1223            // don't need to reparent it, since we know it's destined to end
   1224            // up there anyway.  Just move it to its parent's overflow list.
   1225            if (parent == GetNextInFlow()) {
   1226              nsFrameList toMove(childNIF, childNIF);
   1227              parent->MergeSortedOverflow(toMove);
   1228            } else {
   1229              ReparentFrame(childNIF, parent, this);
   1230              incompleteList.AppendFrame(nullptr, childNIF);
   1231            }
   1232          }
   1233        }
   1234      }
   1235    } else if (aOverflowIncompleteItems.Contains(child)) {
   1236      nsIFrame* childNIF = child->GetNextInFlow();
   1237      if (!childNIF) {
   1238        childNIF = fc->CreateContinuingFrame(child, this);
   1239        childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
   1240        overflowIncompleteList.AppendFrame(nullptr, childNIF);
   1241      } else {
   1242        DebugOnly<nsContainerFrame*> lastParent = this;
   1243        auto* nif = static_cast<nsContainerFrame*>(GetNextInFlow());
   1244        // If child has any non-overflow-container NIFs, convert them to
   1245        // overflow containers, since that's all |child| needs now.
   1246        while (childNIF &&
   1247               !childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   1248          auto* parent = childNIF->GetParent();
   1249          parent->StealFrame(childNIF);
   1250          childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
   1251          if (parent == this) {
   1252            overflowIncompleteList.AppendFrame(nullptr, childNIF);
   1253          } else {
   1254            if (!nif || parent == nif) {
   1255              nsFrameList toMove(childNIF, childNIF);
   1256              parent->MergeSortedExcessOverflowContainers(toMove);
   1257            } else {
   1258              ReparentFrame(childNIF, parent, nif);
   1259              nsFrameList toMove(childNIF, childNIF);
   1260              nif->MergeSortedExcessOverflowContainers(toMove);
   1261            }
   1262            // We only need to reparent the first childNIF (or not at all if
   1263            // its parent is our NIF).
   1264            nif = nullptr;
   1265          }
   1266          lastParent = parent;
   1267          childNIF = childNIF->GetNextInFlow();
   1268        }
   1269      }
   1270    }
   1271    child = next;
   1272  }
   1273 
   1274  // Merge the results into our respective overflow child lists.
   1275  if (!pushedList.IsEmpty()) {
   1276    MergeSortedOverflow(pushedList);
   1277  }
   1278  if (!incompleteList.IsEmpty()) {
   1279    MergeSortedOverflow(incompleteList);
   1280  }
   1281  if (!overflowIncompleteList.IsEmpty()) {
   1282    // If our next-in-flow already has overflow containers list, merge the
   1283    // overflowIncompleteList into that list. Otherwise, merge it into our
   1284    // excess overflow containers list, to be drained by our next-in-flow.
   1285    auto* nif = static_cast<nsContainerFrame*>(GetNextInFlow());
   1286    nsFrameList* oc = nif ? nif->GetOverflowContainers() : nullptr;
   1287    if (oc) {
   1288      ReparentFrames(overflowIncompleteList, this, nif);
   1289      MergeSortedFrameLists(*oc, overflowIncompleteList, GetContent());
   1290    } else {
   1291      MergeSortedExcessOverflowContainers(overflowIncompleteList);
   1292    }
   1293  }
   1294  return true;
   1295 }
   1296 
   1297 void nsContainerFrame::NormalizeChildLists() {
   1298  MOZ_ASSERT(IsFlexOrGridContainer(),
   1299             "Only Flex / Grid containers can call this!");
   1300 
   1301  // Note: the following description uses grid container as an example. Flex
   1302  // container is similar.
   1303  //
   1304  // First we gather child frames we should include in our reflow/placement,
   1305  // i.e. overflowed children from our prev-in-flow, and pushed first-in-flow
   1306  // children (that might now fit). It's important to note that these children
   1307  // can be in arbitrary order vis-a-vis the current children in our lists.
   1308  // E.g. grid items in the document order: A, B, C may be placed in the rows
   1309  // 3, 2, 1.  Assume each row goes in a separate grid container fragment,
   1310  // and we reflow the second fragment.  Now if C (in fragment 1) overflows,
   1311  // we can't just prepend it to our mFrames like we usually do because that
   1312  // would violate the document order invariant that other code depends on.
   1313  // Similarly if we pull up child A (from fragment 3) we can't just append
   1314  // that for the same reason.  Instead, we must sort these children into
   1315  // our child lists.  (The sorting is trivial given that both lists are
   1316  // already fully sorted individually - it's just a merge.)
   1317  //
   1318  // The invariants that we maintain are that each grid container child list
   1319  // is sorted in the normal document order at all times, but that children
   1320  // in different grid container continuations may be in arbitrary order.
   1321 
   1322  const auto didPushItemsBit = IsFlexContainerFrame()
   1323                                   ? NS_STATE_FLEX_DID_PUSH_ITEMS
   1324                                   : NS_STATE_GRID_DID_PUSH_ITEMS;
   1325  const auto hasChildNifBit = IsFlexContainerFrame()
   1326                                  ? NS_STATE_FLEX_HAS_CHILD_NIFS
   1327                                  : NS_STATE_GRID_HAS_CHILD_NIFS;
   1328 
   1329  auto* prevInFlow = static_cast<nsContainerFrame*>(GetPrevInFlow());
   1330  // Merge overflow frames from our prev-in-flow into our principal child list.
   1331  if (prevInFlow) {
   1332    AutoFrameListPtr overflow(PresContext(), prevInFlow->StealOverflowFrames());
   1333    if (overflow) {
   1334      ReparentFrames(*overflow, prevInFlow, this);
   1335      MergeSortedFrameLists(mFrames, *overflow, GetContent());
   1336 
   1337      // Move trailing next-in-flows into our overflow list.
   1338      nsFrameList continuations;
   1339      for (nsIFrame* f = mFrames.FirstChild(); f;) {
   1340        nsIFrame* next = f->GetNextSibling();
   1341        nsIFrame* pif = f->GetPrevInFlow();
   1342        if (pif && pif->GetParent() == this) {
   1343          mFrames.RemoveFrame(f);
   1344          continuations.AppendFrame(nullptr, f);
   1345        }
   1346        f = next;
   1347      }
   1348      MergeSortedOverflow(continuations);
   1349 
   1350      // Move prev-in-flow's excess overflow containers list into our own
   1351      // overflow containers list. If we already have an excess overflow
   1352      // containers list, any child in that list which doesn't have a
   1353      // prev-in-flow in this frame is also merged into our overflow container
   1354      // list.
   1355      nsFrameList* overflowContainers =
   1356          DrainExcessOverflowContainersList(MergeSortedFrameListsFor);
   1357 
   1358      // Move trailing OC next-in-flows into our excess overflow containers
   1359      // list.
   1360      if (overflowContainers) {
   1361        nsFrameList moveToEOC;
   1362        for (nsIFrame* f = overflowContainers->FirstChild(); f;) {
   1363          nsIFrame* next = f->GetNextSibling();
   1364          nsIFrame* pif = f->GetPrevInFlow();
   1365          if (pif && pif->GetParent() == this) {
   1366            overflowContainers->RemoveFrame(f);
   1367            moveToEOC.AppendFrame(nullptr, f);
   1368          }
   1369          f = next;
   1370        }
   1371        if (overflowContainers->IsEmpty()) {
   1372          DestroyOverflowContainers();
   1373        }
   1374        MergeSortedExcessOverflowContainers(moveToEOC);
   1375      }
   1376    }
   1377  }
   1378 
   1379  // For each item in aItems, pull up its next-in-flow (if any), and reparent it
   1380  // to our next-in-flow, unless its parent is already ourselves or our
   1381  // next-in-flow (to avoid leaving a hole there).
   1382  auto PullItemsNextInFlow = [this](const nsFrameList& aItems) {
   1383    auto* firstNIF = static_cast<nsContainerFrame*>(GetNextInFlow());
   1384    if (!firstNIF) {
   1385      return;
   1386    }
   1387    nsFrameList childNIFs;
   1388    nsFrameList childOCNIFs;
   1389    for (auto* child : aItems) {
   1390      if (auto* childNIF = child->GetNextInFlow()) {
   1391        if (auto* parent = childNIF->GetParent();
   1392            parent != this && parent != firstNIF) {
   1393          parent->StealFrame(childNIF);
   1394          ReparentFrame(childNIF, parent, firstNIF);
   1395          if (childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   1396            childOCNIFs.AppendFrame(nullptr, childNIF);
   1397          } else {
   1398            childNIFs.AppendFrame(nullptr, childNIF);
   1399          }
   1400        }
   1401      }
   1402    }
   1403    // Merge aItems' NIFs into our NIF's respective overflow child lists.
   1404    firstNIF->MergeSortedOverflow(childNIFs);
   1405    firstNIF->MergeSortedExcessOverflowContainers(childOCNIFs);
   1406  };
   1407 
   1408  // Merge our own overflow frames into our principal child list,
   1409  // except those that are a next-in-flow for one of our items.
   1410  DebugOnly<bool> foundOwnPushedChild = false;
   1411  {
   1412    nsFrameList* ourOverflow = GetOverflowFrames();
   1413    if (ourOverflow) {
   1414      nsFrameList items;
   1415      for (nsIFrame* f = ourOverflow->FirstChild(); f;) {
   1416        nsIFrame* next = f->GetNextSibling();
   1417        nsIFrame* pif = f->GetPrevInFlow();
   1418        if (!pif || pif->GetParent() != this) {
   1419          MOZ_ASSERT(f->GetParent() == this);
   1420          ourOverflow->RemoveFrame(f);
   1421          items.AppendFrame(nullptr, f);
   1422          if (!pif) {
   1423            foundOwnPushedChild = true;
   1424          }
   1425        }
   1426        f = next;
   1427      }
   1428 
   1429      if (ourOverflow->IsEmpty()) {
   1430        DestroyOverflowList();
   1431        ourOverflow = nullptr;
   1432      }
   1433      if (items.NotEmpty()) {
   1434        PullItemsNextInFlow(items);
   1435      }
   1436      MergeSortedFrameLists(mFrames, items, GetContent());
   1437    }
   1438  }
   1439 
   1440  // Push any child next-in-flows in our principal list to OverflowList.
   1441  if (HasAnyStateBits(hasChildNifBit)) {
   1442    nsFrameList framesToPush;
   1443    nsIFrame* firstChild = mFrames.FirstChild();
   1444    // Note that we potentially modify our mFrames list as we go.
   1445    for (auto* child = firstChild; child; child = child->GetNextSibling()) {
   1446      if (auto* childNIF = child->GetNextInFlow()) {
   1447        if (childNIF->GetParent() == this) {
   1448          for (auto* c = child->GetNextSibling(); c; c = c->GetNextSibling()) {
   1449            if (c == childNIF) {
   1450              // child's next-in-flow is in our principal child list, push it.
   1451              mFrames.RemoveFrame(childNIF);
   1452              framesToPush.AppendFrame(nullptr, childNIF);
   1453              break;
   1454            }
   1455          }
   1456        }
   1457      }
   1458    }
   1459    if (!framesToPush.IsEmpty()) {
   1460      MergeSortedOverflow(framesToPush);
   1461    }
   1462    RemoveStateBits(hasChildNifBit);
   1463  }
   1464 
   1465  // Pull up any first-in-flow children we might have pushed.
   1466  if (HasAnyStateBits(didPushItemsBit)) {
   1467    RemoveStateBits(didPushItemsBit);
   1468    nsFrameList items;
   1469    auto* nif = static_cast<nsContainerFrame*>(GetNextInFlow());
   1470    DebugOnly<bool> nifNeedPushedItem = false;
   1471    while (nif) {
   1472      nsFrameList nifItems;
   1473      for (nsIFrame* nifChild = nif->PrincipalChildList().FirstChild();
   1474           nifChild;) {
   1475        nsIFrame* next = nifChild->GetNextSibling();
   1476        if (!nifChild->GetPrevInFlow()) {
   1477          nif->StealFrame(nifChild);
   1478          ReparentFrame(nifChild, nif, this);
   1479          nifItems.AppendFrame(nullptr, nifChild);
   1480          nifNeedPushedItem = false;
   1481        }
   1482        nifChild = next;
   1483      }
   1484      MergeSortedFrameLists(items, nifItems, GetContent());
   1485 
   1486      if (!nif->HasAnyStateBits(didPushItemsBit)) {
   1487        MOZ_ASSERT(!nifNeedPushedItem || mDidPushItemsBitMayLie,
   1488                   "The state bit stored in didPushItemsBit lied!");
   1489        break;
   1490      }
   1491      nifNeedPushedItem = true;
   1492 
   1493      for (nsIFrame* nifChild =
   1494               nif->GetChildList(FrameChildListID::Overflow).FirstChild();
   1495           nifChild;) {
   1496        nsIFrame* next = nifChild->GetNextSibling();
   1497        if (!nifChild->GetPrevInFlow()) {
   1498          nif->StealFrame(nifChild);
   1499          ReparentFrame(nifChild, nif, this);
   1500          nifItems.AppendFrame(nullptr, nifChild);
   1501          nifNeedPushedItem = false;
   1502        }
   1503        nifChild = next;
   1504      }
   1505      MergeSortedFrameLists(items, nifItems, GetContent());
   1506 
   1507      nif->RemoveStateBits(didPushItemsBit);
   1508      nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow());
   1509      MOZ_ASSERT(nif || !nifNeedPushedItem || mDidPushItemsBitMayLie,
   1510                 "The state bit stored in didPushItemsBit lied!");
   1511    }
   1512 
   1513    if (!items.IsEmpty()) {
   1514      PullItemsNextInFlow(items);
   1515    }
   1516 
   1517    MOZ_ASSERT(
   1518        foundOwnPushedChild || !items.IsEmpty() || mDidPushItemsBitMayLie,
   1519        "The state bit stored in didPushItemsBit lied!");
   1520    MergeSortedFrameLists(mFrames, items, GetContent());
   1521  }
   1522 }
   1523 
   1524 void nsContainerFrame::NoteNewChildren(ChildListID aListID,
   1525                                       const nsFrameList& aFrameList) {
   1526  MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list");
   1527  MOZ_ASSERT(IsFlexOrGridContainer(),
   1528             "Only Flex / Grid containers can call this!");
   1529 
   1530  mozilla::PresShell* presShell = PresShell();
   1531  const auto didPushItemsBit = IsFlexContainerFrame()
   1532                                   ? NS_STATE_FLEX_DID_PUSH_ITEMS
   1533                                   : NS_STATE_GRID_DID_PUSH_ITEMS;
   1534  for (auto* pif = GetPrevInFlow(); pif; pif = pif->GetPrevInFlow()) {
   1535    pif->AddStateBits(didPushItemsBit);
   1536    presShell->FrameNeedsReflow(pif, IntrinsicDirty::FrameAndAncestors,
   1537                                NS_FRAME_IS_DIRTY);
   1538  }
   1539 }
   1540 
   1541 bool nsContainerFrame::MoveOverflowToChildList() {
   1542  bool result = false;
   1543 
   1544  // Check for an overflow list with our prev-in-flow
   1545  nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow();
   1546  if (nullptr != prevInFlow) {
   1547    AutoFrameListPtr prevOverflowFrames(PresContext(),
   1548                                        prevInFlow->StealOverflowFrames());
   1549    if (prevOverflowFrames) {
   1550      // Tables are special; they can have repeated header/footer
   1551      // frames on mFrames at this point.
   1552      NS_ASSERTION(mFrames.IsEmpty() || IsTableFrame(), "bad overflow list");
   1553      mFrames.AppendFrames(this, std::move(*prevOverflowFrames));
   1554      result = true;
   1555    }
   1556  }
   1557 
   1558  // It's also possible that we have an overflow list for ourselves.
   1559  return DrainSelfOverflowList() || result;
   1560 }
   1561 
   1562 void nsContainerFrame::MergeSortedOverflow(nsFrameList& aList) {
   1563  if (aList.IsEmpty()) {
   1564    return;
   1565  }
   1566  MOZ_ASSERT(
   1567      !aList.FirstChild()->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
   1568      "this is the wrong list to put this child frame");
   1569  MOZ_ASSERT(aList.FirstChild()->GetParent() == this);
   1570  nsFrameList* overflow = GetOverflowFrames();
   1571  if (overflow) {
   1572    MergeSortedFrameLists(*overflow, aList, GetContent());
   1573  } else {
   1574    SetOverflowFrames(std::move(aList));
   1575  }
   1576 }
   1577 
   1578 void nsContainerFrame::MergeSortedExcessOverflowContainers(nsFrameList& aList) {
   1579  if (aList.IsEmpty()) {
   1580    return;
   1581  }
   1582  MOZ_ASSERT(
   1583      aList.FirstChild()->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
   1584      "this is the wrong list to put this child frame");
   1585  MOZ_ASSERT(aList.FirstChild()->GetParent() == this);
   1586  if (nsFrameList* eoc = GetExcessOverflowContainers()) {
   1587    MergeSortedFrameLists(*eoc, aList, GetContent());
   1588  } else {
   1589    SetExcessOverflowContainers(std::move(aList));
   1590  }
   1591 }
   1592 
   1593 nsIFrame* nsContainerFrame::GetFirstNonAnonBoxInSubtree(nsIFrame* aFrame) {
   1594  while (aFrame) {
   1595    // If aFrame isn't an anonymous container, or it's text or such, then it'll
   1596    // do.
   1597    if (!aFrame->Style()->IsAnonBox() ||
   1598        nsCSSAnonBoxes::IsNonElement(aFrame->Style()->GetPseudoType())) {
   1599      break;
   1600    }
   1601 
   1602    // Otherwise, descend to its first child and repeat.
   1603 
   1604    // SPECIAL CASE: if we're dealing with an anonymous table, then it might
   1605    // be wrapping something non-anonymous in its col-group list
   1606    // (instead of its principal child list), so we have to look there.
   1607    // (Note: For anonymous tables that have a non-anon cell *and* a non-anon
   1608    // column, we'll always return the column. This is fine; we're really just
   1609    // looking for a handle to *anything* with a meaningful content node inside
   1610    // the table, for use in DOM comparisons to things outside of the table.)
   1611    if (MOZ_UNLIKELY(aFrame->IsTableFrame())) {
   1612      nsIFrame* colgroupDescendant = GetFirstNonAnonBoxInSubtree(
   1613          aFrame->GetChildList(FrameChildListID::ColGroup).FirstChild());
   1614      if (colgroupDescendant) {
   1615        return colgroupDescendant;
   1616      }
   1617    }
   1618 
   1619    // USUAL CASE: Descend to the first child in principal list.
   1620    aFrame = aFrame->PrincipalChildList().FirstChild();
   1621  }
   1622  return aFrame;
   1623 }
   1624 
   1625 /**
   1626 * Is aFrame1 a prev-continuation of aFrame2?
   1627 */
   1628 static bool IsPrevContinuationOf(nsIFrame* aFrame1, nsIFrame* aFrame2) {
   1629  nsIFrame* prev = aFrame2;
   1630  while ((prev = prev->GetPrevContinuation())) {
   1631    if (prev == aFrame1) {
   1632      return true;
   1633    }
   1634  }
   1635  return false;
   1636 }
   1637 
   1638 void nsContainerFrame::MergeSortedFrameLists(nsFrameList& aDest,
   1639                                             nsFrameList& aSrc,
   1640                                             nsIContent* aCommonAncestor) {
   1641  // Returns a frame whose DOM node can be used for the purpose of ordering
   1642  // aFrame among its sibling frames by DOM position. If aFrame is
   1643  // non-anonymous, this just returns aFrame itself. Otherwise, this returns the
   1644  // first non-anonymous descendant in aFrame's continuation chain.
   1645  auto FrameForDOMPositionComparison = [](nsIFrame* aFrame) {
   1646    if (!aFrame->Style()->IsAnonBox()) {
   1647      // The usual case.
   1648      return aFrame;
   1649    }
   1650 
   1651    // Walk the continuation chain from the start, and return the first
   1652    // non-anonymous descendant that we find.
   1653    for (nsIFrame* f = aFrame->FirstContinuation(); f;
   1654         f = f->GetNextContinuation()) {
   1655      if (nsIFrame* nonAnonBox = GetFirstNonAnonBoxInSubtree(f)) {
   1656        return nonAnonBox;
   1657      }
   1658    }
   1659 
   1660    MOZ_ASSERT_UNREACHABLE(
   1661        "Why is there no non-anonymous descendants in the continuation chain?");
   1662    return aFrame;
   1663  };
   1664 
   1665  nsIFrame* dest = aDest.FirstChild();
   1666  for (nsIFrame* src = aSrc.FirstChild(); src;) {
   1667    if (!dest) {
   1668      aDest.AppendFrames(nullptr, std::move(aSrc));
   1669      break;
   1670    }
   1671    nsIContent* srcContent = FrameForDOMPositionComparison(src)->GetContent();
   1672    nsIContent* destContent = FrameForDOMPositionComparison(dest)->GetContent();
   1673    int32_t result = nsContentUtils::CompareTreePosition<TreeKind::Flat>(
   1674        srcContent, destContent, aCommonAncestor);
   1675    if (MOZ_UNLIKELY(result == 0)) {
   1676      // NOTE: we get here when comparing ::before/::after for the same element.
   1677      if (MOZ_UNLIKELY(srcContent->IsGeneratedContentContainerForBefore())) {
   1678        if (MOZ_LIKELY(!destContent->IsGeneratedContentContainerForBefore()) ||
   1679            ::IsPrevContinuationOf(src, dest)) {
   1680          result = -1;
   1681        }
   1682      } else if (MOZ_UNLIKELY(
   1683                     srcContent->IsGeneratedContentContainerForAfter())) {
   1684        if (MOZ_UNLIKELY(destContent->IsGeneratedContentContainerForAfter()) &&
   1685            ::IsPrevContinuationOf(src, dest)) {
   1686          result = -1;
   1687        }
   1688      } else if (::IsPrevContinuationOf(src, dest)) {
   1689        result = -1;
   1690      }
   1691    }
   1692    if (result < 0) {
   1693      // src should come before dest
   1694      nsIFrame* next = src->GetNextSibling();
   1695      aSrc.RemoveFrame(src);
   1696      aDest.InsertFrame(nullptr, dest->GetPrevSibling(), src);
   1697      src = next;
   1698    } else {
   1699      dest = dest->GetNextSibling();
   1700    }
   1701  }
   1702  MOZ_ASSERT(aSrc.IsEmpty());
   1703 }
   1704 
   1705 bool nsContainerFrame::MoveInlineOverflowToChildList(nsIFrame* aLineContainer) {
   1706  MOZ_ASSERT(aLineContainer,
   1707             "Must have line container for moving inline overflows");
   1708 
   1709  bool result = false;
   1710 
   1711  // Check for an overflow list with our prev-in-flow
   1712  if (auto prevInFlow = static_cast<nsContainerFrame*>(GetPrevInFlow())) {
   1713    AutoFrameListPtr prevOverflowFrames(PresContext(),
   1714                                        prevInFlow->StealOverflowFrames());
   1715    if (prevOverflowFrames) {
   1716      // We may need to reparent floats from prev-in-flow to our line
   1717      // container if the container has prev continuation.
   1718      if (aLineContainer->GetPrevContinuation()) {
   1719        ReparentFloatsForInlineChild(aLineContainer,
   1720                                     prevOverflowFrames->FirstChild(), true);
   1721      }
   1722      // Prepend overflow frames to the list.
   1723      mFrames.InsertFrames(this, nullptr, std::move(*prevOverflowFrames));
   1724      result = true;
   1725    }
   1726  }
   1727 
   1728  // It's also possible that we have overflow list for ourselves.
   1729  return DrainSelfOverflowList() || result;
   1730 }
   1731 
   1732 bool nsContainerFrame::DrainSelfOverflowList() {
   1733  AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
   1734  if (overflowFrames) {
   1735    mFrames.AppendFrames(nullptr, std::move(*overflowFrames));
   1736    return true;
   1737  }
   1738  return false;
   1739 }
   1740 
   1741 bool nsContainerFrame::DrainAndMergeSelfOverflowList() {
   1742  MOZ_ASSERT(IsFlexOrGridContainer(),
   1743             "Only Flex / Grid containers can call this!");
   1744 
   1745  // Unlike nsContainerFrame::DrainSelfOverflowList, flex or grid containers
   1746  // need to merge these lists so that the resulting mFrames is in document
   1747  // content order.
   1748  // NOTE: nsContainerFrame::AppendFrames/InsertFrames calls this method and
   1749  // there are also direct calls from the fctor (FindAppendPrevSibling).
   1750  AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
   1751  if (overflowFrames) {
   1752    MergeSortedFrameLists(mFrames, *overflowFrames, GetContent());
   1753    // We set a frame bit to push them again in Reflow() to avoid creating
   1754    // multiple flex / grid items per flex / grid container fragment for the
   1755    // same content.
   1756    AddStateBits(IsFlexContainerFrame() ? NS_STATE_FLEX_HAS_CHILD_NIFS
   1757                                        : NS_STATE_GRID_HAS_CHILD_NIFS);
   1758    return true;
   1759  }
   1760  return false;
   1761 }
   1762 
   1763 nsFrameList* nsContainerFrame::DrainExcessOverflowContainersList(
   1764    ChildFrameMerger aMergeFunc) {
   1765  nsFrameList* overflowContainers = GetOverflowContainers();
   1766 
   1767  // Drain excess overflow containers from our prev-in-flow.
   1768  if (auto* prev = static_cast<nsContainerFrame*>(GetPrevInFlow())) {
   1769    AutoFrameListPtr excessFrames(PresContext(),
   1770                                  prev->StealExcessOverflowContainers());
   1771    if (excessFrames) {
   1772      excessFrames->ApplySetParent(this);
   1773      if (overflowContainers) {
   1774        // The default merge function is AppendFrames, so we use excessFrames as
   1775        // the destination and then assign the result to overflowContainers.
   1776        aMergeFunc(*excessFrames, *overflowContainers, this);
   1777        *overflowContainers = std::move(*excessFrames);
   1778      } else {
   1779        overflowContainers = SetOverflowContainers(std::move(*excessFrames));
   1780      }
   1781    }
   1782  }
   1783 
   1784  // Our own excess overflow containers from a previous reflow can still be
   1785  // present if our next-in-flow hasn't been reflown yet.  Move any children
   1786  // from it that don't have a continuation in this frame to the
   1787  // OverflowContainers list.
   1788  AutoFrameListPtr selfExcessOCFrames(PresContext(),
   1789                                      StealExcessOverflowContainers());
   1790  if (selfExcessOCFrames) {
   1791    nsFrameList toMove;
   1792    auto child = selfExcessOCFrames->FirstChild();
   1793    while (child) {
   1794      auto next = child->GetNextSibling();
   1795      MOZ_ASSERT(child->GetPrevInFlow(),
   1796                 "ExcessOverflowContainers frames must be continuations");
   1797      if (child->GetPrevInFlow()->GetParent() != this) {
   1798        selfExcessOCFrames->RemoveFrame(child);
   1799        toMove.AppendFrame(nullptr, child);
   1800      }
   1801      child = next;
   1802    }
   1803 
   1804    // If there's any remaining excess overflow containers, put them back.
   1805    if (selfExcessOCFrames->NotEmpty()) {
   1806      SetExcessOverflowContainers(std::move(*selfExcessOCFrames));
   1807    }
   1808 
   1809    if (toMove.NotEmpty()) {
   1810      if (overflowContainers) {
   1811        aMergeFunc(*overflowContainers, toMove, this);
   1812      } else {
   1813        overflowContainers = SetOverflowContainers(std::move(toMove));
   1814      }
   1815    }
   1816  }
   1817 
   1818  return overflowContainers;
   1819 }
   1820 
   1821 nsIFrame* nsContainerFrame::GetNextInFlowChild(
   1822    ContinuationTraversingState& aState, bool* aIsInOverflow) {
   1823  nsContainerFrame*& nextInFlow = aState.mNextInFlow;
   1824  while (nextInFlow) {
   1825    // See if there is any frame in the container
   1826    nsIFrame* frame = nextInFlow->mFrames.FirstChild();
   1827    if (frame) {
   1828      if (aIsInOverflow) {
   1829        *aIsInOverflow = false;
   1830      }
   1831      return frame;
   1832    }
   1833    // No frames in the principal list, try its overflow list
   1834    nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
   1835    if (overflowFrames) {
   1836      if (aIsInOverflow) {
   1837        *aIsInOverflow = true;
   1838      }
   1839      return overflowFrames->FirstChild();
   1840    }
   1841    nextInFlow = static_cast<nsContainerFrame*>(nextInFlow->GetNextInFlow());
   1842  }
   1843  return nullptr;
   1844 }
   1845 
   1846 nsIFrame* nsContainerFrame::PullNextInFlowChild(
   1847    ContinuationTraversingState& aState) {
   1848  bool isInOverflow;
   1849  nsIFrame* frame = GetNextInFlowChild(aState, &isInOverflow);
   1850  if (frame) {
   1851    nsContainerFrame* nextInFlow = aState.mNextInFlow;
   1852    if (isInOverflow) {
   1853      nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
   1854      overflowFrames->RemoveFirstChild();
   1855      if (overflowFrames->IsEmpty()) {
   1856        nextInFlow->DestroyOverflowList();
   1857      }
   1858    } else {
   1859      nextInFlow->mFrames.RemoveFirstChild();
   1860    }
   1861 
   1862    // Move the frame to the principal frame list of this container
   1863    mFrames.AppendFrame(this, frame);
   1864  }
   1865  return frame;
   1866 }
   1867 
   1868 /* static */
   1869 void nsContainerFrame::ReparentFloatsForInlineChild(nsIFrame* aOurLineContainer,
   1870                                                    nsIFrame* aFrame,
   1871                                                    bool aReparentSiblings) {
   1872  // XXXbz this would be better if it took a nsFrameList or a frame
   1873  // list slice....
   1874  NS_ASSERTION(aOurLineContainer->GetNextContinuation() ||
   1875                   aOurLineContainer->GetPrevContinuation(),
   1876               "Don't call this when we have no continuation, it's a waste");
   1877  if (!aFrame) {
   1878    NS_ASSERTION(aReparentSiblings, "Why did we get called?");
   1879    return;
   1880  }
   1881 
   1882  nsBlockFrame* frameBlock = nsLayoutUtils::GetFloatContainingBlock(aFrame);
   1883  if (!frameBlock || frameBlock == aOurLineContainer) {
   1884    return;
   1885  }
   1886 
   1887  nsBlockFrame* ourBlock = do_QueryFrame(aOurLineContainer);
   1888  NS_ASSERTION(ourBlock, "Not a block, but broke vertically?");
   1889 
   1890  while (true) {
   1891    ourBlock->ReparentFloats(aFrame, frameBlock, false);
   1892 
   1893    if (!aReparentSiblings) {
   1894      return;
   1895    }
   1896    nsIFrame* next = aFrame->GetNextSibling();
   1897    if (!next) {
   1898      return;
   1899    }
   1900    if (next->GetParent() == aFrame->GetParent()) {
   1901      aFrame = next;
   1902      continue;
   1903    }
   1904    // This is paranoid and will hardly ever get hit ... but we can't actually
   1905    // trust that the frames in the sibling chain all have the same parent,
   1906    // because lazy reparenting may be going on. If we find a different
   1907    // parent we need to redo our analysis.
   1908    ReparentFloatsForInlineChild(aOurLineContainer, next, aReparentSiblings);
   1909    return;
   1910  }
   1911 }
   1912 
   1913 bool nsContainerFrame::ResolvedOrientationIsVertical() const {
   1914  StyleOrient orient = StyleDisplay()->mOrient;
   1915  switch (orient) {
   1916    case StyleOrient::Horizontal:
   1917      return false;
   1918    case StyleOrient::Vertical:
   1919      return true;
   1920    case StyleOrient::Inline:
   1921      return GetWritingMode().IsVertical();
   1922    case StyleOrient::Block:
   1923      return !GetWritingMode().IsVertical();
   1924  }
   1925  MOZ_ASSERT_UNREACHABLE("unexpected -moz-orient value");
   1926  return false;
   1927 }
   1928 
   1929 LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions(
   1930    gfxContext* aRenderingContext, WritingMode aWM,
   1931    const IntrinsicSize& aIntrinsicSize, const AspectRatio& aAspectRatio,
   1932    const LogicalSize& aCBSize, const LogicalSize& aMargin,
   1933    const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
   1934    ComputeSizeFlags aFlags) {
   1935  const nsStylePosition* stylePos = StylePosition();
   1936  const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   1937  const auto styleISize =
   1938      aSizeOverrides.mStyleISize
   1939          ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
   1940          : stylePos->ISize(aWM, anchorResolutionParams);
   1941 
   1942  const auto styleBSize = [&] {
   1943    auto styleBSizeConsideringOverrides =
   1944        aSizeOverrides.mStyleBSize
   1945            ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize)
   1946            : stylePos->BSize(aWM, anchorResolutionParams);
   1947    if (styleBSizeConsideringOverrides->BehavesLikeStretchOnBlockAxis() &&
   1948        aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
   1949      // We've got a 'stretch' BSize; resolve it to a length:
   1950      nscoord stretchBSize = nsLayoutUtils::ComputeStretchBSize(
   1951          aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
   1952          stylePos->mBoxSizing);
   1953      // Note(dshin): This allocates.
   1954      return AnchorResolvedSizeHelper::LengthPercentage(
   1955          LengthPercentage::FromAppUnits(stretchBSize));
   1956    }
   1957    return styleBSizeConsideringOverrides;
   1958  }();
   1959 
   1960  const auto& aspectRatio =
   1961      aSizeOverrides.mAspectRatio ? *aSizeOverrides.mAspectRatio : aAspectRatio;
   1962 
   1963  auto* parentFrame = GetParent();
   1964  const bool isGridItem = IsGridItem();
   1965 
   1966  // flexItemMainAxis is set if this frame is a flex item in a modern flexbox
   1967  // layout. It indicates which logical axis (in this frame's own WM)
   1968  // corresponds to its flex container's main axis.
   1969  Maybe<LogicalAxis> flexItemMainAxis;
   1970  if (IsFlexItem() && !parentFrame->HasAnyStateBits(
   1971                          NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX)) {
   1972    flexItemMainAxis = Some(nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
   1973                                ? LogicalAxis::Inline
   1974                                : LogicalAxis::Block);
   1975  }
   1976 
   1977  // Handle intrinsic sizes and their interaction with
   1978  // {min-,max-,}{width,height} according to the rules in
   1979  // https://www.w3.org/TR/CSS22/visudet.html#min-max-widths and
   1980  // https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes
   1981 
   1982  // Note: throughout the following section of the function, I avoid
   1983  // a * (b / c) because of its reduced accuracy relative to a * b / c
   1984  // or (a * b) / c (which are equivalent).
   1985 
   1986  const bool isAutoOrMaxContentISize =
   1987      styleISize->IsAuto() || styleISize->IsMaxContent();
   1988  const bool isAutoBSize =
   1989      nsLayoutUtils::IsAutoBSize(*styleBSize, aCBSize.BSize(aWM));
   1990 
   1991  const auto boxSizingAdjust = stylePos->mBoxSizing == StyleBoxSizing::Border
   1992                                   ? aBorderPadding
   1993                                   : LogicalSize(aWM);
   1994  const nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) +
   1995                                             aBorderPadding.ISize(aWM) -
   1996                                             boxSizingAdjust.ISize(aWM);
   1997 
   1998  // We don't expect these intial values of iSize/bSize to be used, but this
   1999  // silences a GCC warning about them being uninitialized.
   2000  nscoord minISize, maxISize, minBSize, maxBSize, iSize = 0, bSize = 0;
   2001  enum class FillCB {
   2002    // No stretching or clamping in the relevant axis.
   2003    No,
   2004    // Stretch to fill the containing block size in the relevant axis while
   2005    // preserving the aspect-ratio. If both axes are stretched, the aspect-ratio
   2006    // will be disregarded.
   2007    Stretch,
   2008    // Clamp to fill the containing block size in the relevant axis while
   2009    // preserving the aspect-ratio. If both axes are clamped, the aspect-ratio
   2010    // is respected, and the dimensions do not overflow the containing block
   2011    // size.
   2012    Clamp,
   2013  };
   2014 
   2015  FillCB inlineFillCB = FillCB::No;  // fill CB behavior in the inline axis
   2016  FillCB blockFillCB = FillCB::No;   // fill CB behavior in the block axis
   2017 
   2018  const LogicalSize fallbackIntrinsicSize(aWM, kFallbackIntrinsicSize);
   2019  const Maybe<nscoord>& maybeIntrinsicISize = aIntrinsicSize.ISize(aWM);
   2020  const bool hasIntrinsicISize = maybeIntrinsicISize.isSome();
   2021  nscoord intrinsicISize = std::max(0, maybeIntrinsicISize.valueOr(0));
   2022 
   2023  const auto& maybeIntrinsicBSize = aIntrinsicSize.BSize(aWM);
   2024  const bool hasIntrinsicBSize = maybeIntrinsicBSize.isSome();
   2025  nscoord intrinsicBSize = std::max(0, maybeIntrinsicBSize.valueOr(0));
   2026 
   2027  Maybe<nscoord> iSizeToFillCB;
   2028  Maybe<nscoord> bSizeToFillCB;
   2029  if (!isAutoOrMaxContentISize) {
   2030    iSize = ComputeISizeValue(aRenderingContext, aWM, aCBSize, boxSizingAdjust,
   2031                              boxSizingToMarginEdgeISize, *styleISize,
   2032                              *styleBSize, aspectRatio, aFlags)
   2033                .mISize;
   2034  } else if (MOZ_UNLIKELY(isGridItem) &&
   2035             !parentFrame->IsMasonry(aWM, LogicalAxis::Inline)) {
   2036    MOZ_ASSERT(!IsTrueOverflowContainer());
   2037    // 'auto' inline-size for grid-level box - apply 'stretch' as needed:
   2038    auto cbSize = aCBSize.ISize(aWM);
   2039    if (cbSize != NS_UNCONSTRAINEDSIZE) {
   2040      if (!StyleMargin()->HasInlineAxisAuto(
   2041              aWM, AnchorPosResolutionParams::From(this))) {
   2042        auto inlineAxisAlignment = stylePos->UsedSelfAlignment(
   2043            aWM, LogicalAxis::Inline, parentFrame->GetWritingMode(),
   2044            parentFrame->Style());
   2045        if (inlineAxisAlignment == StyleAlignFlags::STRETCH) {
   2046          inlineFillCB = FillCB::Stretch;
   2047        }
   2048      }
   2049      if (inlineFillCB != FillCB::No ||
   2050          aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
   2051        iSizeToFillCB.emplace(std::max(
   2052            0, cbSize - aBorderPadding.ISize(aWM) - aMargin.ISize(aWM)));
   2053      }
   2054    } else {
   2055      // Reset this flag to avoid applying the clamping below.
   2056      aFlags -= ComputeSizeFlag::IClampMarginBoxMinSize;
   2057    }
   2058  }
   2059 
   2060  // Flex items ignore their min & max sizing properties in their flex
   2061  // container's main-axis. (Those properties get applied later in the flexbox
   2062  // algorithm.)
   2063  const bool isFlexItemInlineAxisMainAxis =
   2064      flexItemMainAxis && *flexItemMainAxis == LogicalAxis::Inline;
   2065  const auto maxISizeCoord = stylePos->MaxISize(aWM, anchorResolutionParams);
   2066  if (!maxISizeCoord->IsNone() && !isFlexItemInlineAxisMainAxis) {
   2067    maxISize =
   2068        ComputeISizeValue(aRenderingContext, aWM, aCBSize, boxSizingAdjust,
   2069                          boxSizingToMarginEdgeISize, *maxISizeCoord,
   2070                          *styleBSize, aspectRatio, aFlags)
   2071            .mISize;
   2072  } else {
   2073    maxISize = nscoord_MAX;
   2074  }
   2075 
   2076  const auto minISizeCoord = stylePos->MinISize(aWM, anchorResolutionParams);
   2077  if (!minISizeCoord->IsAuto() && !isFlexItemInlineAxisMainAxis) {
   2078    minISize =
   2079        ComputeISizeValue(aRenderingContext, aWM, aCBSize, boxSizingAdjust,
   2080                          boxSizingToMarginEdgeISize, *minISizeCoord,
   2081                          *styleBSize, aspectRatio, aFlags)
   2082            .mISize;
   2083  } else {
   2084    // Treat "min-width: auto" as 0.
   2085    // NOTE: Technically, "auto" is supposed to behave like "min-content" on
   2086    // flex items. However, we don't need to worry about that here, because
   2087    // flex items' min-sizes are intentionally ignored until the flex
   2088    // container explicitly considers them during space distribution.
   2089    minISize = 0;
   2090  }
   2091 
   2092  if (!isAutoBSize) {
   2093    bSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
   2094        aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
   2095        boxSizingAdjust.BSize(aWM), *styleBSize);
   2096  } else if (MOZ_UNLIKELY(isGridItem) &&
   2097             !parentFrame->IsMasonry(aWM, LogicalAxis::Block)) {
   2098    MOZ_ASSERT(!IsTrueOverflowContainer());
   2099    // 'auto' block-size for grid-level box - apply 'stretch' as needed:
   2100    auto cbSize = aCBSize.BSize(aWM);
   2101    if (cbSize != NS_UNCONSTRAINEDSIZE) {
   2102      if (!StyleMargin()->HasBlockAxisAuto(
   2103              aWM, AnchorPosResolutionParams::From(this))) {
   2104        auto blockAxisAlignment = stylePos->UsedSelfAlignment(
   2105            aWM, LogicalAxis::Block, parentFrame->GetWritingMode(),
   2106            parentFrame->Style());
   2107        if (blockAxisAlignment == StyleAlignFlags::STRETCH) {
   2108          blockFillCB = FillCB::Stretch;
   2109        }
   2110      }
   2111      if (blockFillCB != FillCB::No ||
   2112          aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize)) {
   2113        bSizeToFillCB.emplace(std::max(
   2114            0, cbSize - aBorderPadding.BSize(aWM) - aMargin.BSize(aWM)));
   2115      }
   2116    } else {
   2117      // Reset this flag to avoid applying the clamping below.
   2118      aFlags -= ComputeSizeFlag::BClampMarginBoxMinSize;
   2119    }
   2120  }
   2121 
   2122  // Flex items ignore their min & max sizing properties in their flex
   2123  // container's main-axis. (Those properties get applied later in the flexbox
   2124  // algorithm.)
   2125  const bool isFlexItemBlockAxisMainAxis =
   2126      flexItemMainAxis && *flexItemMainAxis == LogicalAxis::Block;
   2127  const auto maxBSizeCoord = stylePos->MaxBSize(aWM, anchorResolutionParams);
   2128  if (!nsLayoutUtils::IsAutoBSize(*maxBSizeCoord, aCBSize.BSize(aWM)) &&
   2129      !isFlexItemBlockAxisMainAxis) {
   2130    maxBSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
   2131        aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
   2132        boxSizingAdjust.BSize(aWM), *maxBSizeCoord);
   2133  } else {
   2134    maxBSize = nscoord_MAX;
   2135  }
   2136 
   2137  const auto minBSizeCoord = stylePos->MinBSize(aWM, anchorResolutionParams);
   2138  if (!nsLayoutUtils::IsAutoBSize(*minBSizeCoord, aCBSize.BSize(aWM)) &&
   2139      !isFlexItemBlockAxisMainAxis) {
   2140    minBSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
   2141        aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
   2142        boxSizingAdjust.BSize(aWM), *minBSizeCoord);
   2143  } else {
   2144    minBSize = 0;
   2145  }
   2146 
   2147  NS_ASSERTION(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,
   2148               "Our containing block must not have unconstrained inline-size!");
   2149  MOZ_ASSERT(!(inlineFillCB != FillCB::No ||
   2150               aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) ||
   2151                 iSizeToFillCB,
   2152             "iSizeToFillCB must be valid when stretching or clamping in the "
   2153             "inline axis!");
   2154  MOZ_ASSERT(!(blockFillCB != FillCB::No ||
   2155               aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize)) ||
   2156                 bSizeToFillCB,
   2157             "bSizeToFillCB must be valid when stretching or clamping in the "
   2158             "block axis!");
   2159 
   2160  // Now calculate the used values for iSize and bSize:
   2161  if (isAutoOrMaxContentISize) {
   2162    if (isAutoBSize) {
   2163      // 'auto' iSize, 'auto' bSize
   2164 
   2165      // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2:
   2166 
   2167      nscoord tentISize, tentBSize;
   2168 
   2169      if (hasIntrinsicISize) {
   2170        tentISize = intrinsicISize;
   2171      } else if (hasIntrinsicBSize && aspectRatio) {
   2172        tentISize = aspectRatio.ComputeRatioDependentSize(
   2173            LogicalAxis::Inline, aWM, intrinsicBSize, boxSizingAdjust);
   2174      } else if (aspectRatio) {
   2175        tentISize =
   2176            aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) - aMargin.ISize(aWM);
   2177        if (tentISize < 0) {
   2178          tentISize = 0;
   2179        }
   2180      } else {
   2181        tentISize = fallbackIntrinsicSize.ISize(aWM);
   2182      }
   2183 
   2184      // If we need to clamp the inline size to fit the CB, we use the 'stretch'
   2185      // or 'normal' codepath.  We use the ratio-preserving 'normal' codepath
   2186      // unless we have 'stretch' in the other axis.
   2187      if (aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize) &&
   2188          inlineFillCB != FillCB::Stretch && tentISize > *iSizeToFillCB) {
   2189        inlineFillCB =
   2190            (blockFillCB == FillCB::Stretch ? FillCB::Stretch : FillCB::Clamp);
   2191      }
   2192 
   2193      if (hasIntrinsicBSize) {
   2194        tentBSize = intrinsicBSize;
   2195      } else if (aspectRatio) {
   2196        tentBSize = aspectRatio.ComputeRatioDependentSize(
   2197            LogicalAxis::Block, aWM, tentISize, boxSizingAdjust);
   2198      } else {
   2199        tentBSize = fallbackIntrinsicSize.BSize(aWM);
   2200      }
   2201 
   2202      // (ditto the comment about clamping the inline size above)
   2203      if (aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize) &&
   2204          blockFillCB != FillCB::Stretch && tentBSize > *bSizeToFillCB) {
   2205        blockFillCB =
   2206            (inlineFillCB == FillCB::Stretch ? FillCB::Stretch : FillCB::Clamp);
   2207      }
   2208 
   2209      // The slash notation is the used value of "align-self / justify-self".
   2210      if (inlineFillCB == FillCB::Stretch) {
   2211        tentISize = *iSizeToFillCB;  // * / 'stretch'
   2212        if (blockFillCB == FillCB::Stretch) {
   2213          tentBSize = *bSizeToFillCB;  // 'stretch' / 'stretch'
   2214        } else if (aspectRatio) {
   2215          // * (except 'stretch') / 'stretch'
   2216          tentBSize = aspectRatio.ComputeRatioDependentSize(
   2217              LogicalAxis::Block, aWM, *iSizeToFillCB, boxSizingAdjust);
   2218        }
   2219      } else if (blockFillCB == FillCB::Stretch) {
   2220        tentBSize = *bSizeToFillCB;  // 'stretch' / * (except 'stretch')
   2221        if (aspectRatio) {
   2222          tentISize = aspectRatio.ComputeRatioDependentSize(
   2223              LogicalAxis::Inline, aWM, *bSizeToFillCB, boxSizingAdjust);
   2224        }
   2225      } else if (inlineFillCB == FillCB::Clamp && aspectRatio) {
   2226        tentISize = *iSizeToFillCB;  // * (except 'stretch') / 'normal'
   2227        tentBSize = aspectRatio.ComputeRatioDependentSize(
   2228            LogicalAxis::Block, aWM, *iSizeToFillCB, boxSizingAdjust);
   2229        if (blockFillCB == FillCB::Clamp && tentBSize > *bSizeToFillCB) {
   2230          // Clamp within the CB size with preserved aspect ratio.
   2231          tentBSize = *bSizeToFillCB;  // 'normal' / 'normal'
   2232          tentISize = aspectRatio.ComputeRatioDependentSize(
   2233              LogicalAxis::Inline, aWM, *bSizeToFillCB, boxSizingAdjust);
   2234        }
   2235      } else if (blockFillCB == FillCB::Clamp && aspectRatio) {
   2236        // 'normal' / * (except 'normal' and 'stretch')
   2237        tentBSize = *bSizeToFillCB;
   2238        tentISize = aspectRatio.ComputeRatioDependentSize(
   2239            LogicalAxis::Inline, aWM, *bSizeToFillCB, boxSizingAdjust);
   2240      }
   2241 
   2242      // ComputeAutoSizeWithIntrinsicDimensions preserves the ratio when
   2243      // applying the min/max-size.  We don't want that when we have 'stretch'
   2244      // in either axis because tentISize/tentBSize is likely not according to
   2245      // ratio now.
   2246      if (aspectRatio && inlineFillCB != FillCB::Stretch &&
   2247          blockFillCB != FillCB::Stretch) {
   2248        nsSize autoSize = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(
   2249            minISize, minBSize, maxISize, maxBSize, tentISize, tentBSize);
   2250        // The nsSize that ComputeAutoSizeWithIntrinsicDimensions returns will
   2251        // actually contain logical values if the parameters passed to it were
   2252        // logical coordinates, so we do NOT perform a physical-to-logical
   2253        // conversion here, but just assign the fields directly to our result.
   2254        iSize = autoSize.width;
   2255        bSize = autoSize.height;
   2256      } else {
   2257        // Not honoring an intrinsic ratio: clamp the dimensions independently.
   2258        iSize = CSSMinMax(tentISize, minISize, maxISize);
   2259        bSize = CSSMinMax(tentBSize, minBSize, maxBSize);
   2260      }
   2261    } else {
   2262      // 'auto' iSize, non-'auto' bSize
   2263      bSize = CSSMinMax(bSize, minBSize, maxBSize);
   2264      if (inlineFillCB == FillCB::Stretch) {
   2265        iSize = *iSizeToFillCB;
   2266      } else if (aspectRatio) {
   2267        iSize = aspectRatio.ComputeRatioDependentSize(LogicalAxis::Inline, aWM,
   2268                                                      bSize, boxSizingAdjust);
   2269      } else if (hasIntrinsicISize) {
   2270        iSize = aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize) &&
   2271                        intrinsicISize > *iSizeToFillCB
   2272                    ? *iSizeToFillCB
   2273                    : intrinsicISize;
   2274      } else {
   2275        iSize = fallbackIntrinsicSize.ISize(aWM);
   2276      }
   2277      iSize = CSSMinMax(iSize, minISize, maxISize);
   2278    }
   2279  } else {
   2280    if (isAutoBSize) {
   2281      // non-'auto' iSize, 'auto' bSize
   2282      iSize = CSSMinMax(iSize, minISize, maxISize);
   2283      if (blockFillCB == FillCB::Stretch) {
   2284        bSize = *bSizeToFillCB;
   2285      } else if (aspectRatio) {
   2286        bSize = aspectRatio.ComputeRatioDependentSize(LogicalAxis::Block, aWM,
   2287                                                      iSize, boxSizingAdjust);
   2288      } else if (hasIntrinsicBSize) {
   2289        bSize = aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize) &&
   2290                        intrinsicBSize > *bSizeToFillCB
   2291                    ? *bSizeToFillCB
   2292                    : intrinsicBSize;
   2293      } else {
   2294        bSize = fallbackIntrinsicSize.BSize(aWM);
   2295      }
   2296      bSize = CSSMinMax(bSize, minBSize, maxBSize);
   2297    } else {
   2298      // non-'auto' iSize, non-'auto' bSize
   2299      iSize = CSSMinMax(iSize, minISize, maxISize);
   2300      bSize = CSSMinMax(bSize, minBSize, maxBSize);
   2301    }
   2302  }
   2303 
   2304  return LogicalSize(aWM, iSize, bSize);
   2305 }
   2306 
   2307 nsRect nsContainerFrame::ComputeSimpleTightBounds(
   2308    DrawTarget* aDrawTarget) const {
   2309  if (StyleOutline()->ShouldPaintOutline() || StyleBorder()->HasBorder() ||
   2310      !StyleBackground()->IsTransparent(this) ||
   2311      StyleDisplay()->HasAppearance()) {
   2312    // Not necessarily tight, due to clipping, negative
   2313    // outline-offset, and lots of other issues, but that's OK
   2314    return InkOverflowRect();
   2315  }
   2316 
   2317  nsRect r(0, 0, 0, 0);
   2318  for (const auto& childLists : ChildLists()) {
   2319    for (nsIFrame* child : childLists.mList) {
   2320      r.UnionRect(
   2321          r, child->ComputeTightBounds(aDrawTarget) + child->GetPosition());
   2322    }
   2323  }
   2324  return r;
   2325 }
   2326 
   2327 void nsContainerFrame::PushDirtyBitToAbsoluteFrames() {
   2328  if (!HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
   2329    return;  // No dirty bit to push.
   2330  }
   2331  if (!HasAbsolutelyPositionedChildren()) {
   2332    return;  // No absolute children to push to.
   2333  }
   2334  GetAbsoluteContainingBlock()->MarkAllFramesDirty();
   2335 }
   2336 
   2337 // Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus
   2338 // 4 for the frames above the document's frames:
   2339 //  the Viewport, GFXScroll, ScrollPort, and Canvas
   2340 #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH + 4)
   2341 
   2342 bool nsContainerFrame::IsFrameTreeTooDeep(const ReflowInput& aReflowInput,
   2343                                          ReflowOutput& aMetrics,
   2344                                          nsReflowStatus& aStatus) {
   2345  if (aReflowInput.mReflowDepth > MAX_FRAME_DEPTH) {
   2346    NS_WARNING("frame tree too deep; setting zero size and returning");
   2347    AddStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE);
   2348    ClearOverflowRects();
   2349    aMetrics.ClearSize();
   2350    aMetrics.SetBlockStartAscent(0);
   2351    aMetrics.mCarriedOutBEndMargin.Zero();
   2352    aMetrics.mOverflowAreas.Clear();
   2353 
   2354    aStatus.Reset();
   2355    if (GetNextInFlow()) {
   2356      // Reflow depth might vary between reflows, so we might have
   2357      // successfully reflowed and split this frame before.  If so, we
   2358      // shouldn't delete its continuations.
   2359      aStatus.SetIncomplete();
   2360    }
   2361 
   2362    return true;
   2363  }
   2364  RemoveStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE);
   2365  return false;
   2366 }
   2367 
   2368 bool nsContainerFrame::ShouldAvoidBreakInside(
   2369    const ReflowInput& aReflowInput) const {
   2370  MOZ_ASSERT(this == aReflowInput.mFrame,
   2371             "Caller should pass a ReflowInput for this frame!");
   2372 
   2373  const auto* disp = StyleDisplay();
   2374  const bool mayAvoidBreak = [&] {
   2375    switch (disp->mBreakInside) {
   2376      case StyleBreakWithin::Auto:
   2377        return false;
   2378      case StyleBreakWithin::Avoid:
   2379        return true;
   2380      case StyleBreakWithin::AvoidPage:
   2381        return aReflowInput.mBreakType == ReflowInput::BreakType::Page;
   2382      case StyleBreakWithin::AvoidColumn:
   2383        return aReflowInput.mBreakType == ReflowInput::BreakType::Column;
   2384    }
   2385    MOZ_ASSERT_UNREACHABLE("Unknown break-inside value");
   2386    return false;
   2387  }();
   2388 
   2389  if (!mayAvoidBreak) {
   2390    return false;
   2391  }
   2392  if (aReflowInput.mFlags.mIsTopOfPage) {
   2393    return false;
   2394  }
   2395  if (IsAbsolutelyPositioned(disp)) {
   2396    return false;
   2397  }
   2398  if (GetPrevInFlow()) {
   2399    return false;
   2400  }
   2401  return true;
   2402 }
   2403 
   2404 void nsContainerFrame::ConsiderChildOverflow(OverflowAreas& aOverflowAreas,
   2405                                             nsIFrame* aChildFrame,
   2406                                             bool aAsIfScrolled) {
   2407  if (StyleDisplay()->IsContainLayout() && SupportsContainLayoutAndPaint() &&
   2408      !aAsIfScrolled) {
   2409    // If we have layout containment and are not a non-atomic, inline-level
   2410    // principal box, we should only consider our child's ink overflow,
   2411    // leaving the scrollable regions of the parent unaffected.
   2412    // Note: scrollable overflow is a subset of ink overflow,
   2413    // so this has the same affect as unioning the child's ink and
   2414    // scrollable overflow with the parent's ink overflow.
   2415    const OverflowAreas childOverflows(aChildFrame->InkOverflowRect(),
   2416                                       nsRect());
   2417    aOverflowAreas.UnionWith(childOverflows + aChildFrame->GetPosition());
   2418  } else {
   2419    aOverflowAreas.UnionWith(
   2420        aChildFrame->GetActualAndNormalOverflowAreasRelativeToParent());
   2421  }
   2422 }
   2423 
   2424 StyleAlignFlags nsContainerFrame::CSSAlignmentForAbsPosChild(
   2425    const ReflowInput& aChildRI, LogicalAxis aLogicalAxis) const {
   2426  MOZ_ASSERT(aChildRI.mFrame->IsAbsolutelyPositioned(),
   2427             "This method should only be called for abspos children");
   2428  // For computing the static position of an absolutely positioned box,
   2429  // `auto` takes from parent's `align-items`.
   2430  StyleAlignFlags alignment =
   2431      aChildRI.mStylePosition->UsedSelfAlignment(aLogicalAxis, Style());
   2432  return CSSAlignUtils::UsedAlignmentForAbsPos(aChildRI.mFrame, alignment,
   2433                                               aLogicalAxis, GetWritingMode());
   2434 }
   2435 
   2436 StyleAlignFlags
   2437 nsContainerFrame::CSSAlignmentForAbsPosChildWithinContainingBlock(
   2438    const SizeComputationInput& aSizingInput, LogicalAxis aLogicalAxis,
   2439    const StylePositionArea& aResolvedPositionArea,
   2440    const LogicalSize& aCBSize) const {
   2441  MOZ_ASSERT(aSizingInput.mFrame->IsAbsolutelyPositioned(),
   2442             "This method should only be called for abspos children");
   2443  // When determining the position of absolutely-positioned boxes,
   2444  // `auto` behaves as `normal`.
   2445  StyleAlignFlags alignment =
   2446      aSizingInput.mFrame->StylePosition()->UsedSelfAlignment(aLogicalAxis,
   2447                                                              nullptr);
   2448 
   2449  // Check if position-area is set - if so, it determines the default alignment
   2450  // https://drafts.csswg.org/css-anchor-position/#position-area-alignment
   2451  if (!aResolvedPositionArea.IsNone() && alignment == StyleAlignFlags::NORMAL) {
   2452    const WritingMode cbWM = GetWritingMode();
   2453    const auto anchorResolutionParams = AnchorPosResolutionParams::From(
   2454        &aSizingInput, /* aIgnorePositionArea = */ true);
   2455    const auto anchorOffsetResolutionParams =
   2456        AnchorPosOffsetResolutionParams::ExplicitCBFrameSize(
   2457            anchorResolutionParams, &aCBSize);
   2458 
   2459    // Check if we have exactly one auto inset in this axis (IMCB situation)
   2460    const auto singleAutoInset =
   2461        aSizingInput.mFrame->StylePosition()->GetSingleAutoInsetInAxis(
   2462            aLogicalAxis, cbWM, anchorOffsetResolutionParams);
   2463 
   2464    // Check if exactly one inset in the axis is auto
   2465    // https://drafts.csswg.org/css-anchor-position/#position-area-alignment
   2466    // "However, if only one inset property in the relevant axis is auto, the
   2467    // default alignment is instead towards the edge with the non-auto inset;
   2468    // and this is an unsafe alignment."
   2469    if (singleAutoInset.isSome()) {
   2470      const LogicalSide startSide = aLogicalAxis == LogicalAxis::Inline
   2471                                        ? LogicalSide::IStart
   2472                                        : LogicalSide::BStart;
   2473      const mozilla::Side autoSide = *singleAutoInset;
   2474      const mozilla::Side startPhysicalSide = cbWM.PhysicalSide(startSide);
   2475      // Exactly one inset is auto - align toward the non-auto edge, unsafely
   2476      alignment = (autoSide == startPhysicalSide) ? StyleAlignFlags::END
   2477                                                  : StyleAlignFlags::START;
   2478      alignment |= StyleAlignFlags::UNSAFE;
   2479    } else {
   2480      // Use default position-area self-alignment:
   2481      // https://drafts.csswg.org/css-anchor-position-1/#position-area-alignment
   2482      const auto axis = ToStyleLogicalAxis(aLogicalAxis);
   2483      const auto cbSWM = cbWM.ToStyleWritingMode();
   2484      const auto selfWM =
   2485          aSizingInput.mFrame->GetWritingMode().ToStyleWritingMode();
   2486      Servo_ResolvePositionAreaSelfAlignment(&aResolvedPositionArea, axis,
   2487                                             &cbSWM, &selfWM, &alignment);
   2488    }
   2489  }
   2490 
   2491  return CSSAlignUtils::UsedAlignmentForAbsPos(aSizingInput.mFrame, alignment,
   2492                                               aLogicalAxis, GetWritingMode());
   2493 }
   2494 
   2495 nsOverflowContinuationTracker::nsOverflowContinuationTracker(
   2496    nsContainerFrame* aFrame, bool aWalkOOFFrames,
   2497    bool aSkipOverflowContainerChildren)
   2498    : mOverflowContList(nullptr),
   2499      mPrevOverflowCont(nullptr),
   2500      mSentry(nullptr),
   2501      mParent(aFrame),
   2502      mSkipOverflowContainerChildren(aSkipOverflowContainerChildren),
   2503      mWalkOOFFrames(aWalkOOFFrames) {
   2504  MOZ_ASSERT(aFrame, "null frame pointer");
   2505  SetupOverflowContList();
   2506 }
   2507 
   2508 void nsOverflowContinuationTracker::SetupOverflowContList() {
   2509  MOZ_ASSERT(mParent, "null frame pointer");
   2510  MOZ_ASSERT(!mOverflowContList, "already have list");
   2511  nsContainerFrame* nif =
   2512      static_cast<nsContainerFrame*>(mParent->GetNextInFlow());
   2513  if (nif) {
   2514    mOverflowContList = nif->GetOverflowContainers();
   2515    if (mOverflowContList) {
   2516      mParent = nif;
   2517      SetUpListWalker();
   2518    }
   2519  }
   2520  if (!mOverflowContList) {
   2521    mOverflowContList = mParent->GetExcessOverflowContainers();
   2522    if (mOverflowContList) {
   2523      SetUpListWalker();
   2524    }
   2525  }
   2526 }
   2527 
   2528 /**
   2529 * Helper function to walk past overflow continuations whose prev-in-flow
   2530 * isn't a normal child and to set mSentry and mPrevOverflowCont correctly.
   2531 */
   2532 void nsOverflowContinuationTracker::SetUpListWalker() {
   2533  NS_ASSERTION(!mSentry && !mPrevOverflowCont,
   2534               "forgot to reset mSentry or mPrevOverflowCont");
   2535  if (mOverflowContList) {
   2536    nsIFrame* cur = mOverflowContList->FirstChild();
   2537    if (mSkipOverflowContainerChildren) {
   2538      while (cur && cur->GetPrevInFlow()->HasAnyStateBits(
   2539                        NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   2540        mPrevOverflowCont = cur;
   2541        cur = cur->GetNextSibling();
   2542      }
   2543      while (cur &&
   2544             (cur->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) != mWalkOOFFrames)) {
   2545        mPrevOverflowCont = cur;
   2546        cur = cur->GetNextSibling();
   2547      }
   2548    }
   2549    if (cur) {
   2550      mSentry = cur->GetPrevInFlow();
   2551    }
   2552  }
   2553 }
   2554 
   2555 /**
   2556 * Helper function to step forward through the overflow continuations list.
   2557 * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames
   2558 * as appropriate. May only be called when we have already set up an
   2559 * mOverflowContList; mOverflowContList cannot be null.
   2560 */
   2561 void nsOverflowContinuationTracker::StepForward() {
   2562  MOZ_ASSERT(mOverflowContList, "null list");
   2563 
   2564  // Step forward
   2565  if (mPrevOverflowCont) {
   2566    mPrevOverflowCont = mPrevOverflowCont->GetNextSibling();
   2567  } else {
   2568    mPrevOverflowCont = mOverflowContList->FirstChild();
   2569  }
   2570 
   2571  // Skip over oof or non-oof frames as appropriate
   2572  if (mSkipOverflowContainerChildren) {
   2573    nsIFrame* cur = mPrevOverflowCont->GetNextSibling();
   2574    while (cur &&
   2575           (cur->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) != mWalkOOFFrames)) {
   2576      mPrevOverflowCont = cur;
   2577      cur = cur->GetNextSibling();
   2578    }
   2579  }
   2580 
   2581  // Set up the sentry
   2582  mSentry = (mPrevOverflowCont->GetNextSibling())
   2583                ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow()
   2584                : nullptr;
   2585 }
   2586 
   2587 nsresult nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont,
   2588                                               nsReflowStatus& aReflowStatus) {
   2589  MOZ_ASSERT(aOverflowCont, "null frame pointer");
   2590  MOZ_ASSERT(!mSkipOverflowContainerChildren ||
   2591                 mWalkOOFFrames ==
   2592                     aOverflowCont->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
   2593             "shouldn't insert frame that doesn't match walker type");
   2594  MOZ_ASSERT(aOverflowCont->GetPrevInFlow(),
   2595             "overflow containers must have a prev-in-flow");
   2596 
   2597  nsresult rv = NS_OK;
   2598  bool reparented = false;
   2599  nsPresContext* presContext = aOverflowCont->PresContext();
   2600  bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow();
   2601 
   2602  // If we have a list and aOverflowCont is already in it then don't try to
   2603  // add it again.
   2604  if (addToList && aOverflowCont->GetParent() == mParent &&
   2605      aOverflowCont->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) &&
   2606      mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) {
   2607    addToList = false;
   2608    mPrevOverflowCont = aOverflowCont->GetPrevSibling();
   2609  }
   2610 
   2611  if (addToList) {
   2612    if (aOverflowCont->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   2613      // aOverflowCont is in some other overflow container list,
   2614      // steal it first
   2615      NS_ASSERTION(!(mOverflowContList &&
   2616                     mOverflowContList->ContainsFrame(aOverflowCont)),
   2617                   "overflow containers out of order");
   2618      aOverflowCont->GetParent()->StealFrame(aOverflowCont);
   2619    } else {
   2620      aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
   2621    }
   2622    if (!mOverflowContList) {
   2623      // Note: We don't use SetExcessOverflowContainers() since it requires
   2624      // setting a non-empty list. It's OK to manually set an empty list to
   2625      // ExcessOverflowContainersProperty() because we are going to insert
   2626      // aOverflowCont to mOverflowContList below, which guarantees an nonempty
   2627      // list in ExcessOverflowContainersProperty().
   2628      mOverflowContList = new (presContext->PresShell()) nsFrameList();
   2629      mParent->SetProperty(nsContainerFrame::ExcessOverflowContainersProperty(),
   2630                           mOverflowContList);
   2631      SetUpListWalker();
   2632    }
   2633    if (aOverflowCont->GetParent() != mParent) {
   2634      reparented = true;
   2635    }
   2636 
   2637    // If aOverflowCont has a prev/next-in-flow that might be in
   2638    // mOverflowContList we need to find it and insert after/before it to
   2639    // maintain the order amongst next-in-flows in this list.
   2640    nsIFrame* pif = aOverflowCont->GetPrevInFlow();
   2641    nsIFrame* nif = aOverflowCont->GetNextInFlow();
   2642    if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) ||
   2643        (nif && nif->GetParent() == mParent && mPrevOverflowCont)) {
   2644      for (nsIFrame* f : *mOverflowContList) {
   2645        if (f == pif) {
   2646          mPrevOverflowCont = pif;
   2647          break;
   2648        }
   2649        if (f == nif) {
   2650          mPrevOverflowCont = f->GetPrevSibling();
   2651          break;
   2652        }
   2653      }
   2654    }
   2655 
   2656    mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont);
   2657    aReflowStatus.SetNextInFlowNeedsReflow();
   2658  }
   2659 
   2660  // If we need to reflow it, mark it dirty
   2661  if (aReflowStatus.NextInFlowNeedsReflow()) {
   2662    aOverflowCont->MarkSubtreeDirty();
   2663  }
   2664 
   2665  // It's in our list, just step forward
   2666  StepForward();
   2667  NS_ASSERTION(mPrevOverflowCont == aOverflowCont ||
   2668                   (mSkipOverflowContainerChildren &&
   2669                    mPrevOverflowCont->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) !=
   2670                        aOverflowCont->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)),
   2671               "OverflowContTracker in unexpected state");
   2672 
   2673  if (addToList) {
   2674    // Convert all non-overflow-container next-in-flows of aOverflowCont
   2675    // into overflow containers and move them to our overflow
   2676    // tracker. This preserves the invariant that the next-in-flows
   2677    // of an overflow container are also overflow containers.
   2678    nsIFrame* f = aOverflowCont->GetNextInFlow();
   2679    if (f && (!f->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) ||
   2680              (!reparented && f->GetParent() == mParent) ||
   2681              (reparented && f->GetParent() != mParent))) {
   2682      if (!f->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   2683        f->GetParent()->StealFrame(f);
   2684      }
   2685      Insert(f, aReflowStatus);
   2686    }
   2687  }
   2688  return rv;
   2689 }
   2690 
   2691 void nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild) {
   2692  MOZ_ASSERT(aChild, "null ptr");
   2693  MOZ_ASSERT(aChild->GetNextInFlow(),
   2694             "supposed to call Finish *before* deleting next-in-flow!");
   2695 
   2696  for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) {
   2697    // We'll update these in EndFinish after the next-in-flows are gone.
   2698    if (f == mPrevOverflowCont) {
   2699      mSentry = nullptr;
   2700      mPrevOverflowCont = nullptr;
   2701      break;
   2702    }
   2703    if (f == mSentry) {
   2704      mSentry = nullptr;
   2705      break;
   2706    }
   2707  }
   2708 }
   2709 
   2710 void nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild) {
   2711  if (!mOverflowContList) {
   2712    return;
   2713  }
   2714  // Forget mOverflowContList if it was deleted.
   2715  nsFrameList* eoc = mParent->GetExcessOverflowContainers();
   2716  if (eoc != mOverflowContList) {
   2717    nsFrameList* oc = mParent->GetOverflowContainers();
   2718    if (oc != mOverflowContList) {
   2719      // mOverflowContList was deleted
   2720      mPrevOverflowCont = nullptr;
   2721      mSentry = nullptr;
   2722      mParent = aChild->GetParent();
   2723      mOverflowContList = nullptr;
   2724      SetupOverflowContList();
   2725      return;
   2726    }
   2727  }
   2728  // The list survived, update mSentry if needed.
   2729  if (!mSentry) {
   2730    if (!mPrevOverflowCont) {
   2731      SetUpListWalker();
   2732    } else {
   2733      mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont);
   2734      // step backward to make StepForward() use our current mPrevOverflowCont
   2735      mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling();
   2736      StepForward();
   2737    }
   2738  }
   2739 }
   2740 
   2741 RubyMetrics nsContainerFrame::RubyMetricsIncludingChildren(
   2742    float aRubyMetricsFactor) const {
   2743  mozilla::RubyMetrics result;
   2744  WritingMode containerWM = GetWritingMode();
   2745  bool foundAnyFrames = false;
   2746  for (const auto* f : mFrames) {
   2747    WritingMode wm = f->GetWritingMode();
   2748    if (wm.IsOrthogonalTo(containerWM) || f->IsPlaceholderFrame()) {
   2749      continue;
   2750    }
   2751    mozilla::RubyMetrics m = f->RubyMetrics(aRubyMetricsFactor);
   2752    result.CombineWith(m);
   2753    foundAnyFrames = true;
   2754  }
   2755  if (!foundAnyFrames) {
   2756    result = nsIFrame::RubyMetrics(aRubyMetricsFactor);
   2757  }
   2758  return result;
   2759 }
   2760 
   2761 /////////////////////////////////////////////////////////////////////////////
   2762 // Debugging
   2763 
   2764 #ifdef DEBUG
   2765 void nsContainerFrame::SanityCheckChildListsBeforeReflow() const {
   2766  MOZ_ASSERT(IsFlexOrGridContainer(),
   2767             "Only Flex / Grid containers can call this!");
   2768 
   2769  const auto didPushItemsBit = IsFlexContainerFrame()
   2770                                   ? NS_STATE_FLEX_DID_PUSH_ITEMS
   2771                                   : NS_STATE_GRID_DID_PUSH_ITEMS;
   2772  ChildListIDs absLists = {
   2773      FrameChildListID::Absolute, FrameChildListID::PushedAbsolute,
   2774      FrameChildListID::Fixed, FrameChildListID::OverflowContainers,
   2775      FrameChildListID::ExcessOverflowContainers};
   2776  ChildListIDs itemLists = {FrameChildListID::Principal,
   2777                            FrameChildListID::Overflow};
   2778  for (const nsIFrame* f = this; f; f = f->GetNextInFlow()) {
   2779    MOZ_ASSERT(!f->HasAnyStateBits(didPushItemsBit),
   2780               "At start of reflow, we should've pulled items back from all "
   2781               "NIFs and cleared the state bit stored in didPushItemsBit in "
   2782               "the process.");
   2783    for (const auto& [list, listID] : f->ChildLists()) {
   2784      if (!itemLists.contains(listID)) {
   2785        MOZ_ASSERT(absLists.contains(listID),
   2786                   "unexpected non-empty child list");
   2787        continue;
   2788      }
   2789      for (const auto* child : list) {
   2790        MOZ_ASSERT(f == this || child->GetPrevInFlow(),
   2791                   "all pushed items must be pulled up before reflow");
   2792      }
   2793    }
   2794  }
   2795  // If we have a prev-in-flow, each of its children's next-in-flow
   2796  // should be one of our children or be null.
   2797  const auto* pif = static_cast<nsContainerFrame*>(GetPrevInFlow());
   2798  if (pif) {
   2799    const nsFrameList* oc = GetOverflowContainers();
   2800    const nsFrameList* eoc = GetExcessOverflowContainers();
   2801    const nsFrameList* pifEOC = pif->GetExcessOverflowContainers();
   2802    for (const nsIFrame* child : pif->PrincipalChildList()) {
   2803      const nsIFrame* childNIF = child->GetNextInFlow();
   2804      MOZ_ASSERT(!childNIF || mFrames.ContainsFrame(childNIF) ||
   2805                 (pifEOC && pifEOC->ContainsFrame(childNIF)) ||
   2806                 (oc && oc->ContainsFrame(childNIF)) ||
   2807                 (eoc && eoc->ContainsFrame(childNIF)));
   2808    }
   2809  }
   2810 }
   2811 
   2812 void nsContainerFrame::SetDidPushItemsBitIfNeeded(ChildListID aListID,
   2813                                                  nsIFrame* aOldFrame) {
   2814  MOZ_ASSERT(IsFlexOrGridContainer(),
   2815             "Only Flex / Grid containers can call this!");
   2816 
   2817  // Note that FrameChildListID::Principal doesn't mean aOldFrame must be on
   2818  // that list. It can also be on FrameChildListID::Overflow, in which case it
   2819  // might be a pushed item, and if it's the only pushed item our DID_PUSH_ITEMS
   2820  // bit will lie.
   2821  if (aListID == FrameChildListID::Principal && !aOldFrame->GetPrevInFlow()) {
   2822    // Since the bit may lie, set the mDidPushItemsBitMayLie value to true for
   2823    // ourself and for all our prev-in-flows.
   2824    nsContainerFrame* frameThatMayLie = this;
   2825    do {
   2826      frameThatMayLie->mDidPushItemsBitMayLie = true;
   2827      frameThatMayLie =
   2828          static_cast<nsContainerFrame*>(frameThatMayLie->GetPrevInFlow());
   2829    } while (frameThatMayLie);
   2830  }
   2831 }
   2832 #endif
   2833 
   2834 #ifdef DEBUG_FRAME_DUMP
   2835 void nsContainerFrame::List(FILE* out, const char* aPrefix,
   2836                            ListFlags aFlags) const {
   2837  nsCString str;
   2838  ListGeneric(str, aPrefix, aFlags);
   2839  ExtraContainerFrameInfo(str,
   2840                          aFlags.contains(ListFlag::OnlyListDeterministicInfo));
   2841 
   2842  // Output the frame name and various fields.
   2843  fprintf_stderr(out, "%s <\n", str.get());
   2844 
   2845  const nsCString pfx = nsCString(aPrefix) + "  "_ns;
   2846 
   2847  // Output principal child list separately since we want to omit its
   2848  // name and address.
   2849  for (nsIFrame* kid : PrincipalChildList()) {
   2850    kid->List(out, pfx.get(), aFlags);
   2851  }
   2852 
   2853  // Output rest of the child lists.
   2854  const ChildListIDs skippedListIDs = {FrameChildListID::Principal};
   2855  ListChildLists(out, pfx.get(), aFlags, skippedListIDs);
   2856 
   2857  fprintf_stderr(out, "%s>\n", aPrefix);
   2858 }
   2859 
   2860 void nsContainerFrame::ListWithMatchedRules(FILE* out,
   2861                                            const char* aPrefix) const {
   2862  fprintf_stderr(out, "%s%s\n", aPrefix, ListTag().get());
   2863 
   2864  nsCString rulePrefix;
   2865  rulePrefix += aPrefix;
   2866  rulePrefix += "    ";
   2867  ListMatchedRules(out, rulePrefix.get());
   2868 
   2869  nsCString childPrefix;
   2870  childPrefix += aPrefix;
   2871  childPrefix += "  ";
   2872 
   2873  for (const auto& childList : ChildLists()) {
   2874    for (const nsIFrame* kid : childList.mList) {
   2875      kid->ListWithMatchedRules(out, childPrefix.get());
   2876    }
   2877  }
   2878 }
   2879 
   2880 void nsContainerFrame::ListChildLists(FILE* aOut, const char* aPrefix,
   2881                                      ListFlags aFlags,
   2882                                      ChildListIDs aSkippedListIDs) const {
   2883  const nsCString nestedPfx = nsCString(aPrefix) + "  "_ns;
   2884 
   2885  for (const auto& [list, listID] : ChildLists()) {
   2886    if (aSkippedListIDs.contains(listID)) {
   2887      continue;
   2888    }
   2889 
   2890    // Use nsPrintfCString so that %p don't output prefix "0x". This is
   2891    // consistent with nsIFrame::ListTag().
   2892    nsCString str{nsPrintfCString("%s%s", aPrefix, ChildListName(listID))};
   2893    ListPtr(str, aFlags, &GetChildList(listID), "@");
   2894    str += " <\n";
   2895    fprintf_stderr(aOut, "%s", str.get());
   2896 
   2897    for (nsIFrame* kid : list) {
   2898      // Verify the child frame's parent frame pointer is correct.
   2899      NS_ASSERTION(kid->GetParent() == this, "Bad parent frame pointer!");
   2900      kid->List(aOut, nestedPfx.get(), aFlags);
   2901    }
   2902    fprintf_stderr(aOut, "%s>\n", aPrefix);
   2903  }
   2904 }
   2905 
   2906 void nsContainerFrame::ExtraContainerFrameInfo(nsACString&, bool) const {}
   2907 
   2908 #endif