tor-browser

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

nsIFrame.cpp (480299B)


      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 of all rendering objects */
      8 
      9 #include "nsIFrame.h"
     10 
     11 #include <stdarg.h>
     12 
     13 #include <algorithm>
     14 
     15 #include "AnchorPositioningUtils.h"
     16 #include "LayoutLogging.h"
     17 #include "RubyUtils.h"
     18 #include "TextOverflow.h"
     19 #include "gfx2DGlue.h"
     20 #include "gfxUtils.h"
     21 #include "mozilla/AbsoluteContainingBlock.h"
     22 #include "mozilla/Attributes.h"
     23 #include "mozilla/CaretAssociationHint.h"
     24 #include "mozilla/ComputedStyle.h"
     25 #include "mozilla/DebugOnly.h"
     26 #include "mozilla/DisplayPortUtils.h"
     27 #include "mozilla/EventForwards.h"
     28 #include "mozilla/FocusModel.h"
     29 #include "mozilla/IntegerRange.h"
     30 #include "mozilla/Logging.h"
     31 #include "mozilla/Maybe.h"
     32 #include "mozilla/PresShell.h"
     33 #include "mozilla/PresShellInlines.h"
     34 #include "mozilla/RestyleManager.h"
     35 #include "mozilla/ResultExtensions.h"
     36 #include "mozilla/SVGIntegrationUtils.h"
     37 #include "mozilla/SVGMaskFrame.h"
     38 #include "mozilla/SVGObserverUtils.h"
     39 #include "mozilla/SVGTextFrame.h"
     40 #include "mozilla/SVGUtils.h"
     41 #include "mozilla/ScrollContainerFrame.h"
     42 #include "mozilla/SelectionMovementUtils.h"
     43 #include "mozilla/ServoStyleConsts.h"
     44 #include "mozilla/Sprintf.h"
     45 #include "mozilla/StaticAnalysisFunctions.h"
     46 #include "mozilla/StaticPrefs_dom.h"
     47 #include "mozilla/StaticPrefs_layout.h"
     48 #include "mozilla/StaticPrefs_print.h"
     49 #include "mozilla/StaticPrefs_ui.h"
     50 #include "mozilla/TextControlElement.h"
     51 #include "mozilla/ToString.h"
     52 #include "mozilla/Try.h"
     53 #include "mozilla/ViewportFrame.h"
     54 #include "mozilla/ViewportUtils.h"
     55 #include "mozilla/WritingModes.h"
     56 #include "mozilla/dom/AncestorIterator.h"
     57 #include "mozilla/dom/CSSAnimation.h"
     58 #include "mozilla/dom/CSSTransition.h"
     59 #include "mozilla/dom/ContentVisibilityAutoStateChangeEvent.h"
     60 #include "mozilla/dom/DocumentInlines.h"
     61 #include "mozilla/dom/ElementInlines.h"
     62 #include "mozilla/dom/HTMLDetailsElement.h"
     63 #include "mozilla/dom/Selection.h"
     64 #include "mozilla/gfx/2D.h"
     65 #include "mozilla/gfx/PathHelpers.h"
     66 #include "mozilla/intl/BidiEmbeddingLevel.h"
     67 #include "nsAnimationManager.h"
     68 #include "nsAtom.h"
     69 #include "nsBidiPresUtils.h"
     70 #include "nsCOMPtr.h"
     71 #include "nsCSSAnonBoxes.h"
     72 #include "nsCSSFrameConstructor.h"
     73 #include "nsCSSProps.h"
     74 #include "nsCSSPseudoElements.h"
     75 #include "nsCSSRendering.h"
     76 #include "nsCanvasFrame.h"
     77 #include "nsContentUtils.h"
     78 #include "nsFieldSetFrame.h"
     79 #include "nsFlexContainerFrame.h"
     80 #include "nsFocusManager.h"
     81 #include "nsFrameList.h"
     82 #include "nsFrameSelection.h"
     83 #include "nsFrameState.h"
     84 #include "nsFrameTraversal.h"
     85 #include "nsGkAtoms.h"
     86 #include "nsGridContainerFrame.h"
     87 #include "nsIBaseWindow.h"
     88 #include "nsIContent.h"
     89 #include "nsIContentInlines.h"
     90 #include "nsIPercentBSizeObserver.h"
     91 #include "nsImageFrame.h"
     92 #include "nsInlineFrame.h"
     93 #include "nsLayoutUtils.h"
     94 #include "nsMenuPopupFrame.h"
     95 #include "nsNameSpaceManager.h"
     96 #include "nsPlaceholderFrame.h"
     97 #include "nsPresContext.h"
     98 #include "nsPresContextInlines.h"
     99 #include "nsRange.h"
    100 #include "nsReadableUtils.h"
    101 #include "nsString.h"
    102 #include "nsStyleConsts.h"
    103 #include "nsStyleStructInlines.h"
    104 #include "nsTableWrapperFrame.h"
    105 #include "nsTextControlFrame.h"
    106 #include "nsXULElement.h"
    107 
    108 // For triple-click pref
    109 #include "RetainedDisplayListBuilder.h"
    110 #include "ScrollSnap.h"
    111 #include "StickyScrollContainer.h"
    112 #include "gfxContext.h"
    113 #include "imgIRequest.h"
    114 #include "nsBlockFrame.h"
    115 #include "nsChangeHint.h"
    116 #include "nsContainerFrame.h"
    117 #include "nsDisplayList.h"
    118 #include "nsError.h"
    119 #include "nsFontInflationData.h"
    120 #include "nsIFrameInlines.h"
    121 #include "nsRegion.h"
    122 #include "nsStyleChangeList.h"
    123 #include "nsSubDocumentFrame.h"
    124 #include "nsViewportInfo.h"
    125 #include "nsWindowSizes.h"
    126 
    127 #ifdef ACCESSIBILITY
    128 #  include "nsAccessibilityService.h"
    129 #endif
    130 
    131 #include "ActiveLayerTracker.h"
    132 #include "mozilla/AsyncEventDispatcher.h"
    133 #include "mozilla/CSSClipPathInstance.h"
    134 #include "mozilla/EffectCompositor.h"
    135 #include "mozilla/EffectSet.h"
    136 #include "mozilla/EventListenerManager.h"
    137 #include "mozilla/EventStateManager.h"
    138 #include "mozilla/LookAndFeel.h"
    139 #include "mozilla/MouseEvents.h"
    140 #include "mozilla/Preferences.h"
    141 #include "mozilla/ServoStyleSet.h"
    142 #include "mozilla/ServoStyleSetInlines.h"
    143 #include "mozilla/css/ImageLoader.h"
    144 #include "mozilla/dom/HTMLBodyElement.h"
    145 #include "mozilla/dom/SVGPathData.h"
    146 #include "mozilla/dom/TouchEvent.h"
    147 #include "mozilla/gfx/Tools.h"
    148 #include "mozilla/layers/WebRenderUserData.h"
    149 #include "mozilla/layout/ScrollAnchorContainer.h"
    150 #include "nsITheme.h"
    151 #include "nsPrintfCString.h"
    152 
    153 using namespace mozilla;
    154 using namespace mozilla::css;
    155 using namespace mozilla::dom;
    156 using namespace mozilla::gfx;
    157 using namespace mozilla::layers;
    158 using namespace mozilla::layout;
    159 using nsStyleTransformMatrix::TransformReferenceBox;
    160 
    161 nsIFrame* nsILineIterator::LineInfo::GetLastFrameOnLine() const {
    162  if (!mNumFramesOnLine) {
    163    return nullptr;  // empty line, not illegal
    164  }
    165  MOZ_ASSERT(mFirstFrameOnLine);
    166  nsIFrame* maybeLastFrame = mFirstFrameOnLine;
    167  for ([[maybe_unused]] int32_t i : IntegerRange(mNumFramesOnLine - 1)) {
    168    maybeLastFrame = maybeLastFrame->GetNextSibling();
    169    if (NS_WARN_IF(!maybeLastFrame)) {
    170      return nullptr;
    171    }
    172  }
    173  return maybeLastFrame;
    174 }
    175 
    176 #ifdef HAVE_64BIT_BUILD
    177 static_assert(sizeof(nsIFrame) == 120, "nsIFrame should remain small");
    178 #else
    179 static_assert(sizeof(void*) == 4, "Odd build config?");
    180 // FIXME(emilio): Investigate why win32 and android-arm32 have bigger sizes (80)
    181 // than Linux32 (76).
    182 static_assert(sizeof(nsIFrame) <= 80, "nsIFrame should remain small");
    183 #endif
    184 
    185 const mozilla::LayoutFrameType nsIFrame::sLayoutFrameTypes[kFrameClassCount] = {
    186 #define FRAME_ID(class_, type_, ...) mozilla::LayoutFrameType::type_,
    187 #define ABSTRACT_FRAME_ID(...)
    188 #include "mozilla/FrameIdList.h"
    189 #undef FRAME_ID
    190 #undef ABSTRACT_FRAME_ID
    191 };
    192 
    193 const nsIFrame::ClassFlags nsIFrame::sLayoutFrameClassFlags[kFrameClassCount] =
    194    {
    195 #define FRAME_ID(class_, type_, flags_, ...) flags_,
    196 #define ABSTRACT_FRAME_ID(...)
    197 #include "mozilla/FrameIdList.h"
    198 #undef FRAME_ID
    199 #undef ABSTRACT_FRAME_ID
    200 };
    201 
    202 std::ostream& operator<<(std::ostream& aStream, const nsDirection& aDirection) {
    203  return aStream << (aDirection == eDirNext ? "eDirNext" : "eDirPrevious");
    204 }
    205 
    206 struct nsContentAndOffset {
    207  nsIContent* mContent = nullptr;
    208  int32_t mOffset = 0;
    209 };
    210 
    211 #include "nsILineIterator.h"
    212 #include "prenv.h"
    213 
    214 FrameDestroyContext::~FrameDestroyContext() {
    215  for (auto& content : mozilla::Reversed(mAnonymousContent)) {
    216    mPresShell->NativeAnonymousContentWillBeRemoved(content);
    217    content->UnbindFromTree();
    218  }
    219 }
    220 
    221 // Formerly the nsIFrameDebug interface
    222 
    223 std::ostream& operator<<(std::ostream& aStream, const nsReflowStatus& aStatus) {
    224  char complete = 'Y';
    225  if (aStatus.IsIncomplete()) {
    226    complete = 'N';
    227  } else if (aStatus.IsOverflowIncomplete()) {
    228    complete = 'O';
    229  }
    230 
    231  char brk = 'N';
    232  if (aStatus.IsInlineBreakBefore()) {
    233    brk = 'B';
    234  } else if (aStatus.IsInlineBreakAfter()) {
    235    brk = 'A';
    236  }
    237 
    238  aStream << "["
    239          << "Complete=" << complete << ","
    240          << "NIF=" << (aStatus.NextInFlowNeedsReflow() ? 'Y' : 'N') << ","
    241          << "Break=" << brk << ","
    242          << "FirstLetter=" << (aStatus.FirstLetterComplete() ? 'Y' : 'N')
    243          << "]";
    244  return aStream;
    245 }
    246 
    247 #ifdef DEBUG
    248 
    249 /**
    250 * Note: the log module is created during library initialization which
    251 * means that you cannot perform logging before then.
    252 */
    253 mozilla::LazyLogModule nsIFrame::sFrameLogModule("frame");
    254 
    255 #endif
    256 
    257 NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,
    258                                    AbsoluteContainingBlock)
    259 
    260 bool nsIFrame::HasAbsolutelyPositionedChildren() const {
    261  const auto* absCB = GetAbsoluteContainingBlock();
    262  return absCB && absCB->HasAbsoluteFrames();
    263 }
    264 
    265 AbsoluteContainingBlock* nsIFrame::GetAbsoluteContainingBlock() const {
    266  if (!IsAbsoluteContainer()) {
    267    return nullptr;
    268  }
    269  AbsoluteContainingBlock* absCB =
    270      GetProperty(AbsoluteContainingBlockProperty());
    271  NS_ASSERTION(absCB,
    272               "The frame is marked as an abspos container but doesn't have "
    273               "the property");
    274  return absCB;
    275 }
    276 
    277 void nsIFrame::MarkAsAbsoluteContainingBlock() {
    278  MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
    279  NS_ASSERTION(!GetProperty(AbsoluteContainingBlockProperty()),
    280               "Already has an abs-pos containing block property?");
    281  NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
    282               "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
    283  AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
    284  SetProperty(AbsoluteContainingBlockProperty(),
    285              new AbsoluteContainingBlock(GetAbsoluteListID()));
    286 }
    287 
    288 void nsIFrame::MarkAsNotAbsoluteContainingBlock() {
    289  NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
    290  NS_ASSERTION(GetProperty(AbsoluteContainingBlockProperty()),
    291               "Should have an abs-pos containing block property");
    292  NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
    293               "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
    294  MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
    295  RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
    296  RemoveProperty(AbsoluteContainingBlockProperty());
    297 }
    298 
    299 bool nsIFrame::CheckAndClearPaintedState() {
    300  bool result = HasAnyStateBits(NS_FRAME_PAINTED_THEBES);
    301  RemoveStateBits(NS_FRAME_PAINTED_THEBES);
    302 
    303  for (const auto& childList : ChildLists()) {
    304    for (nsIFrame* child : childList.mList) {
    305      if (child->CheckAndClearPaintedState()) {
    306        result = true;
    307      }
    308    }
    309  }
    310  return result;
    311 }
    312 
    313 nsIFrame* nsIFrame::FindLineContainer() const {
    314  MOZ_ASSERT(IsLineParticipant());
    315  nsIFrame* parent = GetParent();
    316  while (parent &&
    317         (parent->IsLineParticipant() || parent->CanContinueTextRun())) {
    318    parent = parent->GetParent();
    319  }
    320  return parent;
    321 }
    322 
    323 bool nsIFrame::CheckAndClearDisplayListState() {
    324  bool result = BuiltDisplayList();
    325  SetBuiltDisplayList(false);
    326 
    327  for (const auto& childList : ChildLists()) {
    328    for (nsIFrame* child : childList.mList) {
    329      if (child->CheckAndClearDisplayListState()) {
    330        result = true;
    331      }
    332    }
    333  }
    334  return result;
    335 }
    336 
    337 bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const {
    338  if (!StyleVisibility()->IsVisible() ||
    339      HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    340    return false;
    341  }
    342 
    343  if (PresShell()->IsUnderHiddenEmbedderElement()) {
    344    return false;
    345  }
    346 
    347  const nsIFrame* frame = this;
    348  while (frame) {
    349    // Checking mMozSubtreeHiddenOnlyVisually is relatively slow because it
    350    // involves loading more memory. It's only allowed in chrome sheets so let's
    351    // only support it in the parent process so we can mostly optimize this out
    352    // in content processes.
    353    if (XRE_IsParentProcess()) {
    354      if (const nsMenuPopupFrame* popup = do_QueryFrame(frame);
    355          popup && !popup->IsOpen()) {
    356        return false;
    357      }
    358      if (frame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) {
    359        return false;
    360      }
    361    }
    362 
    363    // This method is used to determine if a frame is focusable, because it's
    364    // called by nsIFrame::IsFocusable. `content-visibility: auto` should not
    365    // force this frame to be unfocusable, so we only take into account
    366    // `content-visibility: hidden` here.
    367    if (this != frame &&
    368        frame->HidesContent(IncludeContentVisibility::Hidden)) {
    369      return false;
    370    }
    371 
    372    if (nsIFrame* parent = frame->GetParent()) {
    373      frame = parent;
    374    } else {
    375      parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
    376      if (!parent) {
    377        break;
    378      }
    379 
    380      if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
    381          parent->PresContext()->IsChrome() &&
    382          !frame->PresContext()->IsChrome()) {
    383        break;
    384      }
    385 
    386      frame = parent;
    387    }
    388  }
    389 
    390  return true;
    391 }
    392 
    393 void nsIFrame::FindCloserFrameForSelection(
    394    const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) {
    395  if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
    396                                         aCurrentBestFrame->mXDistance,
    397                                         aCurrentBestFrame->mYDistance)) {
    398    aCurrentBestFrame->mFrame = this;
    399  }
    400 }
    401 
    402 void nsIFrame::ElementStateChanged(mozilla::dom::ElementState aStates) {}
    403 
    404 void WeakFrame::Clear(mozilla::PresShell* aPresShell) {
    405  if (aPresShell) {
    406    aPresShell->RemoveWeakFrame(this);
    407  }
    408  mFrame = nullptr;
    409 }
    410 
    411 AutoWeakFrame::AutoWeakFrame(const WeakFrame& aOther)
    412    : mPrev(nullptr), mFrame(nullptr) {
    413  Init(aOther.GetFrame());
    414 }
    415 
    416 void AutoWeakFrame::Clear(mozilla::PresShell* aPresShell) {
    417  if (aPresShell) {
    418    aPresShell->RemoveAutoWeakFrame(this);
    419  }
    420  mFrame = nullptr;
    421  mPrev = nullptr;
    422 }
    423 
    424 AutoWeakFrame::~AutoWeakFrame() {
    425  Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
    426 }
    427 
    428 void AutoWeakFrame::Init(nsIFrame* aFrame) {
    429  Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
    430  mFrame = aFrame;
    431  if (mFrame) {
    432    mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
    433    NS_WARNING_ASSERTION(presShell, "Null PresShell in AutoWeakFrame!");
    434    if (presShell) {
    435      presShell->AddAutoWeakFrame(this);
    436    } else {
    437      mFrame = nullptr;
    438    }
    439  }
    440 }
    441 
    442 void WeakFrame::Init(nsIFrame* aFrame) {
    443  Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
    444  mFrame = aFrame;
    445  if (mFrame) {
    446    mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
    447    MOZ_ASSERT(presShell, "Null PresShell in WeakFrame!");
    448    if (presShell) {
    449      presShell->AddWeakFrame(this);
    450    } else {
    451      mFrame = nullptr;
    452    }
    453  }
    454 }
    455 
    456 nsIFrame* NS_NewEmptyFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
    457  return new (aPresShell) nsIFrame(aStyle, aPresShell->GetPresContext());
    458 }
    459 
    460 nsIFrame::~nsIFrame() {
    461  MOZ_COUNT_DTOR(nsIFrame);
    462 
    463  MOZ_ASSERT(GetVisibility() != Visibility::ApproximatelyVisible,
    464             "Visible nsFrame is being destroyed");
    465 }
    466 
    467 NS_IMPL_FRAMEARENA_HELPERS(nsIFrame)
    468 
    469 // Dummy operator delete.  Will never be called, but must be defined
    470 // to satisfy some C++ ABIs.
    471 void nsIFrame::operator delete(void*, size_t) {
    472  MOZ_CRASH("nsIFrame::operator delete should never be called");
    473 }
    474 
    475 NS_QUERYFRAME_HEAD(nsIFrame)
    476  NS_QUERYFRAME_ENTRY(nsIFrame)
    477 NS_QUERYFRAME_TAIL_INHERITANCE_ROOT
    478 
    479 /////////////////////////////////////////////////////////////////////////////
    480 // nsIFrame
    481 
    482 static bool IsFontSizeInflationContainer(nsIFrame* aFrame,
    483                                         const nsStyleDisplay* aStyleDisplay) {
    484  /*
    485   * Font size inflation is built around the idea that we're inflating
    486   * the fonts for a pan-and-zoom UI so that when the user scales up a
    487   * block or other container to fill the width of the device, the fonts
    488   * will be readable.  To do this, we need to pick what counts as a
    489   * container.
    490   *
    491   * From a code perspective, the only hard requirement is that frames
    492   * that are line participants (nsIFrame::IsLineParticipant) are never
    493   * containers, since line layout assumes that the inflation is consistent
    494   * within a line.
    495   *
    496   * This is not an imposition, since we obviously want a bunch of text
    497   * (possibly with inline elements) flowing within a block to count the
    498   * block (or higher) as its container.
    499   *
    500   * We also want form controls, including the text in the anonymous
    501   * content inside of them, to match each other and the text next to
    502   * them, so they and their anonymous content should also not be a
    503   * container.
    504   *
    505   * However, because we can't reliably compute sizes across XUL during
    506   * reflow, any XUL frame with a XUL parent is always a container.
    507   *
    508   * There are contexts where it would be nice if some blocks didn't
    509   * count as a container, so that, for example, an indented quotation
    510   * didn't end up with a smaller font size.  However, it's hard to
    511   * distinguish these situations where we really do want the indented
    512   * thing to count as a container, so we don't try, and blocks are
    513   * always containers.
    514   */
    515 
    516  // The root frame should always be an inflation container.
    517  if (!aFrame->GetParent()) {
    518    return true;
    519  }
    520 
    521  nsIContent* content = aFrame->GetContent();
    522  if (content && content->IsInNativeAnonymousSubtree()) {
    523    // Native anonymous content shouldn't be a font inflation root,
    524    // except for the custom content container.
    525    return content ==
    526           aFrame->PresContext()->Document()->GetCustomContentContainer();
    527  }
    528 
    529  LayoutFrameType frameType = aFrame->Type();
    530  bool isInline =
    531      aFrame->GetDisplay().IsInlineFlow() || RubyUtils::IsRubyBox(frameType) ||
    532      (aStyleDisplay->IsFloatingStyle() &&
    533       frameType == LayoutFrameType::Letter) ||
    534      // Given multiple frames for the same node, only the
    535      // outer one should be considered a container.
    536      // (Important, e.g., for nsSelectsAreaFrame.)
    537      (aFrame->GetParent()->GetContent() == content) ||
    538      (content &&
    539       // Form controls shouldn't become inflation containers.
    540       (content->IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::optgroup,
    541                                     nsGkAtoms::select, nsGkAtoms::input,
    542                                     nsGkAtoms::button, nsGkAtoms::textarea)));
    543  NS_ASSERTION(!aFrame->IsLineParticipant() || isInline ||
    544                   // br frames and mathml frames report being line
    545                   // participants even when their position or display is
    546                   // set
    547                   aFrame->IsBrFrame() || aFrame->IsMathMLFrame(),
    548               "line participants must not be containers");
    549  return !isInline;
    550 }
    551 
    552 static void MaybeScheduleReflowSVGNonDisplayText(nsIFrame* aFrame) {
    553  if (!aFrame->IsInSVGTextSubtree()) {
    554    return;
    555  }
    556 
    557  // We need to ensure that any non-display SVGTextFrames get reflowed when a
    558  // child text frame gets new style. Thus we need to schedule a reflow in
    559  // |DidSetComputedStyle|. We also need to call it from |DestroyFrom|,
    560  // because otherwise we won't get notified when style changes to
    561  // "display:none".
    562  SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
    563      nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::SVGText));
    564  nsIFrame* anonBlock = svgTextFrame->PrincipalChildList().FirstChild();
    565 
    566  // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
    567  // anonymous block frame rather than our aFrame, since NS_FRAME_FIRST_REFLOW
    568  // may be set on us if we're a new frame that has been inserted after the
    569  // document's first reflow. (In which case this DidSetComputedStyle call may
    570  // be happening under frame construction under a Reflow() call.)
    571  if (!anonBlock || anonBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
    572    return;
    573  }
    574 
    575  if (!svgTextFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) ||
    576      svgTextFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)) {
    577    return;
    578  }
    579 
    580  svgTextFrame->ScheduleReflowSVGNonDisplayText(
    581      IntrinsicDirty::FrameAncestorsAndDescendants);
    582 }
    583 
    584 bool nsIFrame::IsReplaced() const {
    585  if (HasAnyClassFlag(ClassFlags::Replaced)) {
    586    return true;
    587  }
    588  if (!Style()->IsAnonBox() && mContent->IsHTMLElement(nsGkAtoms::button)) {
    589    // Button always behaves as a replaced element.
    590    return true;
    591  }
    592  return false;
    593 }
    594 
    595 bool nsIFrame::ShouldPropagateRepaintsToRoot() const {
    596  if (!IsPrimaryFrame()) {
    597    // special case for table frames because style images are associated to the
    598    // table frame, but the table wrapper frame is the primary frame
    599    if (IsTableFrame()) {
    600      MOZ_ASSERT(GetParent() && GetParent()->IsTableWrapperFrame());
    601      return GetParent()->ShouldPropagateRepaintsToRoot();
    602    }
    603 
    604    return false;
    605  }
    606  nsIContent* content = GetContent();
    607  Document* document = content->OwnerDoc();
    608  return content == document->GetRootElement() ||
    609         content == document->GetBodyElement();
    610 }
    611 
    612 bool nsIFrame::IsRenderedLegend() const {
    613  if (auto* parent = GetParent(); parent && parent->IsFieldSetFrame()) {
    614    return static_cast<nsFieldSetFrame*>(parent)->GetLegend() == this;
    615  }
    616  return false;
    617 }
    618 
    619 void nsIFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
    620                    nsIFrame* aPrevInFlow) {
    621  MOZ_ASSERT(nsQueryFrame::FrameIID(mClass) == GetFrameId());
    622  MOZ_ASSERT(!mContent, "Double-initing a frame?");
    623 
    624  mContent = aContent;
    625  mParent = aParent;
    626  MOZ_ASSERT(!mParent || PresShell() == mParent->PresShell());
    627 
    628  if (aPrevInFlow) {
    629    mWritingMode = aPrevInFlow->GetWritingMode();
    630 
    631    // Copy some state bits from prev-in-flow (the bits that should apply
    632    // throughout a continuation chain). The bits are sorted according to their
    633    // order in nsFrameStateBits.h.
    634 
    635    // clang-format off
    636    AddStateBits(aPrevInFlow->GetStateBits() &
    637                 (NS_FRAME_GENERATED_CONTENT |
    638                  NS_FRAME_OUT_OF_FLOW |
    639                  NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN |
    640                  NS_FRAME_PART_OF_IBSPLIT |
    641                  NS_FRAME_MAY_BE_TRANSFORMED |
    642                  NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR));
    643    // clang-format on
    644 
    645    // Copy other bits in nsIFrame from prev-in-flow.
    646    mHasColumnSpanSiblings = aPrevInFlow->HasColumnSpanSiblings();
    647 
    648    // If our prev-in-flow is an absolute containing block, we must be one, too.
    649    if (aPrevInFlow->IsAbsoluteContainer()) {
    650      MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN),
    651                 "We should've carried this bit from our prev-in-flow!");
    652      MarkAsAbsoluteContainingBlock();
    653    }
    654  } else {
    655    PresContext()->ConstructedFrame();
    656  }
    657 
    658  if (GetParent()) {
    659    if (MOZ_UNLIKELY(mContent == PresContext()->Document()->GetRootElement() &&
    660                     mContent == GetParent()->GetContent())) {
    661      // Our content is the root element and we have the same content as our
    662      // parent. That is, we are the internal anonymous frame of the root
    663      // element. Copy the used mWritingMode from our parent because
    664      // mDocElementContainingBlock gets its mWritingMode from <body>.
    665      mWritingMode = GetParent()->GetWritingMode();
    666    }
    667 
    668    // Copy some state bits from our parent (the bits that should apply
    669    // recursively throughout a subtree). The bits are sorted according to their
    670    // order in nsFrameStateBits.h.
    671 
    672    // clang-format off
    673    AddStateBits(GetParent()->GetStateBits() &
    674                 (NS_FRAME_GENERATED_CONTENT |
    675                  NS_FRAME_IS_SVG_TEXT |
    676                  NS_FRAME_IN_POPUP |
    677                  NS_FRAME_IS_NONDISPLAY));
    678    // clang-format on
    679 
    680    if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
    681      // Assume all frames in popups are visible.
    682      IncApproximateVisibleCount();
    683    }
    684  }
    685  if (aPrevInFlow) {
    686    mMayHaveOpacityAnimation = aPrevInFlow->MayHaveOpacityAnimation();
    687    mMayHaveTransformAnimation = aPrevInFlow->MayHaveTransformAnimation();
    688  } else if (mContent) {
    689    // It's fine to fetch the EffectSet for the style frame here because in the
    690    // following code we take care of the case where animations may target
    691    // a different frame.
    692    EffectSet* effectSet = EffectSet::GetForStyleFrame(this);
    693    if (effectSet) {
    694      mMayHaveOpacityAnimation = effectSet->MayHaveOpacityAnimation();
    695 
    696      if (effectSet->MayHaveTransformAnimation()) {
    697        // If we are the inner table frame for display:table content, then
    698        // transform animations should go on our parent frame (the table wrapper
    699        // frame).
    700        //
    701        // We do this when initializing the child frame (table inner frame),
    702        // because when initializng the table wrapper frame, we don't yet have
    703        // access to its children so we can't tell if we have transform
    704        // animations or not.
    705        if (SupportsCSSTransforms()) {
    706          mMayHaveTransformAnimation = true;
    707          AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
    708        } else if (aParent && nsLayoutUtils::GetStyleFrame(aParent) == this) {
    709          MOZ_ASSERT(
    710              aParent->SupportsCSSTransforms(),
    711              "Style frames that don't support transforms should have parents"
    712              " that do");
    713          aParent->mMayHaveTransformAnimation = true;
    714          aParent->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
    715        }
    716      }
    717    }
    718  }
    719 
    720  const nsStyleDisplay* disp = StyleDisplay();
    721  if (disp->HasTransform(this)) {
    722    // If 'transform' dynamically changes, RestyleManager takes care of
    723    // updating this bit.
    724    AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
    725  }
    726 
    727  if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) ||
    728      !GetParent()
    729 #ifdef DEBUG
    730      // We have assertions that check inflation invariants even when
    731      // font size inflation is not enabled.
    732      || true
    733 #endif
    734  ) {
    735    if (IsFontSizeInflationContainer(this, disp)) {
    736      AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
    737      if (!GetParent() ||
    738          // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
    739          disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this) ||
    740          GetParent()->IsFlexContainerFrame() ||
    741          GetParent()->IsGridContainerFrame()) {
    742        AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
    743      }
    744    }
    745    NS_ASSERTION(
    746        GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER),
    747        "root frame should always be a container");
    748  }
    749 
    750  if (TrackingVisibility() && PresShell()->AssumeAllFramesVisible()) {
    751    IncApproximateVisibleCount();
    752  }
    753 
    754  DidSetComputedStyle(nullptr);
    755 
    756  // For a newly created frame, we need to update this frame's visibility state.
    757  // Usually we update the state when the frame is restyled and has a
    758  // VisibilityChange change hint but we don't generate any change hints for
    759  // newly created frames.
    760  // Note: We don't need to do this for placeholders since placeholders have
    761  // different styles so that the styles don't have visibility:hidden even if
    762  // the parent has visibility:hidden style. We also don't need to update the
    763  // state when creating continuations because its visibility is the same as its
    764  // prev-in-flow, and the animation code cares only primary frames.
    765  if (!IsPlaceholderFrame() && !aPrevInFlow) {
    766    UpdateVisibleDescendantsState();
    767  }
    768 
    769  if (!aPrevInFlow && HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    770    // We aren't going to get a reflow, so nothing else will call
    771    // InvalidateRenderingObservers, we have to do it here.
    772    SVGObserverUtils::InvalidateRenderingObservers(this);
    773  }
    774 }
    775 
    776 void nsIFrame::InitPrimaryFrame() {
    777  MOZ_ASSERT(IsPrimaryFrame());
    778  HandlePrimaryFrameStyleChange(nullptr);
    779 }
    780 
    781 void nsIFrame::HandlePrimaryFrameStyleChange(ComputedStyle* aOldStyle) {
    782  const nsStyleDisplay* disp = StyleDisplay();
    783  const nsStyleDisplay* oldDisp =
    784      aOldStyle ? aOldStyle->StyleDisplay() : nullptr;
    785 
    786  const bool wasQueryContainer = oldDisp && oldDisp->IsQueryContainer();
    787  const bool isQueryContainer = disp->IsQueryContainer();
    788  if (wasQueryContainer != isQueryContainer) {
    789    auto* pc = PresContext();
    790    if (isQueryContainer) {
    791      pc->RegisterContainerQueryFrame(this);
    792    } else {
    793      pc->UnregisterContainerQueryFrame(this);
    794    }
    795  }
    796 
    797  const bool wasReferringToAnchor = aOldStyle &&
    798                                    oldDisp->IsAbsolutelyPositionedStyle() &&
    799                                    aOldStyle->HasAnchorPosReference();
    800  const bool isReferringToAnchor = HasAnchorPosReference();
    801  if (wasReferringToAnchor && !isReferringToAnchor) {
    802    PresShell()->RemoveAnchorPosPositioned(this);
    803    RemoveProperty(NormalPositionProperty());
    804  } else if (!wasReferringToAnchor && isReferringToAnchor) {
    805    PresShell()->AddAnchorPosPositioned(this);
    806  }
    807 
    808  bool handleAnchorPosAnchorNameChange =
    809      oldDisp ? oldDisp->mAnchorName != disp->mAnchorName
    810              : disp->HasAnchorName();
    811  if (handleAnchorPosAnchorNameChange &&
    812      !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    813    // TODO: Add invalidation.
    814    // TODO: Only remove/add the necessary names below.
    815    if (oldDisp && oldDisp->HasAnchorName()) {
    816      for (const auto& name : oldDisp->mAnchorName.AsSpan()) {
    817        PresShell()->RemoveAnchorPosAnchor(name.AsAtom(), this);
    818      }
    819    }
    820    for (const auto& name : disp->mAnchorName.AsSpan()) {
    821      PresShell()->AddAnchorPosAnchor(name.AsAtom(), this);
    822    }
    823  }
    824 
    825  // According to the Anchor Positioning spec,
    826  // https://drafts.csswg.org/css-anchor-position-1/#last-successful-position-option:
    827  //
    828  //   If el has a last successful position option remove its last successful
    829  //   position option if any of the following are true:
    830  //     1. Its computed position value has changed, its containing block has
    831  //        changed, or it no longer generates a box.
    832  //     2. Its computed value for any longhand of position-try has changed.
    833  //     3. Its computed value for any @position-try property has changed.
    834  //     4. Any of the @position-try rules referenced by it have been added,
    835  //        removed, or mutated.
    836  //
    837  // Case 1 will cause a reframe, so does not need to be handled here.
    838  // Cases 2 and 3 are handled here.
    839  // TODO: case 4, see bug 1987960.
    840  if (aOldStyle && HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
    841      HasProperty(LastSuccessfulPositionFallback())) {
    842    const auto* pos = StylePosition();
    843    const auto* oldPos = aOldStyle->StylePosition();
    844    if (pos->mPositionTryFallbacks != oldPos->mPositionTryFallbacks ||
    845        pos->mPositionTryOrder != oldPos->mPositionTryOrder ||
    846        pos->mOffset != oldPos->mOffset ||
    847        pos->mAlignSelf != oldPos->mAlignSelf ||
    848        pos->mJustifySelf != oldPos->mJustifySelf ||
    849        pos->mPositionAnchor != oldPos->mPositionAnchor ||
    850        pos->mPositionArea != oldPos->mPositionArea ||
    851        pos->mMinWidth != oldPos->mMinWidth ||
    852        pos->mMinHeight != oldPos->mMinHeight ||
    853        pos->mMaxWidth != oldPos->mMaxWidth ||
    854        pos->mMaxHeight != oldPos->mMaxHeight ||
    855        pos->mWidth != oldPos->mWidth || pos->mHeight != oldPos->mHeight ||
    856        StyleMargin()->mMargin != aOldStyle->StyleMargin()->mMargin) {
    857      RemoveProperty(LastSuccessfulPositionFallback());
    858    }
    859  }
    860 
    861  const auto cv = disp->ContentVisibility(*this);
    862  if (!oldDisp || oldDisp->ContentVisibility(*this) != cv) {
    863    if (cv == StyleContentVisibility::Auto) {
    864      PresShell()->RegisterContentVisibilityAutoFrame(this);
    865    } else {
    866      if (auto* element = Element::FromNodeOrNull(GetContent())) {
    867        element->ClearContentRelevancy();
    868      }
    869      PresShell()->UnregisterContentVisibilityAutoFrame(this);
    870    }
    871    PresContext()->SetNeedsToUpdateHiddenByContentVisibilityForAnimations();
    872  }
    873 
    874  HandleLastRememberedSize();
    875 }
    876 
    877 void nsIFrame::Destroy(DestroyContext& aContext) {
    878  NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
    879               "destroy called on frame while scripts not blocked");
    880  NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
    881               "Frames should be removed before destruction.");
    882  MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
    883  MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),
    884             "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?");
    885 
    886  MaybeScheduleReflowSVGNonDisplayText(this);
    887 
    888  SVGObserverUtils::InvalidateDirectRenderingObservers(
    889      this, SVGObserverUtils::INVALIDATE_DESTROY);
    890 
    891  const auto* disp = StyleDisplay();
    892  if (disp->mPosition == StylePositionProperty::Sticky) {
    893    if (auto* ssc = StickyScrollContainer::GetOrCreateForFrame(this)) {
    894      ssc->RemoveFrame(this);
    895    }
    896  }
    897 
    898  if (HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
    899    if (nsPlaceholderFrame* placeholder = GetPlaceholderFrame()) {
    900      placeholder->SetOutOfFlowFrame(nullptr);
    901    }
    902  }
    903 
    904  nsPresContext* pc = PresContext();
    905  mozilla::PresShell* ps = pc->GetPresShell();
    906  if (IsPrimaryFrame()) {
    907    if (disp->IsQueryContainer()) {
    908      pc->UnregisterContainerQueryFrame(this);
    909    }
    910    if (disp->ContentVisibility(*this) == StyleContentVisibility::Auto) {
    911      ps->UnregisterContentVisibilityAutoFrame(this);
    912    }
    913    // This needs to happen before we clear our Properties() table.
    914    ActiveLayerTracker::TransferActivityToContent(this, mContent);
    915  }
    916 
    917  ScrollAnchorContainer* anchor = nullptr;
    918  if (IsScrollAnchor(&anchor)) {
    919    anchor->InvalidateAnchor();
    920  }
    921 
    922  if (HasCSSAnimations() || HasCSSTransitions() ||
    923      // It's fine to look up the style frame here since if we're destroying the
    924      // frames for display:table content we should be destroying both wrapper
    925      // and inner frame.
    926      EffectSet::GetForStyleFrame(this)) {
    927    // If no new frame for this element is created by the end of the
    928    // restyling process, stop animations and transitions for this frame
    929    RestyleManager::AnimationsWithDestroyedFrame* adf =
    930        pc->RestyleManager()->GetAnimationsWithDestroyedFrame();
    931    // AnimationsWithDestroyedFrame only lives during the restyling process.
    932    if (adf) {
    933      adf->Put(mContent, mComputedStyle);
    934    }
    935  }
    936 
    937  if (HasAnchorPosName()) {
    938    for (const auto& name : disp->mAnchorName.AsSpan()) {
    939      PresShell()->RemoveAnchorPosAnchor(name.AsAtom(), this);
    940    }
    941  }
    942 
    943  if (HasAnchorPosReference()) {
    944    ps->RemoveAnchorPosPositioned(this);
    945  }
    946 
    947  // Disable visibility tracking. Note that we have to do this before we clear
    948  // frame properties and lose track of whether we were previously visible.
    949  // XXX(seth): It'd be ideal to assert that we're already marked nonvisible
    950  // here, but it's unfortunately tricky to guarantee in the face of things like
    951  // frame reconstruction induced by style changes.
    952  DisableVisibilityTracking();
    953 
    954  // Ensure that we're not in the approximately visible list anymore.
    955  ps->RemoveFrameFromApproximatelyVisibleList(this);
    956 
    957  ps->NotifyDestroyingFrame(this);
    958 
    959  if (HasAnyStateBits(NS_FRAME_EXTERNAL_REFERENCE)) {
    960    ps->ClearFrameRefs(this);
    961  }
    962 
    963  // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
    964  if (IsPrimaryFrame()) {
    965    mContent->SetPrimaryFrame(nullptr);
    966 
    967    // Pass the root of a generated content subtree (e.g. ::after/::before) to
    968    // aPostDestroyData to unbind it after frame destruction is done.
    969    if (HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
    970        mContent->IsRootOfNativeAnonymousSubtree()) {
    971      aContext.AddAnonymousContent(mContent.forget());
    972    }
    973  }
    974 
    975  // Remove all properties attached to the frame, to ensure any property
    976  // destructors that need the frame pointer are handled properly.
    977  RemoveAllProperties();
    978 
    979  // Must retrieve the object ID before calling destructors, so the
    980  // vtable is still valid.
    981  //
    982  // Note to future tweakers: having the method that returns the
    983  // object size call the destructor will not avoid an indirect call;
    984  // the compiler cannot devirtualize the call to the destructor even
    985  // if it's from a method defined in the same class.
    986 
    987  nsQueryFrame::FrameIID id = GetFrameId();
    988  this->~nsIFrame();
    989 
    990 #ifdef DEBUG
    991  {
    992    nsIFrame* rootFrame = ps->GetRootFrame();
    993    MOZ_ASSERT(rootFrame);
    994    if (this != rootFrame) {
    995      auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(rootFrame);
    996      auto* data = builder ? builder->Data() : nullptr;
    997 
    998      const bool inData =
    999          data && (data->IsModified(this) || data->HasProps(this));
   1000 
   1001      if (inData) {
   1002        DL_LOG(LogLevel::Warning, "Frame %p found in retained data", this);
   1003      }
   1004 
   1005      MOZ_ASSERT(!inData, "Deleted frame in retained data!");
   1006    }
   1007  }
   1008 #endif
   1009 
   1010  // Now that we're totally cleaned out, we need to add ourselves to
   1011  // the presshell's recycler.
   1012  ps->FreeFrame(id, this);
   1013 }
   1014 
   1015 std::pair<int32_t, int32_t> nsIFrame::GetOffsets() const {
   1016  return std::make_pair(0, 0);
   1017 }
   1018 
   1019 static void CompareLayers(
   1020    const nsStyleImageLayers* aFirstLayers,
   1021    const nsStyleImageLayers* aSecondLayers,
   1022    const std::function<void(imgRequestProxy* aReq)>& aCallback) {
   1023  NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aFirstLayers)) {
   1024    const auto& image = aFirstLayers->mLayers[i].mImage;
   1025    if (!image.IsImageRequestType() || !image.IsResolved()) {
   1026      continue;
   1027    }
   1028 
   1029    // aCallback is called when the style image in aFirstLayers is thought to
   1030    // be different with the corresponded one in aSecondLayers
   1031    if (!aSecondLayers || i >= aSecondLayers->mImageCount ||
   1032        (!aSecondLayers->mLayers[i].mImage.IsResolved() ||
   1033         image.GetImageRequest() !=
   1034             aSecondLayers->mLayers[i].mImage.GetImageRequest())) {
   1035      if (imgRequestProxy* req = image.GetImageRequest()) {
   1036        aCallback(req);
   1037      }
   1038    }
   1039  }
   1040 }
   1041 
   1042 static void AddAndRemoveImageAssociations(
   1043    ImageLoader& aImageLoader, nsIFrame* aFrame,
   1044    const nsStyleImageLayers* aOldLayers,
   1045    const nsStyleImageLayers* aNewLayers) {
   1046  // If the old context had a background-image image, or mask-image image,
   1047  // and new context does not have the same image, clear the image load
   1048  // notifier (which keeps the image loading, if it still is) for the frame.
   1049  // We want to do this conservatively because some frames paint their
   1050  // backgrounds from some other frame's style data, and we don't want
   1051  // to clear those notifiers unless we have to.  (They'll be reset
   1052  // when we paint, although we could miss a notification in that
   1053  // interval.)
   1054  if (aOldLayers && aFrame->HasImageRequest()) {
   1055    CompareLayers(aOldLayers, aNewLayers, [&](imgRequestProxy* aReq) {
   1056      aImageLoader.DisassociateRequestFromFrame(aReq, aFrame);
   1057    });
   1058  }
   1059 
   1060  CompareLayers(aNewLayers, aOldLayers, [&](imgRequestProxy* aReq) {
   1061    aImageLoader.AssociateRequestToFrame(aReq, aFrame);
   1062  });
   1063 }
   1064 
   1065 void nsIFrame::AddDisplayItem(nsDisplayItem* aItem) {
   1066  MOZ_DIAGNOSTIC_ASSERT(!mDisplayItems.Contains(aItem));
   1067  mDisplayItems.AppendElement(aItem);
   1068 #ifdef ACCESSIBILITY
   1069  if (nsAccessibilityService* accService = GetAccService()) {
   1070    accService->NotifyOfPossibleBoundsChange(PresShell(), mContent);
   1071  }
   1072 #endif
   1073 }
   1074 
   1075 bool nsIFrame::RemoveDisplayItem(nsDisplayItem* aItem) {
   1076  return mDisplayItems.RemoveElement(aItem);
   1077 }
   1078 
   1079 bool nsIFrame::HasDisplayItems() { return !mDisplayItems.IsEmpty(); }
   1080 
   1081 bool nsIFrame::HasDisplayItem(nsDisplayItem* aItem) {
   1082  return mDisplayItems.Contains(aItem);
   1083 }
   1084 
   1085 bool nsIFrame::HasDisplayItem(uint32_t aKey) {
   1086  for (nsDisplayItem* i : mDisplayItems) {
   1087    if (i->GetPerFrameKey() == aKey) {
   1088      return true;
   1089    }
   1090  }
   1091  return false;
   1092 }
   1093 
   1094 template <typename Condition>
   1095 static void DiscardDisplayItems(nsIFrame* aFrame, Condition aCondition) {
   1096  for (nsDisplayItem* i : aFrame->DisplayItems()) {
   1097    // Only discard items that are invalidated by this frame, as we're only
   1098    // guaranteed to rebuild those items. Table background items are created by
   1099    // the relevant table part, but have the cell frame as the primary frame,
   1100    // and we don't want to remove them if this is the cell.
   1101    if (aCondition(i) && i->FrameForInvalidation() == aFrame) {
   1102      i->SetCantBeReused();
   1103    }
   1104  }
   1105 }
   1106 
   1107 static void DiscardOldItems(nsIFrame* aFrame) {
   1108  DiscardDisplayItems(aFrame,
   1109                      [](nsDisplayItem* aItem) { return aItem->IsOldItem(); });
   1110 }
   1111 
   1112 void nsIFrame::RemoveDisplayItemDataForDeletion() {
   1113  // Destroying a WebRenderUserDataTable can cause destruction of other objects
   1114  // which can remove frame properties in their destructor. If we delete a frame
   1115  // property it runs the destructor of the stored object in the middle of
   1116  // updating the frame property table, so if the destruction of that object
   1117  // causes another update to the frame property table it would leave the frame
   1118  // property table in an inconsistent state. So we remove it from the table and
   1119  // then destroy it. (bug 1530657)
   1120  WebRenderUserDataTable* userDataTable =
   1121      TakeProperty(WebRenderUserDataProperty::Key());
   1122  if (userDataTable) {
   1123    for (const auto& data : userDataTable->Values()) {
   1124      data->RemoveFromTable();
   1125    }
   1126    delete userDataTable;
   1127  }
   1128 
   1129  auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(this);
   1130  if (!builder) {
   1131    MOZ_ASSERT(DisplayItems().IsEmpty());
   1132    MOZ_ASSERT(!IsFrameModified());
   1133    return;
   1134  }
   1135 
   1136  for (nsDisplayItem* i : DisplayItems()) {
   1137    if (i->GetDependentFrame() == this && !i->HasDeletedFrame()) {
   1138      i->Frame()->MarkNeedsDisplayItemRebuild();
   1139    }
   1140    i->RemoveFrame(this);
   1141  }
   1142 
   1143  DisplayItems().Clear();
   1144 
   1145  nsAutoString name;
   1146 #ifdef DEBUG_FRAME_DUMP
   1147  if (DL_LOG_TEST(LogLevel::Debug)) {
   1148    GetFrameName(name);
   1149  }
   1150 #endif
   1151  DL_LOGV("Removing display item data for frame %p (%s)", this,
   1152          NS_ConvertUTF16toUTF8(name).get());
   1153 
   1154  auto* data = builder->Data();
   1155  if (MayHaveWillChangeBudget()) {
   1156    // Keep the frame in list, so it can be removed from the will-change budget.
   1157    data->Flags(this) = RetainedDisplayListData::FrameFlag::HadWillChange;
   1158  } else {
   1159    data->Remove(this);
   1160  }
   1161 }
   1162 
   1163 void nsIFrame::MarkNeedsDisplayItemRebuild() {
   1164  if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() || IsFrameModified() ||
   1165      HasAnyStateBits(NS_FRAME_IN_POPUP)) {
   1166    // Skip frames that are already marked modified.
   1167    return;
   1168  }
   1169 
   1170  if (Type() == LayoutFrameType::Placeholder) {
   1171    nsIFrame* oof = static_cast<nsPlaceholderFrame*>(this)->GetOutOfFlowFrame();
   1172    if (oof) {
   1173      oof->MarkNeedsDisplayItemRebuild();
   1174    }
   1175    // Do not mark placeholder frames modified.
   1176    return;
   1177  }
   1178 
   1179 #ifdef ACCESSIBILITY
   1180  if (nsAccessibilityService* accService = GetAccService()) {
   1181    accService->NotifyOfPossibleBoundsChange(PresShell(), mContent);
   1182  }
   1183 #endif
   1184 
   1185  nsIFrame* rootFrame = PresShell()->GetRootFrame();
   1186 
   1187  if (rootFrame->IsFrameModified()) {
   1188    // The whole frame tree is modified.
   1189    return;
   1190  }
   1191 
   1192  auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(this);
   1193  if (!builder) {
   1194    MOZ_ASSERT(DisplayItems().IsEmpty());
   1195    return;
   1196  }
   1197 
   1198  RetainedDisplayListData* data = builder->Data();
   1199  MOZ_ASSERT(data);
   1200 
   1201  if (data->AtModifiedFrameLimit()) {
   1202    // This marks the whole frame tree modified.
   1203    // See |RetainedDisplayListBuilder::ShouldBuildPartial()|.
   1204    data->AddModifiedFrame(rootFrame);
   1205    return;
   1206  }
   1207 
   1208  nsAutoString name;
   1209 #ifdef DEBUG_FRAME_DUMP
   1210  if (DL_LOG_TEST(LogLevel::Debug)) {
   1211    GetFrameName(name);
   1212  }
   1213 #endif
   1214 
   1215  DL_LOGV("RDL - Rebuilding display items for frame %p (%s)", this,
   1216          NS_ConvertUTF16toUTF8(name).get());
   1217 
   1218  data->AddModifiedFrame(this);
   1219 
   1220  MOZ_ASSERT(
   1221      PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0);
   1222 
   1223  // Hopefully this is cheap, but we could use a frame state bit to note
   1224  // the presence of dependencies to speed it up.
   1225  for (nsDisplayItem* i : DisplayItems()) {
   1226    if (i->HasDeletedFrame() || i->Frame() == this) {
   1227      // Ignore the items with deleted frames, and the items with |this| as
   1228      // the primary frame.
   1229      continue;
   1230    }
   1231 
   1232    if (i->GetDependentFrame() == this) {
   1233      // For items with |this| as a dependent frame, mark the primary frame
   1234      // for rebuild.
   1235      i->Frame()->MarkNeedsDisplayItemRebuild();
   1236    }
   1237  }
   1238 }
   1239 
   1240 // Subclass hook for style post processing
   1241 /* virtual */
   1242 void nsIFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
   1243 #ifdef ACCESSIBILITY
   1244  // Don't notify for reconstructed frames here, since the frame is still being
   1245  // constructed at this point and so LocalAccessible::GetFrame() will return
   1246  // null. Style changes for reconstructed frames are handled in
   1247  // DocAccessible::PruneOrInsertSubtree.
   1248  if (aOldComputedStyle) {
   1249    if (nsAccessibilityService* accService = GetAccService()) {
   1250      accService->NotifyOfComputedStyleChange(PresShell(), mContent);
   1251    }
   1252  }
   1253 #endif
   1254 
   1255  MaybeScheduleReflowSVGNonDisplayText(this);
   1256 
   1257  Document* doc = PresContext()->Document();
   1258  ImageLoader& loader = doc->EnsureStyleImageLoader();
   1259  // Continuing text frame doesn't initialize its continuation pointer before
   1260  // reaching here for the first time, so we have to exclude text frames. This
   1261  // doesn't affect correctness because text can't match selectors.
   1262  //
   1263  // FIXME(emilio): We should consider fixing that.
   1264  //
   1265  // TODO(emilio): Can we avoid doing some / all of the image stuff when
   1266  // isNonTextFirstContinuation is false? We should consider doing this just for
   1267  // primary frames and pseudos, but the first-line reparenting code makes it
   1268  // all bad, should get around to bug 1465474 eventually :(
   1269  const bool isNonText = !IsTextFrame();
   1270  if (isNonText) {
   1271    mComputedStyle->StartImageLoads(*doc, aOldComputedStyle);
   1272  }
   1273 
   1274  const bool isRootElementStyle = Style()->IsRootElementStyle();
   1275  if (isRootElementStyle) {
   1276    PresShell()->SetNeedsWindowPropertiesSync();
   1277  }
   1278 
   1279  const nsStyleImageLayers* oldLayers =
   1280      aOldComputedStyle ? &aOldComputedStyle->StyleBackground()->mImage
   1281                        : nullptr;
   1282  const nsStyleImageLayers* newLayers = &StyleBackground()->mImage;
   1283  AddAndRemoveImageAssociations(loader, this, oldLayers, newLayers);
   1284 
   1285  oldLayers =
   1286      aOldComputedStyle ? &aOldComputedStyle->StyleSVGReset()->mMask : nullptr;
   1287  newLayers = &StyleSVGReset()->mMask;
   1288  AddAndRemoveImageAssociations(loader, this, oldLayers, newLayers);
   1289 
   1290  const nsStyleDisplay* disp = StyleDisplay();
   1291  bool handleStickyChange = false;
   1292  if (aOldComputedStyle) {
   1293    // Detect style changes that should trigger a scroll anchor adjustment
   1294    // suppression.
   1295    // https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers
   1296    bool needScrollAnchorSuppression = false;
   1297 
   1298    const nsStyleMargin* oldMargin = aOldComputedStyle->StyleMargin();
   1299    if (!oldMargin->MarginEquals(*StyleMargin())) {
   1300      needScrollAnchorSuppression = true;
   1301    }
   1302 
   1303    const nsStylePadding* oldPadding = aOldComputedStyle->StylePadding();
   1304    if (oldPadding->mPadding != StylePadding()->mPadding) {
   1305      SetHasPaddingChange(true);
   1306      needScrollAnchorSuppression = true;
   1307    }
   1308 
   1309    const nsStyleDisplay* oldDisp = aOldComputedStyle->StyleDisplay();
   1310    if (oldDisp->mOverflowAnchor != disp->mOverflowAnchor) {
   1311      if (auto* container = ScrollAnchorContainer::FindFor(this)) {
   1312        container->InvalidateAnchor();
   1313      }
   1314      if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(this)) {
   1315        scrollContainerFrame->Anchor()->InvalidateAnchor();
   1316      }
   1317    }
   1318 
   1319    if (mInScrollAnchorChain) {
   1320      const nsStylePosition* pos = StylePosition();
   1321      const nsStylePosition* oldPos = aOldComputedStyle->StylePosition();
   1322      if (!needScrollAnchorSuppression &&
   1323          (oldPos->mOffset != pos->mOffset || oldPos->mWidth != pos->mWidth ||
   1324           oldPos->mMinWidth != pos->mMinWidth ||
   1325           oldPos->mMaxWidth != pos->mMaxWidth ||
   1326           oldPos->mHeight != pos->mHeight ||
   1327           oldPos->mMinHeight != pos->mMinHeight ||
   1328           oldPos->mMaxHeight != pos->mMaxHeight ||
   1329           oldDisp->mPosition != disp->mPosition ||
   1330           oldDisp->mTransform != disp->mTransform)) {
   1331        needScrollAnchorSuppression = true;
   1332      }
   1333 
   1334      if (needScrollAnchorSuppression &&
   1335          StaticPrefs::layout_css_scroll_anchoring_suppressions_enabled()) {
   1336        ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
   1337      }
   1338    }
   1339 
   1340    if (disp->mPosition != oldDisp->mPosition) {
   1341      if (!disp->IsRelativelyOrStickyPositionedStyle() &&
   1342          oldDisp->IsRelativelyOrStickyPositionedStyle()) {
   1343        RemoveProperty(NormalPositionProperty());
   1344      }
   1345 
   1346      handleStickyChange = disp->mPosition == StylePositionProperty::Sticky ||
   1347                           oldDisp->mPosition == StylePositionProperty::Sticky;
   1348    }
   1349    if (disp->mScrollSnapAlign != oldDisp->mScrollSnapAlign) {
   1350      ScrollSnapUtils::PostPendingResnapFor(this);
   1351    }
   1352    if (isRootElementStyle &&
   1353        disp->mScrollSnapType != oldDisp->mScrollSnapType) {
   1354      if (ScrollContainerFrame* sf =
   1355              PresShell()->GetRootScrollContainerFrame()) {
   1356        sf->PostPendingResnap();
   1357      }
   1358    }
   1359    if (StyleUIReset()->mMozSubtreeHiddenOnlyVisually &&
   1360        !aOldComputedStyle->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) {
   1361      PresShell::ClearMouseCapture(this);
   1362    }
   1363  } else {  // !aOldComputedStyle
   1364    handleStickyChange = disp->mPosition == StylePositionProperty::Sticky;
   1365  }
   1366 
   1367  if (handleStickyChange && !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) &&
   1368      !GetPrevInFlow()) {
   1369    // Note that we only add first continuations, but we really only
   1370    // want to add first continuation-or-ib-split-siblings. But since we don't
   1371    // yet know if we're a later part of a block-in-inline split, we'll just
   1372    // add later members of a block-in-inline split here, and then
   1373    // StickyScrollContainer will remove them later.
   1374    if (auto* ssc = StickyScrollContainer::GetOrCreateForFrame(this)) {
   1375      if (disp->mPosition == StylePositionProperty::Sticky) {
   1376        ssc->AddFrame(this);
   1377      } else {
   1378        ssc->RemoveFrame(this);
   1379      }
   1380    }
   1381  }
   1382 
   1383  imgIRequest* oldBorderImage =
   1384      aOldComputedStyle
   1385          ? aOldComputedStyle->StyleBorder()->GetBorderImageRequest()
   1386          : nullptr;
   1387  imgIRequest* newBorderImage = StyleBorder()->GetBorderImageRequest();
   1388  // FIXME (Bug 759996): The following is no longer true.
   1389  // For border-images, we can't be as conservative (we need to set the
   1390  // new loaders if there has been any change) since the CalcDifference
   1391  // call depended on the result of GetComputedBorder() and that result
   1392  // depends on whether the image has loaded, start the image load now
   1393  // so that we'll get notified when it completes loading and can do a
   1394  // restyle.  Otherwise, the image might finish loading from the
   1395  // network before we start listening to its notifications, and then
   1396  // we'll never know that it's finished loading.  Likewise, we want to
   1397  // do this for freshly-created frames to prevent a similar race if the
   1398  // image loads between reflow (which can depend on whether the image
   1399  // is loaded) and paint.  We also don't really care about any callers who try
   1400  // to paint borders with a different style, because they won't have the
   1401  // correct size for the border either.
   1402  if (oldBorderImage != newBorderImage) {
   1403    // stop and restart the image loading/notification
   1404    if (oldBorderImage && HasImageRequest()) {
   1405      loader.DisassociateRequestFromFrame(oldBorderImage, this);
   1406    }
   1407    if (newBorderImage) {
   1408      loader.AssociateRequestToFrame(newBorderImage, this);
   1409    }
   1410  }
   1411 
   1412  auto GetShapeImageRequest = [](const ComputedStyle* aStyle) -> imgIRequest* {
   1413    if (!aStyle) {
   1414      return nullptr;
   1415    }
   1416    auto& shape = aStyle->StyleDisplay()->mShapeOutside;
   1417    if (!shape.IsImage()) {
   1418      return nullptr;
   1419    }
   1420    return shape.AsImage().GetImageRequest();
   1421  };
   1422 
   1423  imgIRequest* oldShapeImage = GetShapeImageRequest(aOldComputedStyle);
   1424  imgIRequest* newShapeImage = GetShapeImageRequest(Style());
   1425  if (oldShapeImage != newShapeImage) {
   1426    if (oldShapeImage && HasImageRequest()) {
   1427      loader.DisassociateRequestFromFrame(oldShapeImage, this);
   1428    }
   1429    if (newShapeImage) {
   1430      loader.AssociateRequestToFrame(
   1431          newShapeImage, this,
   1432          ImageLoader::Flags::
   1433              RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking);
   1434    }
   1435  }
   1436 
   1437  // SVGObserverUtils::GetEffectProperties() asserts that we only invoke it with
   1438  // the first continuation so we need to check that in advance.
   1439  const bool isNonTextFirstContinuation = isNonText && !GetPrevContinuation();
   1440  if (isNonTextFirstContinuation) {
   1441    // Kick off loading of external SVG resources referenced from properties if
   1442    // any. This currently includes filter, clip-path, and mask.
   1443    SVGObserverUtils::InitiateResourceDocLoads(this);
   1444  }
   1445 
   1446  // If the page contains markup that overrides text direction, and
   1447  // does not contain any characters that would activate the Unicode
   1448  // bidi algorithm, we need to call |SetBidiEnabled| on the pres
   1449  // context before reflow starts.  See bug 115921.
   1450  if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
   1451    PresContext()->SetBidiEnabled();
   1452  }
   1453 
   1454  // The following part is for caching offset-path:path(). We cache the
   1455  // flatten gfx path, so we don't have to rebuild and re-flattern it at
   1456  // each cycle if we have animations on offset-* with a fixed offset-path.
   1457  const StyleOffsetPath* oldPath =
   1458      aOldComputedStyle ? &aOldComputedStyle->StyleDisplay()->mOffsetPath
   1459                        : nullptr;
   1460  const StyleOffsetPath& newPath = StyleDisplay()->mOffsetPath;
   1461  if (!oldPath || *oldPath != newPath) {
   1462    // FIXME: Bug 1837042. Cache all basic shapes.
   1463    if (newPath.IsPath()) {
   1464      RefPtr<gfx::PathBuilder> builder = MotionPathUtils::GetPathBuilder();
   1465      RefPtr<gfx::Path> path =
   1466          MotionPathUtils::BuildSVGPath(newPath.AsSVGPathData(), builder);
   1467      if (path) {
   1468        // The newPath could be path('') (i.e. empty path), so its gfx path
   1469        // could be nullptr, and so we only set property for a non-empty path.
   1470        SetProperty(nsIFrame::OffsetPathCache(), path.forget().take());
   1471      } else {
   1472        // May have an old cached path, so we have to delete it.
   1473        RemoveProperty(nsIFrame::OffsetPathCache());
   1474      }
   1475    } else if (oldPath) {
   1476      RemoveProperty(nsIFrame::OffsetPathCache());
   1477    }
   1478  }
   1479 
   1480  if (IsPrimaryFrame()) {
   1481    MOZ_ASSERT(aOldComputedStyle);
   1482    HandlePrimaryFrameStyleChange(aOldComputedStyle);
   1483  }
   1484 
   1485  RemoveStateBits(NS_FRAME_SIMPLE_DISPLAYLIST);
   1486 
   1487  mMayHaveRoundedCorners = true;
   1488 }
   1489 
   1490 void nsIFrame::HandleLastRememberedSize() {
   1491  MOZ_ASSERT(IsPrimaryFrame());
   1492  auto* element = Element::FromNodeOrNull(mContent);
   1493  if (!element) {
   1494    return;
   1495  }
   1496  const WritingMode wm = GetWritingMode();
   1497  const nsStylePosition* stylePos = StylePosition();
   1498  bool canRememberBSize = stylePos->ContainIntrinsicBSize(wm).HasAuto();
   1499  bool canRememberISize = stylePos->ContainIntrinsicISize(wm).HasAuto();
   1500  if (!canRememberBSize) {
   1501    element->RemoveLastRememberedBSize();
   1502  }
   1503  if (!canRememberISize) {
   1504    element->RemoveLastRememberedISize();
   1505  }
   1506  if ((canRememberBSize || canRememberISize) && !HidesContent()) {
   1507    bool isNonReplacedInline = IsLineParticipant() && !IsReplaced();
   1508    if (!isNonReplacedInline) {
   1509      PresContext()->Document()->ObserveForLastRememberedSize(*element);
   1510      return;
   1511    }
   1512  }
   1513  PresContext()->Document()->UnobserveForLastRememberedSize(*element);
   1514 }
   1515 
   1516 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   1517 void nsIFrame::AssertNewStyleIsSane(ComputedStyle& aNewStyle) {
   1518  MOZ_DIAGNOSTIC_ASSERT(
   1519      aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() ||
   1520      // ::first-line continuations are weird, this should probably be fixed via
   1521      // bug 1465474.
   1522      (mComputedStyle->GetPseudoType() == PseudoStyleType::firstLine &&
   1523       aNewStyle.GetPseudoType() == PseudoStyleType::mozLineFrame) ||
   1524      // ::first-letter continuations are broken, in particular floating ones,
   1525      // see bug 1490281. The construction code tries to fix this up after the
   1526      // fact, then restyling undoes it...
   1527      (mComputedStyle->GetPseudoType() == PseudoStyleType::mozText &&
   1528       aNewStyle.GetPseudoType() == PseudoStyleType::firstLetterContinuation) ||
   1529      (mComputedStyle->GetPseudoType() ==
   1530           PseudoStyleType::firstLetterContinuation &&
   1531       aNewStyle.GetPseudoType() == PseudoStyleType::mozText));
   1532 }
   1533 #endif
   1534 
   1535 /* virtual */
   1536 nsMargin nsIFrame::GetUsedMargin() const {
   1537  nsMargin margin;
   1538  if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
   1539      IsInSVGTextSubtree()) {
   1540    return margin;
   1541  }
   1542 
   1543  if (nsMargin* m = GetProperty(UsedMarginProperty())) {
   1544    margin = *m;
   1545  } else if (!StyleMargin()->GetMargin(margin)) {
   1546    // If we get here, our caller probably shouldn't be calling us...
   1547    NS_ERROR(
   1548        "Returning bogus 0-sized margin, because this margin "
   1549        "depends on layout & isn't cached!");
   1550  }
   1551  return margin;
   1552 }
   1553 
   1554 /* virtual */
   1555 nsMargin nsIFrame::GetUsedBorder() const {
   1556  if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
   1557      IsInSVGTextSubtree()) {
   1558    return {};
   1559  }
   1560 
   1561  const nsStyleDisplay* disp = StyleDisplay();
   1562  if (IsThemed(disp)) {
   1563    // Theme methods don't use const-ness.
   1564    auto* mutable_this = const_cast<nsIFrame*>(this);
   1565    nsPresContext* pc = PresContext();
   1566    LayoutDeviceIntMargin widgetBorder = pc->Theme()->GetWidgetBorder(
   1567        pc->DeviceContext(), mutable_this, disp->EffectiveAppearance());
   1568    return LayoutDevicePixel::ToAppUnits(widgetBorder,
   1569                                         pc->AppUnitsPerDevPixel());
   1570  }
   1571 
   1572  return StyleBorder()->GetComputedBorder();
   1573 }
   1574 
   1575 /* virtual */
   1576 nsMargin nsIFrame::GetUsedPadding() const {
   1577  nsMargin padding;
   1578  if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
   1579      IsInSVGTextSubtree()) {
   1580    return padding;
   1581  }
   1582 
   1583  const nsStyleDisplay* disp = StyleDisplay();
   1584  if (IsThemed(disp)) {
   1585    // Theme methods don't use const-ness.
   1586    nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
   1587    nsPresContext* pc = PresContext();
   1588    LayoutDeviceIntMargin widgetPadding;
   1589    if (pc->Theme()->GetWidgetPadding(pc->DeviceContext(), mutable_this,
   1590                                      disp->EffectiveAppearance(),
   1591                                      &widgetPadding)) {
   1592      return LayoutDevicePixel::ToAppUnits(widgetPadding,
   1593                                           pc->AppUnitsPerDevPixel());
   1594    }
   1595  }
   1596 
   1597  if (nsMargin* p = GetProperty(UsedPaddingProperty())) {
   1598    padding = *p;
   1599  } else if (!StylePadding()->GetPadding(padding)) {
   1600    // If we get here, our caller probably shouldn't be calling us...
   1601    NS_ERROR(
   1602        "Returning bogus 0-sized padding, because this padding "
   1603        "depends on layout & isn't cached!");
   1604  }
   1605  return padding;
   1606 }
   1607 
   1608 nsIFrame::Sides nsIFrame::GetSkipSides() const {
   1609  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
   1610                   StyleBoxDecorationBreak::Clone) &&
   1611      !HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   1612    return Sides();
   1613  }
   1614 
   1615  // Convert the logical skip sides to physical sides using the frame's
   1616  // writing mode
   1617  WritingMode writingMode = GetWritingMode();
   1618  LogicalSides logicalSkip = GetLogicalSkipSides();
   1619  Sides skip;
   1620 
   1621  if (logicalSkip.BStart()) {
   1622    if (writingMode.IsVertical()) {
   1623      skip |= writingMode.IsVerticalLR() ? SideBits::eLeft : SideBits::eRight;
   1624    } else {
   1625      skip |= SideBits::eTop;
   1626    }
   1627  }
   1628 
   1629  if (logicalSkip.BEnd()) {
   1630    if (writingMode.IsVertical()) {
   1631      skip |= writingMode.IsVerticalLR() ? SideBits::eRight : SideBits::eLeft;
   1632    } else {
   1633      skip |= SideBits::eBottom;
   1634    }
   1635  }
   1636 
   1637  if (logicalSkip.IStart()) {
   1638    if (writingMode.IsVertical()) {
   1639      skip |= SideBits::eTop;
   1640    } else {
   1641      skip |= writingMode.IsBidiLTR() ? SideBits::eLeft : SideBits::eRight;
   1642    }
   1643  }
   1644 
   1645  if (logicalSkip.IEnd()) {
   1646    if (writingMode.IsVertical()) {
   1647      skip |= SideBits::eBottom;
   1648    } else {
   1649      skip |= writingMode.IsBidiLTR() ? SideBits::eRight : SideBits::eLeft;
   1650    }
   1651  }
   1652  return skip;
   1653 }
   1654 
   1655 nsRect nsIFrame::GetPaddingRectRelativeToSelf() const {
   1656  nsMargin border = GetUsedBorder().ApplySkipSides(GetSkipSides());
   1657  nsRect r(0, 0, mRect.width, mRect.height);
   1658  r.Deflate(border);
   1659  return r;
   1660 }
   1661 
   1662 nsRect nsIFrame::GetPaddingRect() const {
   1663  return GetPaddingRectRelativeToSelf() + GetPosition();
   1664 }
   1665 
   1666 WritingMode nsIFrame::WritingModeForLine(WritingMode aSelfWM,
   1667                                         nsIFrame* aSubFrame) const {
   1668  MOZ_ASSERT(aSelfWM == GetWritingMode());
   1669  WritingMode writingMode = aSelfWM;
   1670 
   1671  if (StyleTextReset()->mUnicodeBidi == StyleUnicodeBidi::Plaintext) {
   1672    mozilla::intl::BidiEmbeddingLevel frameLevel =
   1673        nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
   1674    writingMode.SetDirectionFromBidiLevel(frameLevel);
   1675  }
   1676 
   1677  return writingMode;
   1678 }
   1679 
   1680 nsRect nsIFrame::GetMarginRect() const {
   1681  return GetMarginRectRelativeToSelf() + GetPosition();
   1682 }
   1683 
   1684 nsRect nsIFrame::GetMarginRectRelativeToSelf() const {
   1685  nsMargin m = GetUsedMargin().ApplySkipSides(GetSkipSides());
   1686  nsRect r(0, 0, mRect.width, mRect.height);
   1687  r.Inflate(m);
   1688  return r;
   1689 }
   1690 
   1691 bool nsIFrame::IsTransformed() const {
   1692  if (!HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED)) {
   1693    MOZ_ASSERT(!IsCSSTransformed());
   1694    MOZ_ASSERT(!GetParentSVGTransforms());
   1695    return false;
   1696  }
   1697  return IsCSSTransformed() || GetParentSVGTransforms();
   1698 }
   1699 
   1700 bool nsIFrame::IsCSSTransformed() const {
   1701  return HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
   1702         (StyleDisplay()->HasTransform(this) || HasAnimationOfTransform());
   1703 }
   1704 
   1705 bool nsIFrame::HasAnimationOfTransform() const {
   1706  if (!MayHaveTransformAnimation()) {
   1707    MOZ_ASSERT(!IsPrimaryFrame() || !SupportsCSSTransforms() ||
   1708               !nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this));
   1709    return false;
   1710  }
   1711  return IsPrimaryFrame() && SupportsCSSTransforms() &&
   1712         nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this);
   1713 }
   1714 
   1715 bool nsIFrame::ChildrenHavePerspective(
   1716    const nsStyleDisplay* aStyleDisplay) const {
   1717  MOZ_ASSERT(aStyleDisplay == StyleDisplay());
   1718  return aStyleDisplay->HasPerspective(this);
   1719 }
   1720 
   1721 bool nsIFrame::HasAnimationOfOpacity(EffectSet* aEffectSet) const {
   1722  return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
   1723           nsLayoutUtils::FirstContinuationOrIBSplitSibling(this)
   1724               ->IsPrimaryFrame()) &&
   1725          nsLayoutUtils::HasAnimationOfPropertySet(
   1726              this, nsCSSPropertyIDSet::OpacityProperties(), aEffectSet));
   1727 }
   1728 
   1729 bool nsIFrame::HasOpacityInternal(float aThreshold,
   1730                                  const nsStyleDisplay* aStyleDisplay,
   1731                                  const nsStyleEffects* aStyleEffects,
   1732                                  EffectSet* aEffectSet) const {
   1733  MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
   1734  if (aStyleEffects->mOpacity < aThreshold ||
   1735      aStyleDisplay->mWillChange.bits & StyleWillChangeBits::OPACITY) {
   1736    return true;
   1737  }
   1738 
   1739  if (!mMayHaveOpacityAnimation) {
   1740    return false;
   1741  }
   1742 
   1743  return HasAnimationOfOpacity(aEffectSet);
   1744 }
   1745 
   1746 bool nsIFrame::DoGetParentSVGTransforms(gfx::Matrix*) const { return false; }
   1747 
   1748 bool nsIFrame::Extend3DContext(const nsStyleDisplay* aStyleDisplay,
   1749                               const nsStyleEffects* aStyleEffects,
   1750                               mozilla::EffectSet* aEffectSetForOpacity) const {
   1751  if (!HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED)) {
   1752    return false;
   1753  }
   1754  const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
   1755  if (disp->mTransformStyle != StyleTransformStyle::Preserve3d ||
   1756      !SupportsCSSTransforms()) {
   1757    return false;
   1758  }
   1759 
   1760  // If we're all scroll frame, then all descendants will be clipped, so we
   1761  // can't preserve 3d.
   1762  if (IsScrollContainerFrame()) {
   1763    return false;
   1764  }
   1765 
   1766  const nsStyleEffects* effects = StyleEffectsWithOptionalParam(aStyleEffects);
   1767  if (HasOpacity(disp, effects, aEffectSetForOpacity)) {
   1768    return false;
   1769  }
   1770 
   1771  return ShouldApplyOverflowClipping(disp).isEmpty() &&
   1772         !GetClipPropClipRect(disp, effects, GetSize()) &&
   1773         !SVGIntegrationUtils::UsingEffectsForFrame(this) &&
   1774         !effects->HasMixBlendMode() &&
   1775         !ForcesStackingContextForViewTransition() &&
   1776         disp->mIsolation != StyleIsolation::Isolate;
   1777 }
   1778 
   1779 bool nsIFrame::Combines3DTransformWithAncestors() const {
   1780  // Check these first as they are faster then both calls below and are we are
   1781  // likely to hit the early return (backface hidden is uncommon and
   1782  // GetReferenceFrame is a hot caller of this which only calls this if
   1783  // IsCSSTransformed is false).
   1784  if (!IsCSSTransformed() && !BackfaceIsHidden()) {
   1785    return false;
   1786  }
   1787  nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
   1788  return parent && parent->Extend3DContext();
   1789 }
   1790 
   1791 bool nsIFrame::In3DContextAndBackfaceIsHidden() const {
   1792  // While both tests fail most of the time, test BackfaceIsHidden()
   1793  // first since it's likely to fail faster.
   1794  return BackfaceIsHidden() && Combines3DTransformWithAncestors();
   1795 }
   1796 
   1797 bool nsIFrame::HasPerspective() const {
   1798  if (!IsCSSTransformed()) {
   1799    return false;
   1800  }
   1801  nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
   1802  if (!parent) {
   1803    return false;
   1804  }
   1805  return parent->ChildrenHavePerspective();
   1806 }
   1807 
   1808 nsRect nsIFrame::GetContentRectRelativeToSelf() const {
   1809  nsMargin bp = GetUsedBorderAndPadding().ApplySkipSides(GetSkipSides());
   1810  nsRect r(0, 0, mRect.width, mRect.height);
   1811  r.Deflate(bp);
   1812  return r;
   1813 }
   1814 
   1815 nsRect nsIFrame::GetContentRect() const {
   1816  return GetContentRectRelativeToSelf() + GetPosition();
   1817 }
   1818 
   1819 bool nsIFrame::ComputeBorderRadii(const BorderRadius& aBorderRadius,
   1820                                  const nsSize& aFrameSize,
   1821                                  const nsSize& aBorderArea, Sides aSkipSides,
   1822                                  nsRectCornerRadii& aRadii) {
   1823  // Percentages are relative to whichever side they're on.
   1824  for (const auto i : mozilla::AllPhysicalHalfCorners()) {
   1825    const LengthPercentage& c = aBorderRadius.Get(i);
   1826    nscoord axis = HalfCornerIsX(i) ? aFrameSize.width : aFrameSize.height;
   1827    aRadii[i] = std::max(0, c.Resolve(axis));
   1828  }
   1829 
   1830  if (aSkipSides.Intersects(SideBits::eTop | SideBits::eLeft)) {
   1831    aRadii.TopLeft() = {};
   1832  }
   1833  if (aSkipSides.Intersects(SideBits::eTop | SideBits::eRight)) {
   1834    aRadii.TopRight() = {};
   1835  }
   1836  if (aSkipSides.Intersects(SideBits::eBottom | SideBits::eLeft)) {
   1837    aRadii.BottomLeft() = {};
   1838  }
   1839  if (aSkipSides.Intersects(SideBits::eBottom | SideBits::eRight)) {
   1840    aRadii.BottomRight() = {};
   1841  }
   1842 
   1843  // css3-background specifies this algorithm for reducing
   1844  // corner radii when they are too big.
   1845  bool haveRadius = false;
   1846  double ratio = 1.0f;
   1847  for (const auto side : mozilla::AllPhysicalSides()) {
   1848    auto hc1 = SideToHalfCorner(side, false, true);
   1849    auto hc2 = SideToHalfCorner(side, true, true);
   1850    nscoord length =
   1851        SideIsVertical(side) ? aBorderArea.height : aBorderArea.width;
   1852    nscoord sum = aRadii[hc1] + aRadii[hc2];
   1853    if (sum) {
   1854      haveRadius = true;
   1855      // avoid floating point division in the normal case
   1856      if (length < sum) {
   1857        ratio = std::min(ratio, double(length) / sum);
   1858      }
   1859    }
   1860  }
   1861  if (ratio < 1.0) {
   1862    for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
   1863      aRadii[corner] *= ratio;
   1864    }
   1865  }
   1866 
   1867  return haveRadius;
   1868 }
   1869 
   1870 static inline bool RadiiAreDefinitelyZero(const BorderRadius& aBorderRadius) {
   1871  for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
   1872    if (!aBorderRadius.Get(corner).IsDefinitelyZero()) {
   1873      return false;
   1874    }
   1875  }
   1876  return true;
   1877 }
   1878 
   1879 /* virtual */
   1880 bool nsIFrame::GetBorderRadii(const nsSize& aFrameSize,
   1881                              const nsSize& aBorderArea, Sides aSkipSides,
   1882                              nsRectCornerRadii& aRadii) const {
   1883  if (!mMayHaveRoundedCorners) {
   1884    return false;
   1885  }
   1886 
   1887  if (IsThemed()) {
   1888    // When we're themed, the native theme code draws the border and
   1889    // background, and therefore it doesn't make sense to tell other
   1890    // code that's interested in border-radius that we have any radii.
   1891    //
   1892    // In an ideal world, we might have a way for the them to tell us an
   1893    // border radius, but since we don't, we're better off assuming
   1894    // zero.
   1895    return false;
   1896  }
   1897 
   1898  const auto& radii = StyleBorder()->mBorderRadius;
   1899  const bool hasRadii =
   1900      ComputeBorderRadii(radii, aFrameSize, aBorderArea, aSkipSides, aRadii);
   1901  if (!hasRadii) {
   1902    // TODO(emilio): Maybe we can just remove this bit and do the
   1903    // IsDefinitelyZero check unconditionally. That should still avoid most of
   1904    // the work, though maybe not the cache miss of going through the style and
   1905    // the border struct.
   1906    const_cast<nsIFrame*>(this)->mMayHaveRoundedCorners =
   1907        !RadiiAreDefinitelyZero(radii);
   1908  }
   1909  return hasRadii;
   1910 }
   1911 
   1912 bool nsIFrame::GetBorderRadii(nsRectCornerRadii& aRadii) const {
   1913  nsSize sz = GetSize();
   1914  return GetBorderRadii(sz, sz, GetSkipSides(), aRadii);
   1915 }
   1916 
   1917 bool nsIFrame::GetMarginBoxBorderRadii(nsRectCornerRadii& aRadii) const {
   1918  if (!GetBorderRadii(aRadii)) {
   1919    return false;
   1920  }
   1921  aRadii.AdjustOutwards(GetUsedMargin());
   1922  return true;
   1923 }
   1924 
   1925 bool nsIFrame::GetPaddingBoxBorderRadii(nsRectCornerRadii& aRadii) const {
   1926  if (!GetBorderRadii(aRadii)) {
   1927    return false;
   1928  }
   1929  aRadii.AdjustInwards(GetUsedBorder());
   1930  return true;
   1931 }
   1932 
   1933 bool nsIFrame::GetContentBoxBorderRadii(nsRectCornerRadii& aRadii) const {
   1934  if (!GetBorderRadii(aRadii)) {
   1935    return false;
   1936  }
   1937  aRadii.AdjustInwards(GetUsedBorderAndPadding());
   1938  return true;
   1939 }
   1940 
   1941 bool nsIFrame::GetShapeBoxBorderRadii(nsRectCornerRadii& aRadii) const {
   1942  using Tag = StyleShapeOutside::Tag;
   1943  auto& shapeOutside = StyleDisplay()->mShapeOutside;
   1944  auto box = StyleShapeBox::MarginBox;
   1945  switch (shapeOutside.tag) {
   1946    case Tag::Image:
   1947    case Tag::None:
   1948      return false;
   1949    case Tag::Box:
   1950      box = shapeOutside.AsBox();
   1951      break;
   1952    case Tag::Shape:
   1953      box = shapeOutside.AsShape()._1;
   1954      break;
   1955  }
   1956 
   1957  switch (box) {
   1958    case StyleShapeBox::ContentBox:
   1959      return GetContentBoxBorderRadii(aRadii);
   1960    case StyleShapeBox::PaddingBox:
   1961      return GetPaddingBoxBorderRadii(aRadii);
   1962    case StyleShapeBox::BorderBox:
   1963      return GetBorderRadii(aRadii);
   1964    case StyleShapeBox::MarginBox:
   1965      return GetMarginBoxBorderRadii(aRadii);
   1966    default:
   1967      MOZ_ASSERT_UNREACHABLE("Unexpected box value");
   1968      return false;
   1969  }
   1970 }
   1971 
   1972 nscoord nsIFrame::OneEmInAppUnits() const {
   1973  return StyleFont()
   1974      ->mFont.size.ScaledBy(nsLayoutUtils::FontSizeInflationFor(this))
   1975      .ToAppUnits();
   1976 }
   1977 
   1978 RubyMetrics nsIFrame::RubyMetrics(float aRubyMetricsFactor) const {
   1979  RefPtr<nsFontMetrics> fm =
   1980      nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
   1981  return mozilla::RubyMetrics{
   1982      nscoord(NS_round(fm->TrimmedAscent() * aRubyMetricsFactor)),
   1983      nscoord(NS_round(fm->TrimmedDescent() * aRubyMetricsFactor))};
   1984 }
   1985 
   1986 ComputedStyle* nsIFrame::GetAdditionalComputedStyle(int32_t aIndex) const {
   1987  MOZ_ASSERT(aIndex >= 0, "invalid index number");
   1988  return nullptr;
   1989 }
   1990 
   1991 void nsIFrame::SetAdditionalComputedStyle(int32_t aIndex,
   1992                                          ComputedStyle* aComputedStyle) {
   1993  MOZ_ASSERT(aIndex >= 0, "invalid index number");
   1994 }
   1995 
   1996 nscoord nsIFrame::SynthesizeFallbackBaseline(
   1997    WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
   1998  const auto margin = GetLogicalUsedMargin(aWM);
   1999  NS_ASSERTION(!IsSubtreeDirty(), "frame must not be dirty");
   2000  if (aWM.IsCentralBaseline()) {
   2001    return (BSize(aWM) + GetLogicalUsedMargin(aWM).BEnd(aWM)) / 2;
   2002  }
   2003  // Baseline for inverted line content is the top (block-start) margin edge,
   2004  // as the frame is in effect "flipped" for alignment purposes.
   2005  if (aWM.IsLineInverted()) {
   2006    const auto marginStart = margin.BStart(aWM);
   2007    return aBaselineGroup == BaselineSharingGroup::First
   2008               ? -marginStart
   2009               : BSize(aWM) + marginStart;
   2010  }
   2011  // Otherwise, the bottom margin edge, per CSS2.1's definition of the
   2012  // 'baseline' value of 'vertical-align'.
   2013  const auto marginEnd = margin.BEnd(aWM);
   2014  return aBaselineGroup == BaselineSharingGroup::First ? BSize(aWM) + marginEnd
   2015                                                       : -marginEnd;
   2016 }
   2017 
   2018 nscoord nsIFrame::GetLogicalBaseline(WritingMode aWM) const {
   2019  return GetLogicalBaseline(aWM, GetDefaultBaselineSharingGroup(),
   2020                            BaselineExportContext::LineLayout);
   2021 }
   2022 
   2023 nscoord nsIFrame::GetLogicalBaseline(
   2024    WritingMode aWM, BaselineSharingGroup aBaselineGroup,
   2025    BaselineExportContext aExportContext) const {
   2026  const auto result =
   2027      GetNaturalBaselineBOffset(aWM, aBaselineGroup, aExportContext)
   2028          .valueOrFrom([this, aWM, aBaselineGroup]() {
   2029            return SynthesizeFallbackBaseline(aWM, aBaselineGroup);
   2030          });
   2031  if (aBaselineGroup == BaselineSharingGroup::Last) {
   2032    return BSize(aWM) - result;
   2033  }
   2034  return result;
   2035 }
   2036 
   2037 const nsFrameList& nsIFrame::GetChildList(ChildListID aListID) const {
   2038  if (IsAbsoluteContainer()) {
   2039    if (aListID == GetAbsoluteListID()) {
   2040      return GetAbsoluteContainingBlock()->GetChildList();
   2041    } else if (aListID == FrameChildListID::PushedAbsolute) {
   2042      return GetAbsoluteContainingBlock()->GetPushedChildList();
   2043    }
   2044  }
   2045  return nsFrameList::EmptyList();
   2046 }
   2047 
   2048 void nsIFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
   2049  if (const auto* absCB = GetAbsoluteContainingBlock()) {
   2050    const nsFrameList& absoluteList = absCB->GetChildList();
   2051    absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID());
   2052    const nsFrameList& pushedAbsoluteList = absCB->GetPushedChildList();
   2053    pushedAbsoluteList.AppendIfNonempty(aLists,
   2054                                        FrameChildListID::PushedAbsolute);
   2055  }
   2056 }
   2057 
   2058 AutoTArray<nsIFrame::ChildList, 4> nsIFrame::CrossDocChildLists() {
   2059  AutoTArray<ChildList, 4> childLists;
   2060  nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this);
   2061  if (subdocumentFrame) {
   2062    // Descend into the subdocument
   2063    nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
   2064    if (root) {
   2065      childLists.EmplaceBack(
   2066          nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
   2067          FrameChildListID::Principal);
   2068    }
   2069  }
   2070 
   2071  GetChildLists(&childLists);
   2072  return childLists;
   2073 }
   2074 
   2075 nsIFrame::CaretBlockAxisMetrics nsIFrame::GetCaretBlockAxisMetrics(
   2076    mozilla::WritingMode aWM, const nsFontMetrics& aFM) const {
   2077  // Note(dshin): Ultimately, this does something highly similar (But still
   2078  // different) to `nsLayoutUtils::GetFirstLinePosition`.
   2079  const auto baseline = GetCaretBaseline();
   2080  nscoord ascent = 0, descent = 0;
   2081  ascent = aFM.MaxAscent();
   2082  descent = aFM.MaxDescent();
   2083  const nscoord height = ascent + descent;
   2084  if (aWM.IsVertical() && aWM.IsLineInverted()) {
   2085    return CaretBlockAxisMetrics{.mOffset = baseline - descent,
   2086                                 .mExtent = height};
   2087  }
   2088  return CaretBlockAxisMetrics{.mOffset = baseline - ascent, .mExtent = height};
   2089 }
   2090 
   2091 nscoord nsIFrame::GetFontMetricsDerivedCaretBaseline(nscoord aBSize) const {
   2092  float inflation = nsLayoutUtils::FontSizeInflationFor(this);
   2093  RefPtr<nsFontMetrics> fm =
   2094      nsLayoutUtils::GetFontMetricsForFrame(this, inflation);
   2095  const WritingMode wm = GetWritingMode();
   2096  nscoord lineHeight = ReflowInput::CalcLineHeight(
   2097      *Style(), PresContext(), GetContent(), aBSize, inflation);
   2098  return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight,
   2099                                                wm.IsLineInverted()) +
   2100         GetLogicalUsedBorderAndPadding(wm).BStart(wm);
   2101 }
   2102 
   2103 const nsAtom* nsIFrame::ComputePageValue(const nsAtom* aAutoValue) const {
   2104  const nsAtom* value = aAutoValue ? aAutoValue : nsGkAtoms::_empty;
   2105  const nsIFrame* frame = this;
   2106  // Find what CSS page name value this frame's subtree has, if any.
   2107  // Starting with this frame, check if a page name other than auto is present,
   2108  // and record it if so. Then, if the current frame is a container frame, find
   2109  // the first non-placeholder child and repeat.
   2110  // This will find the most deeply nested first in-flow child of this frame's
   2111  // subtree, and return its page name (with auto resolved if applicable, and
   2112  // subtrees with no page-names returning the empty atom rather than null).
   2113  do {
   2114    if (const nsAtom* maybePageName = frame->GetStylePageName()) {
   2115      value = maybePageName;
   2116    }
   2117    // Get the next frame to read from.
   2118    const nsIFrame* firstNonPlaceholderFrame = nullptr;
   2119    // If this is a container frame, inspect its in-flow children.
   2120    if (const nsContainerFrame* containerFrame = do_QueryFrame(frame)) {
   2121      for (const nsIFrame* childFrame : containerFrame->PrincipalChildList()) {
   2122        if (!childFrame->IsPlaceholderFrame()) {
   2123          firstNonPlaceholderFrame = childFrame;
   2124          break;
   2125        }
   2126      }
   2127    }
   2128    frame = firstNonPlaceholderFrame;
   2129  } while (frame);
   2130  return value;
   2131 }
   2132 
   2133 Visibility nsIFrame::GetVisibility() const {
   2134  if (!HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
   2135    return Visibility::Untracked;
   2136  }
   2137 
   2138  bool isSet = false;
   2139  uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
   2140 
   2141  MOZ_ASSERT(isSet,
   2142             "Should have a VisibilityStateProperty value "
   2143             "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
   2144 
   2145  return visibleCount > 0 ? Visibility::ApproximatelyVisible
   2146                          : Visibility::ApproximatelyNonVisible;
   2147 }
   2148 
   2149 void nsIFrame::UpdateVisibilitySynchronously() {
   2150  mozilla::PresShell* presShell = PresShell();
   2151  if (!presShell) {
   2152    return;
   2153  }
   2154 
   2155  if (presShell->AssumeAllFramesVisible()) {
   2156    presShell->EnsureFrameInApproximatelyVisibleList(this);
   2157    return;
   2158  }
   2159 
   2160  bool visible = StyleVisibility()->IsVisible();
   2161  nsIFrame* f = GetParent();
   2162  nsRect rect = GetRectRelativeToSelf();
   2163  nsIFrame* rectFrame = this;
   2164  while (f && visible) {
   2165    if (ScrollContainerFrame* sf = do_QueryFrame(f)) {
   2166      nsRect transformedRect =
   2167          nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
   2168      if (!sf->IsRectNearlyVisible(transformedRect)) {
   2169        visible = false;
   2170        break;
   2171      }
   2172 
   2173      // In this code we're trying to synchronously update *approximate*
   2174      // visibility. (In the future we may update precise visibility here as
   2175      // well, which is why the method name does not contain 'approximate'.) The
   2176      // IsRectNearlyVisible() check above tells us that the rect we're checking
   2177      // is approximately visible within the scrollframe, but we still need to
   2178      // ensure that, even if it was scrolled into view, it'd be visible when we
   2179      // consider the rest of the document. To do that, we move transformedRect
   2180      // to be contained in the scrollport as best we can (it might not fit) to
   2181      // pretend that it was scrolled into view.
   2182      rect = transformedRect.MoveInsideAndClamp(sf->GetScrollPortRect());
   2183      rectFrame = f;
   2184    }
   2185    nsIFrame* parent = f->GetParent();
   2186    if (!parent) {
   2187      parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
   2188      if (parent && parent->PresContext()->IsChrome()) {
   2189        break;
   2190      }
   2191    }
   2192    f = parent;
   2193  }
   2194 
   2195  if (visible) {
   2196    presShell->EnsureFrameInApproximatelyVisibleList(this);
   2197  } else {
   2198    presShell->RemoveFrameFromApproximatelyVisibleList(this);
   2199  }
   2200 }
   2201 
   2202 void nsIFrame::EnableVisibilityTracking() {
   2203  if (HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
   2204    return;  // Nothing to do.
   2205  }
   2206 
   2207  MOZ_ASSERT(!HasProperty(VisibilityStateProperty()),
   2208             "Shouldn't have a VisibilityStateProperty value "
   2209             "if NS_FRAME_VISIBILITY_IS_TRACKED is not set");
   2210 
   2211  // Add the state bit so we know to track visibility for this frame, and
   2212  // initialize the frame property.
   2213  AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
   2214  SetProperty(VisibilityStateProperty(), 0);
   2215 
   2216  mozilla::PresShell* presShell = PresShell();
   2217  if (!presShell) {
   2218    return;
   2219  }
   2220 
   2221  // Schedule a visibility update. This method will virtually always be called
   2222  // when layout has changed anyway, so it's very unlikely that any additional
   2223  // visibility updates will be triggered by this, but this way we guarantee
   2224  // that if this frame is currently visible we'll eventually find out.
   2225  presShell->ScheduleApproximateFrameVisibilityUpdateSoon();
   2226 }
   2227 
   2228 void nsIFrame::DisableVisibilityTracking() {
   2229  if (!HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
   2230    return;  // Nothing to do.
   2231  }
   2232 
   2233  bool isSet = false;
   2234  uint32_t visibleCount = TakeProperty(VisibilityStateProperty(), &isSet);
   2235 
   2236  MOZ_ASSERT(isSet,
   2237             "Should have a VisibilityStateProperty value "
   2238             "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
   2239 
   2240  RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
   2241 
   2242  if (visibleCount == 0) {
   2243    return;  // We were nonvisible.
   2244  }
   2245 
   2246  // We were visible, so send an OnVisibilityChange() notification.
   2247  OnVisibilityChange(Visibility::ApproximatelyNonVisible);
   2248 }
   2249 
   2250 void nsIFrame::DecApproximateVisibleCount(
   2251    const Maybe<OnNonvisible>& aNonvisibleAction
   2252    /* = Nothing() */) {
   2253  MOZ_ASSERT(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED));
   2254 
   2255  bool isSet = false;
   2256  uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
   2257 
   2258  MOZ_ASSERT(isSet,
   2259             "Should have a VisibilityStateProperty value "
   2260             "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
   2261  MOZ_ASSERT(visibleCount > 0,
   2262             "Frame is already nonvisible and we're "
   2263             "decrementing its visible count?");
   2264 
   2265  visibleCount--;
   2266  SetProperty(VisibilityStateProperty(), visibleCount);
   2267  if (visibleCount > 0) {
   2268    return;
   2269  }
   2270 
   2271  // We just became nonvisible, so send an OnVisibilityChange() notification.
   2272  OnVisibilityChange(Visibility::ApproximatelyNonVisible, aNonvisibleAction);
   2273 }
   2274 
   2275 void nsIFrame::IncApproximateVisibleCount() {
   2276  MOZ_ASSERT(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED));
   2277 
   2278  bool isSet = false;
   2279  uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
   2280 
   2281  MOZ_ASSERT(isSet,
   2282             "Should have a VisibilityStateProperty value "
   2283             "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
   2284 
   2285  visibleCount++;
   2286  SetProperty(VisibilityStateProperty(), visibleCount);
   2287  if (visibleCount > 1) {
   2288    return;
   2289  }
   2290 
   2291  // We just became visible, so send an OnVisibilityChange() notification.
   2292  OnVisibilityChange(Visibility::ApproximatelyVisible);
   2293 }
   2294 
   2295 void nsIFrame::OnVisibilityChange(Visibility aNewVisibility,
   2296                                  const Maybe<OnNonvisible>& aNonvisibleAction
   2297                                  /* = Nothing() */) {
   2298  // XXX(seth): In bug 1218990 we'll implement visibility tracking for CSS
   2299  // images here.
   2300 }
   2301 
   2302 static nsIFrame* GetActiveSelectionFrame(nsPresContext* aPresContext,
   2303                                         nsIFrame* aFrame) {
   2304  nsIContent* capturingContent = PresShell::GetCapturingContent();
   2305  if (capturingContent) {
   2306    nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
   2307    return activeFrame ? activeFrame : aFrame;
   2308  }
   2309 
   2310  return aFrame;
   2311 }
   2312 
   2313 bool nsIFrame::ShouldHandleSelectionMovementEvents() {
   2314  if (GetDisplaySelection() == nsISelectionController::SELECTION_OFF) {
   2315    return false;
   2316  }
   2317  if (!IsSelectable()) {
   2318    // Check whether style allows selection.
   2319    return false;
   2320  }
   2321  if (IsScrollbarFrame() || IsHTMLCanvasFrame()) {
   2322    // Scrollbars and canvas don't move selection with the mouse.
   2323    return false;
   2324  }
   2325  return true;
   2326 }
   2327 
   2328 static Element* FindElementAncestorForMozSelection(nsIContent* aContent) {
   2329  NS_ENSURE_TRUE(aContent, nullptr);
   2330  while (aContent && aContent->IsInNativeAnonymousSubtree()) {
   2331    aContent = aContent->GetClosestNativeAnonymousSubtreeRootParentOrHost();
   2332  }
   2333  NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
   2334  return aContent ? aContent->GetAsElementOrParentElement() : nullptr;
   2335 }
   2336 
   2337 already_AddRefed<ComputedStyle> nsIFrame::ComputeSelectionStyle(
   2338    int16_t aSelectionStatus) const {
   2339  // Just bail out if not a selection-status that ::selection applies to.
   2340  if (aSelectionStatus != nsISelectionController::SELECTION_ON &&
   2341      aSelectionStatus != nsISelectionController::SELECTION_DISABLED) {
   2342    return nullptr;
   2343  }
   2344  Element* element = FindElementAncestorForMozSelection(GetContent());
   2345  if (!element) {
   2346    return nullptr;
   2347  }
   2348  RefPtr<ComputedStyle> pseudoStyle =
   2349      PresContext()->StyleSet()->ProbePseudoElementStyle(
   2350          *element, PseudoStyleType::selection, nullptr, Style());
   2351  if (!pseudoStyle) {
   2352    return nullptr;
   2353  }
   2354  // When in high-contrast mode, the style system ends up ignoring the color
   2355  // declarations, which means that the ::selection style becomes the inherited
   2356  // color, and default background. That's no good.
   2357  // When force-color-adjust is set to none allow using the color styles,
   2358  // as they will not be replaced.
   2359  if (PresContext()->ForcingColors() &&
   2360      pseudoStyle->StyleText()->mForcedColorAdjust !=
   2361          StyleForcedColorAdjust::None) {
   2362    return nullptr;
   2363  }
   2364  return do_AddRef(pseudoStyle);
   2365 }
   2366 
   2367 already_AddRefed<ComputedStyle> nsIFrame::ComputeHighlightSelectionStyle(
   2368    nsAtom* aHighlightName) {
   2369  Element* element = FindElementAncestorForMozSelection(GetContent());
   2370  if (!element) {
   2371    return nullptr;
   2372  }
   2373  return PresContext()->StyleSet()->ProbePseudoElementStyle(
   2374      *element, PseudoStyleType::highlight, aHighlightName, Style());
   2375 }
   2376 
   2377 already_AddRefed<ComputedStyle> nsIFrame::ComputeTargetTextStyle() const {
   2378  const Element* element = FindElementAncestorForMozSelection(GetContent());
   2379  if (!element) {
   2380    return nullptr;
   2381  }
   2382  RefPtr pseudoStyle = PresContext()->StyleSet()->ProbePseudoElementStyle(
   2383      *element, PseudoStyleType::targetText, nullptr, Style());
   2384  if (!pseudoStyle) {
   2385    return nullptr;
   2386  }
   2387  if (PresContext()->ForcingColors() &&
   2388      pseudoStyle->StyleText()->mForcedColorAdjust !=
   2389          StyleForcedColorAdjust::None) {
   2390    return nullptr;
   2391  }
   2392  return pseudoStyle.forget();
   2393 }
   2394 
   2395 nsTextControlFrame* nsIFrame::GetContainingTextControlFrame() const {
   2396  const nsIFrame* cur = this;
   2397  do {
   2398    if (const nsTextControlFrame* tc = do_QueryFrame(cur)) {
   2399      return const_cast<nsTextControlFrame*>(tc);
   2400    }
   2401    auto* content = cur->GetContent();
   2402    if (!content || !content->IsInNativeAnonymousSubtree()) {
   2403      // All content inside text controls is anonymous.
   2404      return nullptr;
   2405    }
   2406    cur = cur->GetParent();
   2407  } while (cur);
   2408  return nullptr;
   2409 }
   2410 
   2411 bool nsIFrame::CanBeDynamicReflowRoot() const {
   2412  const auto& display = *StyleDisplay();
   2413  if (IsLineParticipant() || display.mDisplay.IsRuby() ||
   2414      display.IsInnerTableStyle() ||
   2415      display.DisplayInside() == StyleDisplayInside::Table) {
   2416    // We have a display type where 'width' and 'height' don't actually set the
   2417    // width or height (i.e., the size depends on content).
   2418    MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT),
   2419               "should not have dynamic reflow root bit");
   2420    return false;
   2421  }
   2422 
   2423  // In general, frames that have contain:layout+size can be reflow roots.
   2424  // (One exception: table-wrapper frames don't work well as reflow roots,
   2425  // because their inner-table ReflowInput init path tries to reuse & deref
   2426  // the wrapper's containing block's reflow input, which may be null if we
   2427  // initiate reflow from the table-wrapper itself.)
   2428  //
   2429  // Changes to `contain` force frame reconstructions, so we used to use
   2430  // NS_FRAME_REFLOW_ROOT, this bit could be set for the whole lifetime of
   2431  // this frame. But after the support of `content-visibility: auto` which
   2432  // is with contain layout + size when it's not relevant to user, and only
   2433  // with contain layout when it is relevant. The frame does not reconstruct
   2434  // when the relevancy changes. So we use NS_FRAME_DYNAMIC_REFLOW_ROOT instead.
   2435  //
   2436  // We place it above the pref check on purpose, to make sure it works for
   2437  // containment even with the pref disabled.
   2438  if (display.IsContainLayout() && GetContainSizeAxes().IsBoth()) {
   2439    return true;
   2440  }
   2441 
   2442  if (!StaticPrefs::layout_dynamic_reflow_roots_enabled()) {
   2443    return false;
   2444  }
   2445 
   2446  // We can't serve as a dynamic reflow root if our used 'width' and 'height'
   2447  // might be influenced by content.
   2448  //
   2449  // FIXME: For display:block, we should probably optimize inline-size: auto.
   2450  // FIXME: Other flex and grid cases?
   2451  const auto& pos = *StylePosition();
   2452  const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   2453  const auto width = pos.GetWidth(anchorResolutionParams);
   2454  const auto height = pos.GetHeight(anchorResolutionParams);
   2455  if (!width->IsLengthPercentage() || width->HasPercent() ||
   2456      !height->IsLengthPercentage() || height->HasPercent() ||
   2457      IsIntrinsicKeyword(*pos.GetMinWidth(anchorResolutionParams)) ||
   2458      IsIntrinsicKeyword(*pos.GetMaxWidth(anchorResolutionParams)) ||
   2459      IsIntrinsicKeyword(*pos.GetMinHeight(anchorResolutionParams)) ||
   2460      IsIntrinsicKeyword(*pos.GetMaxHeight(anchorResolutionParams)) ||
   2461      ((pos.GetMinWidth(anchorResolutionParams)->IsAuto() ||
   2462        pos.GetMinHeight(anchorResolutionParams)->IsAuto()) &&
   2463       IsFlexOrGridItem())) {
   2464    return false;
   2465  }
   2466 
   2467  // If our flex-basis is 'auto', it'll defer to 'width' (or 'height') which
   2468  // we've already checked. Otherwise, it preempts them, so we need to
   2469  // perform the same "could-this-value-be-influenced-by-content" checks that
   2470  // we performed for 'width' and 'height' above.
   2471  if (IsFlexItem()) {
   2472    const auto& flexBasis = pos.mFlexBasis;
   2473    if (!flexBasis.IsAuto()) {
   2474      if (!flexBasis.IsSize() || !flexBasis.AsSize().IsLengthPercentage() ||
   2475          flexBasis.AsSize().HasPercent()) {
   2476        return false;
   2477      }
   2478    }
   2479  }
   2480 
   2481  if (!IsFixedPosContainingBlock()) {
   2482    // We can't treat this frame as a reflow root, since dynamic changes
   2483    // to absolutely-positioned frames inside of it require that we
   2484    // reflow the placeholder before we reflow the absolutely positioned
   2485    // frame.
   2486    // FIXME:  Alternatively, we could sort the reflow roots in
   2487    // PresShell::ProcessReflowCommands by depth in the tree, from
   2488    // deepest to least deep.  However, for performance (FIXME) we
   2489    // should really be sorting them in the opposite order!
   2490    return false;
   2491  }
   2492 
   2493  // If we participate in a container's block reflow context, or margins
   2494  // can collapse through us, we can't be a dynamic reflow root.
   2495  // (NS_BLOCK_BFC is block specific bit, check first as an optimization, it's
   2496  // okay because we also check that it is a block frame.)
   2497  if (!HasAnyStateBits(NS_BLOCK_BFC) && IsBlockFrameOrSubclass()) {
   2498    return false;
   2499  }
   2500 
   2501  // Subgrids are never reflow roots, but 'contain:layout/paint' prevents
   2502  // creating a subgrid in the first place.
   2503  if (pos.mGridTemplateColumns.IsSubgrid() ||
   2504      pos.mGridTemplateRows.IsSubgrid()) {
   2505    // NOTE: we could check that 'display' of our parent's primary frame is
   2506    // '[inline-]grid' here but that's probably not worth it in practice.
   2507    if (!display.IsContainLayout() && !display.IsContainPaint()) {
   2508      return false;
   2509    }
   2510  }
   2511 
   2512  // If we are split, we can't be a dynamic reflow root. Our reflow status may
   2513  // change after reflow, and our parent is responsible to create or delete our
   2514  // next-in-flow.
   2515  if (GetPrevContinuation() || GetNextContinuation()) {
   2516    return false;
   2517  }
   2518 
   2519  return true;
   2520 }
   2521 
   2522 /********************************************************
   2523 * Refreshes each content's frame
   2524 *********************************************************/
   2525 
   2526 void nsIFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
   2527                                           const nsDisplayListSet& aLists) {
   2528  // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
   2529  // "All css properties of table-column and table-column-group boxes are
   2530  // ignored, except when explicitly specified by this specification."
   2531  // CSS outlines fall into this category, so we skip them on these boxes.
   2532  MOZ_ASSERT(!IsTableColGroupFrame() && !IsTableColFrame());
   2533  const auto& outline = *StyleOutline();
   2534 
   2535  if (!outline.ShouldPaintOutline()) {
   2536    return;
   2537  }
   2538 
   2539  // Outlines are painted by the table wrapper frame.
   2540  if (IsTableFrame()) {
   2541    return;
   2542  }
   2543 
   2544  if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT) &&
   2545      ScrollableOverflowRect().IsEmpty()) {
   2546    // Skip parts of IB-splits with an empty overflow rect, see bug 434301.
   2547    // We may still want to fix some of the overflow area calculations over in
   2548    // that bug.
   2549    return;
   2550  }
   2551 
   2552  // We don't display outline-style: auto on themed frames that have their own
   2553  // focus indicators.
   2554  if (outline.mOutlineStyle.IsAuto()) {
   2555    auto* disp = StyleDisplay();
   2556    if (IsThemed(disp) && PresContext()->Theme()->ThemeDrawsFocusForWidget(
   2557                              this, disp->EffectiveAppearance())) {
   2558      return;
   2559    }
   2560  }
   2561 
   2562  aLists.Outlines()->AppendNewToTop<nsDisplayOutline>(aBuilder, this);
   2563 }
   2564 
   2565 void nsIFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
   2566                              const nsDisplayListSet& aLists) {
   2567  if (!IsVisibleForPainting()) {
   2568    return;
   2569  }
   2570 
   2571  DisplayOutlineUnconditional(aBuilder, aLists);
   2572 }
   2573 
   2574 void nsIFrame::DisplayInsetBoxShadowUnconditional(
   2575    nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
   2576  // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
   2577  // just because we're visible?  Or should it depend on the cell visibility
   2578  // when we're not the whole table?
   2579  const auto* effects = StyleEffects();
   2580  if (effects->HasBoxShadowWithInset(true)) {
   2581    aList->AppendNewToTop<nsDisplayBoxShadowInner>(aBuilder, this);
   2582  }
   2583 }
   2584 
   2585 void nsIFrame::DisplayInsetBoxShadow(nsDisplayListBuilder* aBuilder,
   2586                                     nsDisplayList* aList) {
   2587  if (!IsVisibleForPainting()) {
   2588    return;
   2589  }
   2590 
   2591  DisplayInsetBoxShadowUnconditional(aBuilder, aList);
   2592 }
   2593 
   2594 void nsIFrame::DisplayOutsetBoxShadowUnconditional(
   2595    nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
   2596  // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
   2597  // just because we're visible?  Or should it depend on the cell visibility
   2598  // when we're not the whole table?
   2599  const auto* effects = StyleEffects();
   2600  if (effects->HasBoxShadowWithInset(false)) {
   2601    aList->AppendNewToTop<nsDisplayBoxShadowOuter>(aBuilder, this);
   2602  }
   2603 }
   2604 
   2605 void nsIFrame::DisplayOutsetBoxShadow(nsDisplayListBuilder* aBuilder,
   2606                                      nsDisplayList* aList) {
   2607  if (!IsVisibleForPainting()) {
   2608    return;
   2609  }
   2610 
   2611  DisplayOutsetBoxShadowUnconditional(aBuilder, aList);
   2612 }
   2613 
   2614 void nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
   2615                            nsDisplayList* aList) {
   2616  if (!IsVisibleForPainting()) {
   2617    return;
   2618  }
   2619 
   2620  aList->AppendNewToTop<nsDisplayCaret>(aBuilder, this);
   2621 }
   2622 
   2623 nscolor nsIFrame::GetCaretColorAt(int32_t aOffset) {
   2624  return nsLayoutUtils::GetTextColor(this, &nsStyleUI::mCaretColor);
   2625 }
   2626 
   2627 auto nsIFrame::ComputeShouldPaintBackground() const -> ShouldPaintBackground {
   2628  nsPresContext* pc = PresContext();
   2629  ShouldPaintBackground settings{pc->GetBackgroundColorDraw(),
   2630                                 pc->GetBackgroundImageDraw()};
   2631  if (settings.mColor && settings.mImage) {
   2632    return settings;
   2633  }
   2634 
   2635  if (StyleVisibility()->mPrintColorAdjust == StylePrintColorAdjust::Exact) {
   2636    return {true, true};
   2637  }
   2638 
   2639  return settings;
   2640 }
   2641 
   2642 bool nsIFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
   2643                                              const nsDisplayListSet& aLists) {
   2644  if (aBuilder->IsForEventDelivery() && !aBuilder->HitTestIsForVisibility()) {
   2645    // For hit-testing, we generally just need a light-weight data structure
   2646    // like nsDisplayEventReceiver. But if the hit-testing is for visibility,
   2647    // then we need to know the opaque region in order to determine whether to
   2648    // stop or not.
   2649    aLists.BorderBackground()->AppendNewToTop<nsDisplayEventReceiver>(aBuilder,
   2650                                                                      this);
   2651    return false;
   2652  }
   2653 
   2654  const AppendedBackgroundType result =
   2655      nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
   2656          aBuilder, this,
   2657          GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this),
   2658          aLists.BorderBackground());
   2659 
   2660  if (result == AppendedBackgroundType::None) {
   2661    aBuilder->BuildCompositorHitTestInfoIfNeeded(this,
   2662                                                 aLists.BorderBackground());
   2663  }
   2664 
   2665  return result == AppendedBackgroundType::ThemedBackground;
   2666 }
   2667 
   2668 void nsIFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
   2669                                              const nsDisplayListSet& aLists) {
   2670  // The visibility check belongs here since child elements have the
   2671  // opportunity to override the visibility property and display even if
   2672  // their parent is hidden.
   2673  if (!IsVisibleForPainting()) {
   2674    return;
   2675  }
   2676 
   2677  DisplayOutsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
   2678 
   2679  bool bgIsThemed = DisplayBackgroundUnconditional(aBuilder, aLists);
   2680  DisplayInsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
   2681 
   2682  // If there's a themed background, we should not create a border item.
   2683  // It won't be rendered.
   2684  // Don't paint borders for tables here, since they paint them in a different
   2685  // order.
   2686  if (!bgIsThemed && StyleBorder()->HasBorder() && !IsTableFrame()) {
   2687    aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this);
   2688  }
   2689 
   2690  DisplayOutlineUnconditional(aBuilder, aLists);
   2691 }
   2692 
   2693 inline static bool IsSVGContentWithCSSClip(const nsIFrame* aFrame) {
   2694  // The CSS spec says that the 'clip' property only applies to absolutely
   2695  // positioned elements, whereas the SVG spec says that it applies to SVG
   2696  // elements regardless of the value of the 'position' property. Here we obey
   2697  // the CSS spec for outer-<svg> (since that's what we generally do), but
   2698  // obey the SVG spec for other SVG elements to which 'clip' applies.
   2699  return aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) &&
   2700         aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::svg,
   2701                                                  nsGkAtoms::foreignObject);
   2702 }
   2703 
   2704 Maybe<nsRect> nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp,
   2705                                            const nsStyleEffects* aEffects,
   2706                                            const nsSize& aSize) const {
   2707  if (aEffects->mClip.IsAuto() ||
   2708      !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
   2709    return Nothing();
   2710  }
   2711 
   2712  auto& clipRect = aEffects->mClip.AsRect();
   2713  nsRect rect = clipRect.ToLayoutRect();
   2714  if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak ==
   2715                 StyleBoxDecorationBreak::Slice)) {
   2716    // The clip applies to the joined boxes so it's relative the first
   2717    // continuation.
   2718    nscoord y = 0;
   2719    for (nsIFrame* f = GetPrevContinuation(); f; f = f->GetPrevContinuation()) {
   2720      y += f->GetRect().height;
   2721    }
   2722    rect.MoveBy(nsPoint(0, -y));
   2723  }
   2724 
   2725  if (clipRect.right.IsAuto()) {
   2726    rect.width = aSize.width - rect.x;
   2727  }
   2728  if (clipRect.bottom.IsAuto()) {
   2729    rect.height = aSize.height - rect.y;
   2730  }
   2731  return Some(rect);
   2732 }
   2733 
   2734 // https://drafts.csswg.org/css-view-transitions-1/#named-and-transitioning
   2735 //
   2736 // Note https://github.com/w3c/csswg-drafts/issues/11772, however, for the root
   2737 // style check.
   2738 bool nsIFrame::ForcesStackingContextForViewTransition() const {
   2739  auto* style = Style();
   2740  return !style->IsRootElementStyle() &&
   2741         (style->StyleUIReset()->HasViewTransitionName() ||
   2742          HasAnyStateBits(NS_FRAME_CAPTURED_IN_VIEW_TRANSITION) ||
   2743          style->StyleDisplay()->mWillChange.bits &
   2744              mozilla::StyleWillChangeBits::VIEW_TRANSITION_NAME);
   2745 }
   2746 
   2747 /**
   2748 * If the CSS 'overflow' property applies to this frame, and is not
   2749 * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
   2750 * for that overflow in aBuilder->ClipState() to clip all containing-block
   2751 * descendants.
   2752 */
   2753 static void ApplyOverflowClipping(
   2754    nsDisplayListBuilder* aBuilder, const nsIFrame* aFrame,
   2755    PhysicalAxes aClipAxes,
   2756    DisplayListClipState::AutoClipMultiple& aClipState) {
   2757  nsRect clipRect;
   2758  nsRectCornerRadii radii;
   2759  bool haveRadii =
   2760      aFrame->ComputeOverflowClipRectRelativeToSelf(aClipAxes, clipRect, radii);
   2761  aClipState.ClipContainingBlockDescendantsExtra(
   2762      clipRect + aBuilder->ToReferenceFrame(aFrame),
   2763      haveRadii ? &radii : nullptr);
   2764 }
   2765 
   2766 static Sides ToSkipSides(PhysicalAxes aClipAxes) {
   2767  SideBits result{};
   2768  if (!aClipAxes.contains(PhysicalAxis::Vertical)) {
   2769    result |= SideBits::eTop;
   2770    result |= SideBits::eBottom;
   2771  }
   2772  if (!aClipAxes.contains(PhysicalAxis::Horizontal)) {
   2773    result |= SideBits::eLeft;
   2774    result |= SideBits::eRight;
   2775  }
   2776  return Sides(result);
   2777 }
   2778 
   2779 bool nsIFrame::ComputeOverflowClipRectRelativeToSelf(
   2780    const PhysicalAxes aClipAxes, nsRect& aOutRect,
   2781    nsRectCornerRadii& aOutRadii) const {
   2782  // Only 'clip' is handled here (and 'hidden' for table frames, and any
   2783  // non-'visible' value for blocks in a paginated context).
   2784  // We allow 'clip' to apply to any kind of frame. This is required by
   2785  // comboboxes which make their display text (an inline frame) have clipping.
   2786  MOZ_ASSERT(!aClipAxes.isEmpty());
   2787  MOZ_ASSERT(ShouldApplyOverflowClipping(StyleDisplay()) == aClipAxes);
   2788  auto boxMargin = OverflowClipMargin(aClipAxes, /* aAllowNegative = */ true);
   2789  boxMargin.ApplySkipSides(GetSkipSides() | ToSkipSides(aClipAxes));
   2790 
   2791  aOutRect = nsRect(nsPoint(), GetSize());
   2792  aOutRect.Inflate(boxMargin);
   2793  if (MOZ_UNLIKELY(!aClipAxes.contains(PhysicalAxis::Horizontal))) {
   2794    // NOTE(mats) We shouldn't be clipping at all in this dimension really,
   2795    // but clipping in just one axis isn't supported by our GFX APIs so we
   2796    // clip to our visual overflow rect instead.
   2797    nsRect o = InkOverflowRectRelativeToSelf();
   2798    aOutRect.x = o.x;
   2799    aOutRect.width = o.width;
   2800  }
   2801  if (MOZ_UNLIKELY(!aClipAxes.contains(PhysicalAxis::Vertical))) {
   2802    // See the note above.
   2803    nsRect o = InkOverflowRectRelativeToSelf();
   2804    aOutRect.y = o.y;
   2805    aOutRect.height = o.height;
   2806  }
   2807  if (!GetBorderRadii(aOutRadii)) {
   2808    return false;
   2809  }
   2810  aOutRadii.AdjustOutwards(boxMargin);
   2811  return true;
   2812 }
   2813 
   2814 nsMargin nsIFrame::OverflowClipMargin(PhysicalAxes aClipAxes,
   2815                                      bool aAllowNegative) const {
   2816  nsMargin result;
   2817  if (aClipAxes.isEmpty()) {
   2818    return result;
   2819  }
   2820  const auto& margin = StyleMargin()->mOverflowClipMargin;
   2821  if (!aAllowNegative && margin.offset.IsZero()) {
   2822    return result;
   2823  }
   2824  switch (margin.visual_box) {
   2825    case StyleOverflowClipMarginBox::BorderBox:
   2826      break;
   2827    case StyleOverflowClipMarginBox::PaddingBox:
   2828      result = -GetUsedBorder();
   2829      break;
   2830    case StyleOverflowClipMarginBox::ContentBox:
   2831      result = -GetUsedBorderAndPadding();
   2832      break;
   2833  }
   2834  if (!margin.offset.IsZero()) {
   2835    nscoord marginAu = margin.offset.ToAppUnits();
   2836    result += nsMargin(marginAu, marginAu, marginAu, marginAu);
   2837  }
   2838  if (!aAllowNegative) {
   2839    result.EnsureAtLeast(nsMargin());
   2840  }
   2841  return result;
   2842 }
   2843 
   2844 /**
   2845 * Returns whether a display item that gets created with the builder's current
   2846 * state will have a scrolled clip, i.e. a clip that is scrolled by a scroll
   2847 * frame which does not move the item itself.
   2848 */
   2849 static bool BuilderHasScrolledClip(nsDisplayListBuilder* aBuilder) {
   2850  const DisplayItemClipChain* currentClip =
   2851      aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
   2852  if (!currentClip) {
   2853    return false;
   2854  }
   2855 
   2856  const ActiveScrolledRoot* currentClipASR = currentClip->mASR;
   2857  const ActiveScrolledRoot* currentASR = aBuilder->CurrentActiveScrolledRoot();
   2858  return ActiveScrolledRoot::PickDescendant(currentClipASR, currentASR) !=
   2859         currentASR;
   2860 }
   2861 
   2862 class AutoTrackStackingContextBits {
   2863  nsDisplayListBuilder& mBuilder;
   2864  StackingContextBits mBitsToSet;
   2865 
   2866 public:
   2867  explicit AutoTrackStackingContextBits(nsDisplayListBuilder& aBuilder)
   2868      : mBuilder(aBuilder), mBitsToSet(aBuilder.GetStackingContextBits()) {}
   2869 
   2870  ~AutoTrackStackingContextBits() {
   2871    mBuilder.SetStackingContextBits(mBitsToSet);
   2872  }
   2873 
   2874  void AddToParent(StackingContextBits aBits) { mBitsToSet |= aBits; }
   2875 };
   2876 
   2877 static bool IsFrameOrAncestorApzAware(nsIFrame* aFrame) {
   2878  nsIContent* node = aFrame->GetContent();
   2879  if (!node) {
   2880    return false;
   2881  }
   2882 
   2883  do {
   2884    if (node->IsNodeApzAware()) {
   2885      return true;
   2886    }
   2887    nsIContent* shadowRoot = node->GetShadowRoot();
   2888    if (shadowRoot && shadowRoot->IsNodeApzAware()) {
   2889      return true;
   2890    }
   2891 
   2892    // Even if the node owning aFrame doesn't have apz-aware event listeners
   2893    // itself, its shadow root or display: contents ancestors (which have no
   2894    // frames) might, so we need to account for them too.
   2895  } while ((node = node->GetFlattenedTreeParent()) && node->IsElement() &&
   2896           node->AsElement()->IsDisplayContents());
   2897 
   2898  return false;
   2899 }
   2900 
   2901 static void CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder,
   2902                                          nsIFrame* aFrame) {
   2903  if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
   2904    return;
   2905  }
   2906 
   2907  if (IsFrameOrAncestorApzAware(aFrame)) {
   2908    aBuilder->SetAncestorHasApzAwareEventHandler(true);
   2909  }
   2910 }
   2911 
   2912 static void UpdateCurrentHitTestInfo(nsDisplayListBuilder* aBuilder,
   2913                                     nsIFrame* aFrame) {
   2914  if (!aBuilder->BuildCompositorHitTestInfo()) {
   2915    // Compositor hit test info is not used.
   2916    return;
   2917  }
   2918 
   2919  CheckForApzAwareEventHandlers(aBuilder, aFrame);
   2920 
   2921  const CompositorHitTestInfo info =
   2922      aFrame->GetCompositorHitTestInfoWithoutPointerEvents(aBuilder);
   2923  aBuilder->SetInheritedCompositorHitTestInfo(info);
   2924 }
   2925 
   2926 /**
   2927 * True if aDescendant participates the context aAncestor participating.
   2928 */
   2929 static bool FrameParticipatesIn3DContext(nsIFrame* aAncestor,
   2930                                         nsIFrame* aDescendant) {
   2931  MOZ_ASSERT(aAncestor != aDescendant);
   2932  MOZ_ASSERT(aAncestor->GetContent() != aDescendant->GetContent());
   2933  MOZ_ASSERT(aAncestor->Extend3DContext());
   2934 
   2935  nsIFrame* ancestor = aAncestor->FirstContinuation();
   2936  MOZ_ASSERT(ancestor->IsPrimaryFrame());
   2937 
   2938  nsIFrame* frame;
   2939  for (frame = aDescendant->GetClosestFlattenedTreeAncestorPrimaryFrame();
   2940       frame && ancestor != frame;
   2941       frame = frame->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
   2942    if (!frame->Extend3DContext()) {
   2943      return false;
   2944    }
   2945  }
   2946 
   2947  MOZ_ASSERT(frame == ancestor);
   2948  return true;
   2949 }
   2950 
   2951 static bool ItemParticipatesIn3DContext(nsIFrame* aAncestor,
   2952                                        nsDisplayItem* aItem) {
   2953  auto type = aItem->GetType();
   2954  const bool isContainer = type == DisplayItemType::TYPE_WRAP_LIST ||
   2955                           type == DisplayItemType::TYPE_CONTAINER;
   2956 
   2957  if (isContainer && aItem->GetChildren()->Length() == 1) {
   2958    // If the wraplist has only one child item, use the type of that item.
   2959    type = aItem->GetChildren()->GetBottom()->GetType();
   2960  }
   2961 
   2962  if (type != DisplayItemType::TYPE_TRANSFORM &&
   2963      type != DisplayItemType::TYPE_PERSPECTIVE) {
   2964    return false;
   2965  }
   2966  nsIFrame* transformFrame = aItem->Frame();
   2967  if (aAncestor->GetContent() == transformFrame->GetContent()) {
   2968    return true;
   2969  }
   2970  return FrameParticipatesIn3DContext(aAncestor, transformFrame);
   2971 }
   2972 
   2973 static void WrapSeparatorTransform(nsDisplayListBuilder* aBuilder,
   2974                                   nsIFrame* aFrame,
   2975                                   nsDisplayList* aNonParticipants,
   2976                                   nsDisplayList* aParticipants, int aIndex,
   2977                                   nsDisplayItem** aSeparator) {
   2978  if (aNonParticipants->IsEmpty()) {
   2979    return;
   2980  }
   2981 
   2982  nsDisplayTransform* item = MakeDisplayItemWithIndex<nsDisplayTransform>(
   2983      aBuilder, aFrame, aIndex, aNonParticipants, aBuilder->GetVisibleRect());
   2984 
   2985  if (*aSeparator == nullptr && item) {
   2986    *aSeparator = item;
   2987  }
   2988 
   2989  aParticipants->AppendToTop(item);
   2990 }
   2991 
   2992 // Try to compute a clip rect to bound the contents of the mask item
   2993 // that will be built for |aMaskedFrame|. If we're not able to compute
   2994 // one, return an empty Maybe.
   2995 // The returned clip rect, if there is one, is relative to |aMaskedFrame|.
   2996 static Maybe<nsRect> ComputeClipForMaskItem(
   2997    nsDisplayListBuilder* aBuilder, nsIFrame* aMaskedFrame,
   2998    const SVGUtils::MaskUsage& aMaskUsage) {
   2999  const nsStyleSVGReset* svgReset = aMaskedFrame->StyleSVGReset();
   3000 
   3001  nsPoint offsetToUserSpace =
   3002      nsLayoutUtils::ComputeOffsetToUserSpace(aBuilder, aMaskedFrame);
   3003  int32_t devPixelRatio = aMaskedFrame->PresContext()->AppUnitsPerDevPixel();
   3004  gfxPoint devPixelOffsetToUserSpace =
   3005      nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, devPixelRatio);
   3006  CSSToLayoutDeviceScale cssToDevScale =
   3007      aMaskedFrame->PresContext()->CSSToDevPixelScale();
   3008 
   3009  nsPoint toReferenceFrame;
   3010  aBuilder->FindReferenceFrameFor(aMaskedFrame, &toReferenceFrame);
   3011 
   3012  Maybe<gfxRect> combinedClip;
   3013  if (aMaskUsage.ShouldApplyBasicShapeOrPath()) {
   3014    Maybe<Rect> result =
   3015        CSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
   3016            aMaskedFrame, svgReset->mClipPath);
   3017    if (result) {
   3018      combinedClip = Some(ThebesRect(*result));
   3019    }
   3020  } else if (aMaskUsage.ShouldApplyClipPath()) {
   3021    gfxRect result = SVGUtils::GetBBox(
   3022        aMaskedFrame,
   3023        SVGUtils::eBBoxIncludeClipped | SVGUtils::eBBoxIncludeFill |
   3024            SVGUtils::eBBoxIncludeMarkers | SVGUtils::eBBoxIncludeStroke |
   3025            SVGUtils::eDoNotClipToBBoxOfContentInsideClipPath);
   3026    combinedClip = Some(
   3027        ThebesRect((CSSRect::FromUnknownRect(ToRect(result)) * cssToDevScale)
   3028                       .ToUnknownRect()));
   3029  } else {
   3030    // The code for this case is adapted from ComputeMaskGeometry().
   3031 
   3032    nsRect borderArea(toReferenceFrame, aMaskedFrame->GetSize());
   3033    borderArea -= offsetToUserSpace;
   3034 
   3035    // Use an infinite dirty rect to pass into nsCSSRendering::
   3036    // GetImageLayerClip() because we don't have an actual dirty rect to
   3037    // pass in. This is fine because the only time GetImageLayerClip() will
   3038    // not intersect the incoming dirty rect with something is in the "NoClip"
   3039    // case, and we handle that specially.
   3040    nsRect dirtyRect(nscoord_MIN / 2, nscoord_MIN / 2, nscoord_MAX,
   3041                     nscoord_MAX);
   3042 
   3043    nsIFrame* firstFrame =
   3044        nsLayoutUtils::FirstContinuationOrIBSplitSibling(aMaskedFrame);
   3045    nsTArray<SVGMaskFrame*> maskFrames;
   3046    // XXX check return value?
   3047    SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
   3048 
   3049    for (uint32_t i = 0; i < maskFrames.Length(); ++i) {
   3050      gfxRect clipArea;
   3051      if (maskFrames[i]) {
   3052        clipArea = maskFrames[i]->GetMaskArea(aMaskedFrame);
   3053        clipArea = ThebesRect(
   3054            (CSSRect::FromUnknownRect(ToRect(clipArea)) * cssToDevScale)
   3055                .ToUnknownRect());
   3056      } else {
   3057        const auto& layer = svgReset->mMask.mLayers[i];
   3058        if (layer.mClip == StyleGeometryBox::NoClip) {
   3059          return Nothing();
   3060        }
   3061 
   3062        nsCSSRendering::ImageLayerClipState clipState;
   3063        nsCSSRendering::GetImageLayerClip(
   3064            layer, aMaskedFrame, *aMaskedFrame->StyleBorder(), borderArea,
   3065            dirtyRect, false /* aWillPaintBorder */, devPixelRatio, &clipState);
   3066        clipArea = clipState.mDirtyRectInDevPx;
   3067      }
   3068      combinedClip = UnionMaybeRects(combinedClip, Some(clipArea));
   3069    }
   3070  }
   3071  if (combinedClip) {
   3072    // Convert to user space.
   3073    *combinedClip += devPixelOffsetToUserSpace;
   3074 
   3075    // Round the clip out. In FrameLayerBuilder we round clips to nearest
   3076    // pixels, and if we have a really thin clip here, that can cause the
   3077    // clip to become empty if we didn't round out here.
   3078    // The rounding happens in coordinates that are relative to the reference
   3079    // frame, which matches what FrameLayerBuilder does.
   3080    combinedClip->RoundOut();
   3081 
   3082    // Convert to app units.
   3083    nsRect result =
   3084        nsLayoutUtils::RoundGfxRectToAppRect(*combinedClip, devPixelRatio);
   3085 
   3086    // The resulting clip is relative to the reference frame, but the caller
   3087    // expects it to be relative to the masked frame, so adjust it.
   3088    result -= toReferenceFrame;
   3089    return Some(result);
   3090  }
   3091  return Nothing();
   3092 }
   3093 
   3094 struct AutoCheckBuilder {
   3095  explicit AutoCheckBuilder(nsDisplayListBuilder* aBuilder)
   3096      : mBuilder(aBuilder) {
   3097    aBuilder->Check();
   3098  }
   3099 
   3100  ~AutoCheckBuilder() { mBuilder->Check(); }
   3101 
   3102  nsDisplayListBuilder* mBuilder;
   3103 };
   3104 
   3105 /**
   3106 * Tries to reuse a top-level stacking context item from the previous paint.
   3107 * Returns true if an item was reused, otherwise false.
   3108 */
   3109 bool TryToReuseStackingContextItem(nsDisplayListBuilder* aBuilder,
   3110                                   nsDisplayList* aList, nsIFrame* aFrame) {
   3111  if (!aBuilder->IsForPainting() || !aBuilder->IsPartialUpdate() ||
   3112      aBuilder->InInvalidSubtree()) {
   3113    return false;
   3114  }
   3115 
   3116  if (aFrame->IsFrameModified() || aFrame->HasModifiedDescendants()) {
   3117    return false;
   3118  }
   3119 
   3120  auto& items = aFrame->DisplayItems();
   3121  auto* res = std::find_if(
   3122      items.begin(), items.end(),
   3123      [](nsDisplayItem* aItem) { return aItem->IsPreProcessed(); });
   3124 
   3125  if (res == items.end()) {
   3126    return false;
   3127  }
   3128 
   3129  nsDisplayItem* container = *res;
   3130  MOZ_ASSERT(container->Frame() == aFrame);
   3131  DL_LOGD("RDL - Found SC item %p (%s) (frame: %p)", container,
   3132          container->Name(), container->Frame());
   3133 
   3134  aList->AppendToTop(container);
   3135  aBuilder->ReuseDisplayItem(container);
   3136  return true;
   3137 }
   3138 
   3139 void nsIFrame::BuildDisplayListForStackingContext(
   3140    nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
   3141    bool* aCreatedContainerItem) {
   3142 #ifdef DEBUG
   3143  DL_LOGV("BuildDisplayListForStackingContext (%p) <", this);
   3144  ScopeExit e(
   3145      [this]() { DL_LOGV("> BuildDisplayListForStackingContext (%p)", this); });
   3146 #endif
   3147 
   3148  AutoCheckBuilder check(aBuilder);
   3149 
   3150  if (aBuilder->IsReusingStackingContextItems() &&
   3151      TryToReuseStackingContextItem(aBuilder, aList, this)) {
   3152    if (aCreatedContainerItem) {
   3153      *aCreatedContainerItem = true;
   3154    }
   3155    return;
   3156  }
   3157 
   3158  if (HasAnyStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE)) {
   3159    return;
   3160  }
   3161 
   3162  const auto& style = *Style();
   3163  const nsStyleDisplay* disp = style.StyleDisplay();
   3164  const nsStyleEffects* effects = style.StyleEffects();
   3165  EffectSet* effectSetForOpacity =
   3166      EffectSet::GetForFrame(this, nsCSSPropertyIDSet::OpacityProperties());
   3167  // We can stop right away if this is a zero-opacity stacking context and
   3168  // we're painting, and we're not animating opacity.
   3169  bool needHitTestInfo = aBuilder->BuildCompositorHitTestInfo() &&
   3170                         Style()->PointerEvents() != StylePointerEvents::None;
   3171  bool opacityItemForEventsOnly = false;
   3172  if (effects->IsTransparent() && aBuilder->IsForPainting() &&
   3173      !(disp->mWillChange.bits & StyleWillChangeBits::OPACITY) &&
   3174      !nsLayoutUtils::HasAnimationOfPropertySet(
   3175          this, nsCSSPropertyIDSet::OpacityProperties(), effectSetForOpacity)) {
   3176    if (needHitTestInfo) {
   3177      opacityItemForEventsOnly = true;
   3178    } else {
   3179      return;
   3180    }
   3181  }
   3182 
   3183  // Root gets handled in
   3184  // ScrollContainerFrame::MaybeCreateTopLayerAndWrapRootItems.
   3185  const bool capturedByViewTransition =
   3186      HasAnyStateBits(NS_FRAME_CAPTURED_IN_VIEW_TRANSITION) &&
   3187      !style.IsRootElementStyle();
   3188 
   3189  // Do not need to build the display list of the captured frames for event
   3190  // delivery (i.e. to determine if this frame is under the mouse position).
   3191  //
   3192  // Per spec, hit-testing is skipped because the element’s DOM location does
   3193  // not correspond to where its contents are rendered, so we could just skip
   3194  // the building of the display list for these frames, and then these APIs,
   3195  // e.g. elementFromPoint(), will skip these frames as well.
   3196  // https://drafts.csswg.org/css-view-transitions-1/#view-transition-stacking-layer
   3197  if (capturedByViewTransition && aBuilder->IsForEventDelivery()) {
   3198    return;
   3199  }
   3200 
   3201  if (aBuilder->IsForPainting() && disp->mWillChange.bits) {
   3202    aBuilder->AddToWillChangeBudget(this, GetSize());
   3203  }
   3204 
   3205  // For preserves3d, use the dirty rect already installed on the
   3206  // builder, since aDirtyRect maybe distorted for transforms along
   3207  // the chain.
   3208  nsRect visibleRect = aBuilder->GetVisibleRect();
   3209  nsRect dirtyRect = aBuilder->GetDirtyRect();
   3210 
   3211  // We build an opacity item if it's not going to be drawn by SVG content.
   3212  // We could in principle skip creating an nsDisplayOpacity item if
   3213  // nsDisplayOpacity::NeedsActiveLayer returns false and usingSVGEffects is
   3214  // true (the nsDisplayFilter/nsDisplayMasksAndClipPaths could handle the
   3215  // opacity). Since SVG has perf issues where we sometimes spend a lot of
   3216  // time creating display list items that might be helpful.  We'd need to
   3217  // restore our mechanism to do that (changed in bug 1482403), and we'd
   3218  // need to invalidate the frame if the value that would be return from
   3219  // NeedsActiveLayer was to change, which we don't currently do.
   3220  const bool useOpacity =
   3221      HasVisualOpacity(disp, effects, effectSetForOpacity) &&
   3222      !SVGUtils::CanOptimizeOpacity(this);
   3223 
   3224  const bool isTransformed = IsTransformed();
   3225  const bool hasPerspective = isTransformed && HasPerspective();
   3226  const bool extend3DContext =
   3227      Extend3DContext(disp, effects, effectSetForOpacity);
   3228  const bool combines3DTransformWithAncestors =
   3229      (extend3DContext || isTransformed) && Combines3DTransformWithAncestors();
   3230 
   3231  UniquePtr<nsDisplayListBuilder::AutoPreserves3DContext>
   3232      autoPreserves3DContext;
   3233  if (extend3DContext && !combines3DTransformWithAncestors) {
   3234    // Start a new preserves3d context to keep informations on
   3235    // nsDisplayListBuilder.
   3236    autoPreserves3DContext =
   3237        MakeUnique<nsDisplayListBuilder::AutoPreserves3DContext>(aBuilder);
   3238    // Save dirty rect on the builder to avoid being distorted for
   3239    // multiple transforms along the chain.
   3240    aBuilder->SavePreserves3DRect();
   3241 
   3242    // We rebuild everything within preserve-3d and don't try
   3243    // to retain, so override the dirty rect now.
   3244    if (aBuilder->IsRetainingDisplayList()) {
   3245      dirtyRect = visibleRect;
   3246      aBuilder->SetDisablePartialUpdates(true);
   3247    }
   3248  }
   3249 
   3250  AutoTrackStackingContextBits stackingContextTracker(*aBuilder);
   3251  aBuilder->ClearStackingContextBits();
   3252 
   3253  nsRect visibleRectOutsideTransform = visibleRect;
   3254  nsDisplayTransform::PrerenderInfo prerenderInfo;
   3255  bool inTransform = aBuilder->IsInTransform();
   3256  if (isTransformed) {
   3257    prerenderInfo = nsDisplayTransform::ShouldPrerenderTransformedContent(
   3258        aBuilder, this, &visibleRect);
   3259 
   3260    switch (prerenderInfo.mDecision) {
   3261      case nsDisplayTransform::PrerenderDecision::Full:
   3262      case nsDisplayTransform::PrerenderDecision::Partial:
   3263        dirtyRect = visibleRect;
   3264        break;
   3265      case nsDisplayTransform::PrerenderDecision::No: {
   3266        // If we didn't prerender an animated frame in a preserve-3d context,
   3267        // then we want disable async animations for the rest of the preserve-3d
   3268        // (especially ancestors).
   3269        if ((extend3DContext || combines3DTransformWithAncestors) &&
   3270            prerenderInfo.mHasAnimations) {
   3271          aBuilder->SavePreserves3DAllowAsyncAnimation(false);
   3272        }
   3273 
   3274        const nsRect overflow = InkOverflowRectRelativeToSelf();
   3275        if (overflow.IsEmpty() && !extend3DContext) {
   3276          return;
   3277        }
   3278 
   3279        // If we're in preserve-3d then grab the dirty rect that was given to
   3280        // the root and transform using the combined transform.
   3281        if (combines3DTransformWithAncestors) {
   3282          visibleRect = dirtyRect = aBuilder->GetPreserves3DRect();
   3283        }
   3284 
   3285        const float appPerDev = PresContext()->AppUnitsPerDevPixel();
   3286        uint32_t flags = nsDisplayTransform::kTransformRectFlags &
   3287                         ~nsDisplayTransform::OFFSET_BY_ORIGIN;
   3288        if (!hasPerspective) {
   3289          flags &= ~nsDisplayTransform::INCLUDE_PERSPECTIVE;
   3290        }
   3291        if (!combines3DTransformWithAncestors) {
   3292          flags &= ~nsDisplayTransform::INCLUDE_PRESERVE3D_ANCESTORS;
   3293        }
   3294        auto transform = nsDisplayTransform::GetResultingTransformMatrix(
   3295            this, nsPoint(), appPerDev, flags);
   3296        nsRect untransformedDirtyRect;
   3297        if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, transform,
   3298                                                appPerDev,
   3299                                                &untransformedDirtyRect)) {
   3300          dirtyRect = untransformedDirtyRect;
   3301          nsDisplayTransform::UntransformRect(visibleRect, overflow, transform,
   3302                                              appPerDev, &visibleRect);
   3303        } else {
   3304          // This should only happen if the transform is singular, in which case
   3305          // nothing is visible anyway
   3306          dirtyRect.SetEmpty();
   3307          visibleRect.SetEmpty();
   3308        }
   3309      }
   3310    }
   3311    inTransform = true;
   3312  } else if (IsFixedPosContainingBlock()) {
   3313    // Restict the building area to the overflow rect for these frames, since
   3314    // RetainedDisplayListBuilder uses it to know if the size of the stacking
   3315    // context changed.
   3316    visibleRect.IntersectRect(visibleRect, InkOverflowRect());
   3317    dirtyRect.IntersectRect(dirtyRect, InkOverflowRect());
   3318  }
   3319 
   3320  bool hasOverrideDirtyRect = false;
   3321  // If we're doing a partial build, we're not invalid and we're capable
   3322  // of having an override building rect (stacking context and fixed pos
   3323  // containing block), then we should assume we have one.
   3324  // Either we have an explicit one, or nothing in our subtree changed and
   3325  // we have an implicit empty rect.
   3326  //
   3327  // These conditions should match |CanStoreDisplayListBuildingRect()| in
   3328  // RetainedDisplayListBuilder.cpp
   3329  if (!aBuilder->IsReusingStackingContextItems() &&
   3330      aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
   3331      !IsFrameModified() && IsFixedPosContainingBlock() &&
   3332      !GetPrevContinuation() && !GetNextContinuation()) {
   3333    dirtyRect = nsRect();
   3334    if (HasOverrideDirtyRegion()) {
   3335      nsDisplayListBuilder::DisplayListBuildingData* data =
   3336          GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
   3337      if (data) {
   3338        dirtyRect = data->mDirtyRect.Intersect(visibleRect);
   3339        hasOverrideDirtyRect = true;
   3340      }
   3341    }
   3342  }
   3343 
   3344  const bool usingFilter = effects->HasFilters() && !style.IsRootElementStyle();
   3345  const SVGUtils::MaskUsage maskUsage =
   3346      SVGUtils::DetermineMaskUsage(this, false);
   3347  const bool usingMask = maskUsage.UsingMaskOrClipPath();
   3348  const bool usingSVGEffects = usingFilter || usingMask;
   3349 
   3350  const nsRect visibleRectOutsideSVGEffects = visibleRect;
   3351  nsDisplayList hoistedScrollInfoItemsStorage(aBuilder);
   3352  if (usingSVGEffects) {
   3353    dirtyRect =
   3354        SVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
   3355    visibleRect =
   3356        SVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, visibleRect);
   3357    aBuilder->EnterSVGEffectsContents(this, &hoistedScrollInfoItemsStorage);
   3358  }
   3359 
   3360  const bool useStickyPosition =
   3361      disp->mPosition == StylePositionProperty::Sticky;
   3362  bool shouldFlattenStickyItem = true;
   3363 
   3364  const bool useFixedPosition =
   3365      disp->mPosition == StylePositionProperty::Fixed &&
   3366      aBuilder->IsPaintingToWindow() && !IsMenuPopupFrame() &&
   3367      (DisplayPortUtils::IsFixedPosFrameInDisplayPort(this) ||
   3368       BuilderHasScrolledClip(aBuilder));
   3369 
   3370  if (capturedByViewTransition) {
   3371    // Captured view transition elements must have their frames built regardless
   3372    // of onscreen visibility so they can be snapshotted, since the snapshot can
   3373    // itself be in view. We set visibleRect and dirtyRect to ensure the frame
   3374    // and its descendants are painted.
   3375    visibleRect = InkOverflowRectRelativeToSelf();
   3376    dirtyRect = InkOverflowRectRelativeToSelf();
   3377  }
   3378 
   3379  nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
   3380      aBuilder, this, visibleRect, dirtyRect, isTransformed);
   3381 
   3382  UpdateCurrentHitTestInfo(aBuilder, this);
   3383 
   3384  // Depending on the effects that are applied to this frame, we can create
   3385  // multiple container display items and wrap them around our contents.
   3386  // This enum lists all the potential container display items, in the order
   3387  // outside to inside.
   3388  enum class ContainerItemType : uint8_t {
   3389    None = 0,
   3390    FixedPosition,
   3391    OwnLayerForTransformWithRoundedClip,
   3392    Perspective,
   3393    Transform,
   3394    Filter,
   3395    ViewTransitionCapture,
   3396  };
   3397 
   3398  // NOTE(emilio): The order of these RAII objects is quite subtle.
   3399  nsDisplayListBuilder::AutoEnterViewTransitionCapture
   3400      inViewTransitionCaptureSetter(aBuilder, capturedByViewTransition);
   3401  RefPtr<const ActiveScrolledRoot> stickyASR = nullptr;
   3402  nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
   3403  if (aBuilder->IsInViewTransitionCapture()) {
   3404    // View transition contents shouldn't scroll along our ASR. They get
   3405    // "pulled out" of the rendering (or when they don't, you can't scroll
   3406    // anyways).
   3407    asrSetter.SetCurrentActiveScrolledRoot(nullptr);
   3408  }
   3409  if (useStickyPosition) {
   3410    StickyScrollContainer* stickyScrollContainer =
   3411        StickyScrollContainer::GetOrCreateForFrame(this);
   3412    if (stickyScrollContainer) {
   3413      if (aBuilder->IsPaintingToWindow() &&
   3414          !aBuilder->IsInViewTransitionCapture() &&
   3415          stickyScrollContainer->ScrollContainer()
   3416              ->IsMaybeAsynchronouslyScrolled()) {
   3417        shouldFlattenStickyItem = false;
   3418      }
   3419      stickyScrollContainer->SetShouldFlatten(shouldFlattenStickyItem);
   3420    }
   3421 
   3422    if (shouldFlattenStickyItem) {
   3423      stickyASR = aBuilder->CurrentActiveScrolledRoot();
   3424    } else {
   3425      stickyASR = aBuilder->GetOrCreateActiveScrolledRootForSticky(
   3426          aBuilder->CurrentActiveScrolledRoot(), this);
   3427      asrSetter.SetCurrentActiveScrolledRoot(stickyASR);
   3428    }
   3429  }
   3430 
   3431  nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
   3432 
   3433  auto cssClip = GetClipPropClipRect(disp, effects, GetSize());
   3434  auto ApplyClipProp = [&](DisplayListClipState::AutoSaveRestore& aClipState) {
   3435    if (!cssClip) {
   3436      return;
   3437    }
   3438    nsPoint offset = aBuilder->GetCurrentFrameOffsetToReferenceFrame();
   3439    aBuilder->IntersectDirtyRect(*cssClip);
   3440    aBuilder->IntersectVisibleRect(*cssClip);
   3441    aClipState.ClipContentDescendants(*cssClip + offset);
   3442  };
   3443 
   3444  // The CSS clip property is effectively inside the transform, but outside the
   3445  // filters. So if we're not transformed we can apply it just here for
   3446  // simplicity, instead of on each of the places that handle clipCapturedBy.
   3447  DisplayListClipState::AutoSaveRestore untransformedCssClip(aBuilder);
   3448  if (!isTransformed) {
   3449    ApplyClipProp(untransformedCssClip);
   3450  }
   3451 
   3452  // If there is a current clip, then depending on the container items we
   3453  // create, different things can happen to it. Some container items simply
   3454  // propagate the clip to their children and aren't clipped themselves.
   3455  // But other container items, especially those that establish a different
   3456  // geometry for their contents (e.g. transforms), capture the clip on
   3457  // themselves and unset the clip for their contents. If we create more than
   3458  // one of those container items, the clip will be captured on the outermost
   3459  // one and the inner container items will be unclipped.
   3460  ContainerItemType clipCapturedBy = ContainerItemType::None;
   3461  if (capturedByViewTransition) {
   3462    clipCapturedBy = isTransformed ? ContainerItemType::Transform
   3463                                   : ContainerItemType::ViewTransitionCapture;
   3464  } else if (useFixedPosition) {
   3465    clipCapturedBy = ContainerItemType::FixedPosition;
   3466  } else if (isTransformed) {
   3467    const DisplayItemClipChain* currentClip =
   3468        aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
   3469    if ((hasPerspective || extend3DContext) &&
   3470        (currentClip && currentClip->HasRoundedCorners())) {
   3471      // If we're creating an nsDisplayTransform item that is going to combine
   3472      // its transform with its children (preserve-3d or perspective), then we
   3473      // can't have an intermediate surface. Mask layers force an intermediate
   3474      // surface, so if we're going to need both then create a separate
   3475      // wrapping layer for the mask.
   3476      clipCapturedBy = ContainerItemType::OwnLayerForTransformWithRoundedClip;
   3477    } else if (hasPerspective) {
   3478      clipCapturedBy = ContainerItemType::Perspective;
   3479    } else {
   3480      clipCapturedBy = ContainerItemType::Transform;
   3481    }
   3482  } else if (usingFilter) {
   3483    clipCapturedBy = ContainerItemType::Filter;
   3484  }
   3485 
   3486  DisplayListClipState::AutoSaveRestore clipState(aBuilder);
   3487  if (clipCapturedBy != ContainerItemType::None) {
   3488    clipState.Clear();
   3489  }
   3490 
   3491  DisplayListClipState::AutoSaveRestore transformedCssClip(aBuilder);
   3492  if (isTransformed) {
   3493    // FIXME(emilio, bug 1525159): In the case we have a both a transform _and_
   3494    // filters, this clips the input to the filters as well, which is not
   3495    // correct (clipping by the `clip` property is supposed to happen after
   3496    // applying the filter effects, per [1].
   3497    //
   3498    // This is not a regression though, since we used to do that anyway before
   3499    // bug 1514384, and even without the transform we get it wrong.
   3500    //
   3501    // [1]: https://drafts.fxtf.org/css-masking/#placement
   3502    ApplyClipProp(transformedCssClip);
   3503  }
   3504 
   3505  uint32_t numActiveScrollframesEncounteredBefore =
   3506      aBuilder->GetNumActiveScrollframesEncountered();
   3507 
   3508  nsDisplayListCollection set(aBuilder);
   3509  Maybe<nsRect> clipForMask;
   3510 
   3511  {
   3512    DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
   3513    nsDisplayListBuilder::AutoInTransformSetter inTransformSetter(aBuilder,
   3514                                                                  inTransform);
   3515    nsDisplayListBuilder::AutoEnterFilter filterASRSetter(aBuilder,
   3516                                                          usingFilter);
   3517    nsDisplayListBuilder::AutoInEventsOnly inEventsSetter(
   3518        aBuilder, opacityItemForEventsOnly);
   3519 
   3520    DisplayListClipState::AutoSaveRestore stickyItemNestedClipState(aBuilder);
   3521    if (useStickyPosition && !shouldFlattenStickyItem) {
   3522      stickyItemNestedClipState.MaybeRemoveDisplayportClip();
   3523    }
   3524 
   3525    // If we have a mask, compute a clip to bound the masked content.
   3526    // This is necessary in case the content moves with an ancestor
   3527    // ASR of the mask.
   3528    // Don't do this if we also have a filter, because then the clip
   3529    // would be applied before the filter, violating
   3530    // https://www.w3.org/TR/filter-effects-1/#placement.
   3531    // Filters are a containing block for fixed and absolute descendants,
   3532    // so the masked content cannot move with an ancestor ASR.
   3533    if (usingMask && !usingFilter) {
   3534      clipForMask = ComputeClipForMaskItem(aBuilder, this, maskUsage);
   3535      if (clipForMask) {
   3536        aBuilder->IntersectDirtyRect(*clipForMask);
   3537        aBuilder->IntersectVisibleRect(*clipForMask);
   3538        nestedClipState.ClipContentDescendants(
   3539            *clipForMask + aBuilder->GetCurrentFrameOffsetToReferenceFrame());
   3540      }
   3541    }
   3542 
   3543    // extend3DContext also guarantees that applyAbsPosClipping and
   3544    // usingSVGEffects are false We only modify the preserve-3d rect if we are
   3545    // the top of a preserve-3d heirarchy
   3546    if (extend3DContext) {
   3547      // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
   3548      // going to be forced to descend into frames.
   3549      aBuilder->MarkPreserve3DFramesForDisplayList(this);
   3550    }
   3551 
   3552    aBuilder->AdjustWindowDraggingRegion(this);
   3553 
   3554    MarkAbsoluteFramesForDisplayList(aBuilder);
   3555    aBuilder->Check();
   3556    BuildDisplayList(aBuilder, set);
   3557    SetBuiltDisplayList(true);
   3558    aBuilder->Check();
   3559    aBuilder->DisplayCaret(this, set.Outlines());
   3560 
   3561    // Blend modes are a real pain for retained display lists. We build a blend
   3562    // container item if the built list contains any blend mode items within
   3563    // the current stacking context. This can change without an invalidation
   3564    // to the stacking context frame, or the blend mode frame (e.g. by moving
   3565    // an intermediate frame).
   3566    // When we gain/remove a blend container item, we need to mark this frame
   3567    // as invalid and have the full display list for merging to track
   3568    // the change correctly.
   3569    // It seems really hard to track this in advance, as the bookkeeping
   3570    // required to note which stacking contexts have blend descendants
   3571    // is complex and likely to be buggy.
   3572    // Instead we're doing the sad thing, detecting it afterwards, and just
   3573    // repeating display list building if it changed.
   3574    // We have to repeat building for the entire display list (or at least
   3575    // the outer stacking context), since we need to mark this frame as invalid
   3576    // to remove any existing content that isn't wrapped in the blend container,
   3577    // and then we need to build content infront/behind the blend container
   3578    // to get correct positioning during merging.
   3579    if (aBuilder->ContainsBlendMode() && aBuilder->IsRetainingDisplayList()) {
   3580      if (aBuilder->IsPartialUpdate()) {
   3581        aBuilder->SetPartialBuildFailed(true);
   3582      } else {
   3583        aBuilder->SetDisablePartialUpdates(true);
   3584      }
   3585    }
   3586  }
   3587 
   3588  if (aBuilder->IsBackgroundOnly()) {
   3589    set.BlockBorderBackgrounds()->DeleteAll(aBuilder);
   3590    set.Floats()->DeleteAll(aBuilder);
   3591    set.Content()->DeleteAll(aBuilder);
   3592    set.PositionedDescendants()->DeleteAll(aBuilder);
   3593    set.Outlines()->DeleteAll(aBuilder);
   3594  }
   3595 
   3596  if (hasOverrideDirtyRect &&
   3597      StaticPrefs::layout_display_list_show_rebuild_area()) {
   3598    nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
   3599        aBuilder, this,
   3600        dirtyRect + aBuilder->GetCurrentFrameOffsetToReferenceFrame(),
   3601        NS_RGBA(255, 0, 0, 64), false);
   3602    if (color) {
   3603      color->SetOverrideZIndex(INT32_MAX);
   3604      set.PositionedDescendants()->AppendToTop(color);
   3605    }
   3606  }
   3607 
   3608  nsIContent* content = GetContent();
   3609  if (!content) {
   3610    content = PresContext()->Document()->GetRootElement();
   3611  }
   3612 
   3613  nsDisplayList resultList(aBuilder);
   3614  set.SerializeWithCorrectZOrder(&resultList, content);
   3615 
   3616  // Get the ASR to use for the container items that we create here.
   3617  const ActiveScrolledRoot* containerItemASR = contASRTracker.GetContainerASR();
   3618 
   3619  bool createdContainer = false;
   3620  const StackingContextBits localIsolationReasons = [&] {
   3621    auto reasons = StackingContextBits::None;
   3622    if (!GetParent()) {
   3623      // We don't need to isolate the root frame.
   3624      return reasons;
   3625    }
   3626    // Elements with a view-transition name also form a backdrop-root. Same for
   3627    // masks / clip-path.
   3628    // See https://www.w3.org/TR/css-view-transitions-1/#named-and-transitioning
   3629    // and https://github.com/w3c/csswg-drafts/issues/11772
   3630    const bool hasViewTransitionName =
   3631        style.StyleUIReset()->HasViewTransitionName() &&
   3632        !style.IsRootElementStyle();
   3633    if ((disp->mWillChange.bits & StyleWillChangeBits::BACKDROP_ROOT) ||
   3634        hasViewTransitionName || usingMask) {
   3635      reasons |= StackingContextBits::ContainsBackdropFilter;
   3636    }
   3637    if (!combines3DTransformWithAncestors) {
   3638      reasons |= StackingContextBits::MayContainNonIsolated3DTransform;
   3639    }
   3640    return reasons;
   3641  }();
   3642 
   3643  StackingContextBits currentIsolationReasons =
   3644      localIsolationReasons & aBuilder->GetStackingContextBits();
   3645  bool isolated = false;
   3646  auto MarkAsIsolated = [&] {
   3647    isolated = true;
   3648    currentIsolationReasons = StackingContextBits::None;
   3649  };
   3650  auto ShouldForceIsolation = [&] {
   3651    if (localIsolationReasons == StackingContextBits::None) {
   3652      return false;
   3653    }
   3654    bool force = currentIsolationReasons != StackingContextBits::None;
   3655    MarkAsIsolated();
   3656    return force;
   3657  };
   3658 
   3659  // If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
   3660  // same list, the nsDisplayBlendContainer should be added first. This only
   3661  // happens when the element creating this stacking context has mix-blend-mode
   3662  // and also contains a child which has mix-blend-mode.
   3663  // The nsDisplayBlendContainer must be added to the list first, so it does not
   3664  // isolate the containing element blending as well.
   3665  if (aBuilder->ContainsBlendMode()) {
   3666    resultList.AppendToTop(nsDisplayBlendContainer::CreateForMixBlendMode(
   3667        aBuilder, this, &resultList, containerItemASR,
   3668        nsDisplayItem::ContainerASRType::AncestorOfContained));
   3669    createdContainer = true;
   3670    MarkAsIsolated();
   3671  }
   3672 
   3673  // NOTE: When changing this condition make sure to tweak ScrollContainerFrame
   3674  // as well.
   3675  const bool usingBackdropFilter = effects->HasBackdropFilters() &&
   3676                                   IsVisibleForPainting() &&
   3677                                   !style.IsRootElementStyle();
   3678  if (usingBackdropFilter) {
   3679    stackingContextTracker.AddToParent(
   3680        StackingContextBits::ContainsBackdropFilter);
   3681    nsRect backdropRect =
   3682        GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
   3683    resultList.AppendNewToTop<nsDisplayBackdropFilters>(
   3684        aBuilder, this, &resultList, backdropRect, this);
   3685    createdContainer = true;
   3686    MarkAsIsolated();
   3687  }
   3688 
   3689  // If there are any SVG effects, wrap the list up in an SVG effects item
   3690  // (which also handles CSS group opacity). Note that we create an SVG effects
   3691  // item even if resultList is empty, since a filter can produce graphical
   3692  // output even if the element being filtered wouldn't otherwise do so.
   3693  if (usingSVGEffects) {
   3694    MOZ_ASSERT(usingFilter || usingMask,
   3695               "Beside filter & mask/clip-path, what else effect do we have?");
   3696 
   3697    if (clipCapturedBy == ContainerItemType::Filter) {
   3698      clipState.Restore();
   3699    }
   3700    // Revert to the post-filter dirty rect.
   3701    aBuilder->SetVisibleRect(visibleRectOutsideSVGEffects);
   3702 
   3703    // Skip all filter effects while generating glyph mask.
   3704    if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) {
   3705      /* List now emptied, so add the new list to the top. */
   3706      resultList.AppendNewToTop<nsDisplayFilters>(aBuilder, this, &resultList,
   3707                                                  this, usingBackdropFilter);
   3708      createdContainer = true;
   3709    }
   3710 
   3711    if (usingMask) {
   3712      // The mask should move with aBuilder->CurrentActiveScrolledRoot(), so
   3713      // that's the ASR we prefer to use for the mask item. However, we can
   3714      // only do this if the mask if clipped with respect to that ASR, because
   3715      // an item always needs to have finite bounds with respect to its ASR.
   3716      // If we weren't able to compute a clip for the mask, we fall back to
   3717      // using containerItemASR, which is the lowest common ancestor clip of
   3718      // the mask's contents. That's not entirely correct, but it satisfies
   3719      // the base requirement of the ASR system (that items have finite bounds
   3720      // wrt. their ASR).
   3721      const ActiveScrolledRoot* maskASR =
   3722          clipForMask.isSome() ? aBuilder->CurrentActiveScrolledRoot()
   3723                               : containerItemASR;
   3724      /* List now emptied, so add the new list to the top. */
   3725      resultList.AppendNewToTop<nsDisplayMasksAndClipPaths>(
   3726          aBuilder, this, &resultList, maskASR,
   3727          clipForMask.isSome()
   3728              ? nsDisplayItem::ContainerASRType::Constant
   3729              : nsDisplayItem::ContainerASRType::AncestorOfContained,
   3730          usingBackdropFilter, ShouldForceIsolation());
   3731      createdContainer = true;
   3732    }
   3733 
   3734    // TODO(miko): We could probably create a wraplist here and avoid creating
   3735    // it later in |BuildDisplayListForChild()|.
   3736    createdContainer = false;
   3737 
   3738    // Also add the hoisted scroll info items. We need those for APZ scrolling
   3739    // because nsDisplayMasksAndClipPaths items can't build active layers.
   3740    aBuilder->ExitSVGEffectsContents();
   3741    resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
   3742  }
   3743 
   3744  // If the list is non-empty and there is CSS group opacity without SVG
   3745  // effects, wrap it up in an opacity item.
   3746  if (useOpacity) {
   3747    const bool needsActiveOpacityLayer =
   3748        nsDisplayOpacity::NeedsActiveLayer(aBuilder, this);
   3749    resultList.AppendNewToTop<nsDisplayOpacity>(
   3750        aBuilder, this, &resultList, containerItemASR,
   3751        nsDisplayItem::ContainerASRType::AncestorOfContained,
   3752        opacityItemForEventsOnly, needsActiveOpacityLayer, usingBackdropFilter,
   3753        ShouldForceIsolation());
   3754    createdContainer = true;
   3755  }
   3756 
   3757  // We build nsDisplayViewTransitionCapture here, and then use
   3758  // nsDisplayTransform to wrap it, to make sure we create the correct transform
   3759  // for the captured element and its descendants. This is necessary to make
   3760  // sure our captured element doesn't become blurry when using scale()
   3761  // transform.
   3762  //
   3763  // So the display list looks like this:
   3764  //   nsDisplayTransform      // For the captured element if it is transformed
   3765  //     VTCapture             // For the captured element
   3766  //       ...
   3767  //       Other display items // For the descendants of the captured element
   3768  //       ...
   3769  //   ...
   3770  //
   3771  // We intentionally use nsDisplayTransform to wrap the VTCapture (so it's
   3772  // different from opacity display item) because nsDisplayTransform may push a
   3773  // reference frame which creates a new coordinate system in WR. So it's just
   3774  // like a separator between the VTCapture the outside, if it is transformed.
   3775  if (capturedByViewTransition) {
   3776    resultList.AppendNewToTop<nsDisplayViewTransitionCapture>(
   3777        aBuilder, this, &resultList, nullptr, false);
   3778    createdContainer = true;
   3779    MarkAsIsolated();
   3780    // We don't want the capture to be clipped, so we do this _after_ building
   3781    // the wrapping item.
   3782    if (clipCapturedBy == ContainerItemType::ViewTransitionCapture) {
   3783      clipState.Restore();
   3784    }
   3785  }
   3786 
   3787  // If we're going to apply a transformation and don't have preserve-3d set,
   3788  // wrap everything in an nsDisplayTransform. If there's nothing in the list,
   3789  // don't add anything.
   3790  //
   3791  // For the preserve-3d case we want to individually wrap every child in the
   3792  // list with a separate nsDisplayTransform instead. When the child is already
   3793  // an nsDisplayTransform, we can skip this step, as the computed transform
   3794  // will already include our own.
   3795  //
   3796  // We also traverse into sublists created by nsDisplayWrapList, so that we
   3797  // find all the correct children.
   3798  //
   3799  // We still need to create nsDisplayTransform to wrap the VT capture element
   3800  // to make sure WR renders it properly if it is transformed as well.
   3801  if (isTransformed) {
   3802    if (extend3DContext) {
   3803      // Install dummy nsDisplayTransform as a leaf containing
   3804      // descendants not participating this 3D rendering context.
   3805      nsDisplayList nonparticipants(aBuilder);
   3806      nsDisplayList participants(aBuilder);
   3807      int index = 1;
   3808 
   3809      nsDisplayItem* separator = nullptr;
   3810 
   3811      // TODO: This can be simplified: |participants| is just |resultList|.
   3812      for (nsDisplayItem* item : resultList.TakeItems()) {
   3813        if (ItemParticipatesIn3DContext(this, item) &&
   3814            !item->GetClip().HasClip()) {
   3815          // The frame of this item participates the same 3D context.
   3816          WrapSeparatorTransform(aBuilder, this, &nonparticipants,
   3817                                 &participants, index++, &separator);
   3818 
   3819          participants.AppendToTop(item);
   3820        } else {
   3821          // The frame of the item doesn't participate the current
   3822          // context, or has no transform.
   3823          //
   3824          // For items participating but not transformed, they are add
   3825          // to nonparticipants to get a separator layer for handling
   3826          // clips, if there is, on an intermediate surface.
   3827          // \see ContainerLayer::DefaultComputeEffectiveTransforms().
   3828          nonparticipants.AppendToTop(item);
   3829        }
   3830      }
   3831      WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants,
   3832                             index++, &separator);
   3833 
   3834      if (separator) {
   3835        createdContainer = true;
   3836        MarkAsIsolated();
   3837      }
   3838 
   3839      resultList.AppendToTop(&participants);
   3840    }
   3841 
   3842    transformedCssClip.Restore();
   3843    if (clipCapturedBy == ContainerItemType::Transform) {
   3844      // Restore clip state now so nsDisplayTransform is clipped properly.
   3845      clipState.Restore();
   3846    }
   3847    // Revert to the dirtyrect coming in from the parent, without our transform
   3848    // taken into account.
   3849    aBuilder->SetVisibleRect(visibleRectOutsideTransform);
   3850 
   3851    if (this != aBuilder->RootReferenceFrame()) {
   3852      // Revert to the outer reference frame and offset because all display
   3853      // items we create from now on are outside the transform.
   3854      nsPoint toOuterReferenceFrame;
   3855      const nsIFrame* outerReferenceFrame =
   3856          aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
   3857      toOuterReferenceFrame += GetPosition();
   3858 
   3859      buildingDisplayList.SetReferenceFrameAndCurrentOffset(
   3860          outerReferenceFrame, toOuterReferenceFrame);
   3861    }
   3862 
   3863    // We would like to block async animations for ancestors of ones not
   3864    // prerendered in the preserve-3d tree. Now that we've finished processing
   3865    // all descendants, update allowAsyncAnimation to take their prerender
   3866    // state into account
   3867    // FIXME: We don't block async animations for previous siblings because
   3868    // their prerender decisions have been made. We may have to figure out a
   3869    // better way to rollback their prerender decisions.
   3870    // Alternatively we could not block animations for later siblings, and only
   3871    // block them for ancestors of a blocked one.
   3872    if ((extend3DContext || combines3DTransformWithAncestors) &&
   3873        prerenderInfo.CanUseAsyncAnimations() &&
   3874        !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
   3875      // aBuilder->GetPreserves3DAllowAsyncAnimation() means the inner or
   3876      // previous silbing frames are allowed/disallowed for async animations.
   3877      prerenderInfo.mDecision = nsDisplayTransform::PrerenderDecision::No;
   3878    }
   3879 
   3880    nsDisplayTransform* transformItem = MakeDisplayItem<nsDisplayTransform>(
   3881        aBuilder, this, &resultList, visibleRect, prerenderInfo.mDecision,
   3882        usingBackdropFilter, ShouldForceIsolation());
   3883    if (transformItem) {
   3884      resultList.AppendToTop(transformItem);
   3885      createdContainer = true;
   3886 
   3887      if (numActiveScrollframesEncounteredBefore !=
   3888          aBuilder->GetNumActiveScrollframesEncountered()) {
   3889        transformItem->SetContainsASRs(true);
   3890      }
   3891 
   3892      if (hasPerspective) {
   3893        transformItem->MarkWithAssociatedPerspective();
   3894 
   3895        if (clipCapturedBy == ContainerItemType::Perspective) {
   3896          clipState.Restore();
   3897        }
   3898        resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this,
   3899                                                        &resultList);
   3900        createdContainer = true;
   3901      }
   3902 
   3903      // TODO(emilio): Ideally should also isolate when the transform is
   3904      // potentially animated (prerenderInfo.mHasAnimations), but that causes a
   3905      // lot of fuzz on Windows due to text antialiasing.
   3906      const bool hasMaybe3dTransform =
   3907          hasPerspective || !transformItem->GetTransform().Is2D();
   3908      if (hasMaybe3dTransform) {
   3909        stackingContextTracker.AddToParent(
   3910            StackingContextBits::MayContainNonIsolated3DTransform);
   3911      }
   3912    }
   3913    if (clipCapturedBy ==
   3914        ContainerItemType::OwnLayerForTransformWithRoundedClip) {
   3915      clipState.Restore();
   3916      resultList.AppendNewToTopWithIndex<nsDisplayOwnLayer>(
   3917          aBuilder, this,
   3918          /* aIndex = */ nsDisplayOwnLayer::OwnLayerForTransformWithRoundedClip,
   3919          &resultList, aBuilder->CurrentActiveScrolledRoot(),
   3920          nsDisplayItem::ContainerASRType::Constant,
   3921          nsDisplayOwnLayerFlags::None, ScrollbarData{},
   3922          /* aForceActive = */ false, false);
   3923      createdContainer = true;
   3924    }
   3925  }
   3926 
   3927  // If we have sticky positioning, wrap it in a sticky position item.
   3928  if (useFixedPosition && !capturedByViewTransition) {
   3929    if (clipCapturedBy == ContainerItemType::FixedPosition) {
   3930      clipState.Restore();
   3931    }
   3932    // The ASR for the fixed item should be the ASR of our containing block,
   3933    // which has been set as the builder's current ASR, unless this frame is
   3934    // invisible and we hadn't saved display item data for it. In that case,
   3935    // we need to take the containerItemASR since we might have fixed children.
   3936    // For WebRender, we want to the know what |containerItemASR| is for the
   3937    // case where the fixed-pos item is not a "real" fixed-pos item (e.g. it's
   3938    // nested inside a scrolling transform), so we stash that on the display
   3939    // item as well.
   3940    const ActiveScrolledRoot* fixedASR = ActiveScrolledRoot::PickAncestor(
   3941        containerItemASR, aBuilder->CurrentActiveScrolledRoot());
   3942    const ActiveScrolledRoot* scrollTargetASR =
   3943        containerItemASR ? containerItemASR->GetNearestScrollASR() : nullptr;
   3944    resultList.AppendNewToTop<nsDisplayFixedPosition>(
   3945        aBuilder, this, &resultList, fixedASR,
   3946        nsDisplayItem::ContainerASRType::AncestorOfContained, scrollTargetASR,
   3947        ShouldForceIsolation());
   3948    createdContainer = true;
   3949  } else if (useStickyPosition && !capturedByViewTransition) {
   3950    // For position:sticky, the clip needs to be applied both to the sticky
   3951    // container item and to the contents. The container item needs the clip
   3952    // because a scrolled clip needs to move independently from the sticky
   3953    // contents, and the contents need the clip so that they have finite
   3954    // clipped bounds with respect to the container item's ASR. The latter is
   3955    // a little tricky in the case where the sticky item has both fixed and
   3956    // non-fixed descendants, because that means that the sticky container
   3957    // item's ASR is the ASR of the fixed descendant.
   3958    // For WebRender display list building, though, we still want to know the
   3959    // the ASR that the sticky container item would normally have, so we stash
   3960    // that on the display item as the "container ASR" (i.e. the normal ASR of
   3961    // the container item, excluding the special behaviour induced by fixed
   3962    // descendants).
   3963    DisplayListClipState::AutoSaveRestore stickyItemClipState(aBuilder);
   3964    stickyItemClipState.MaybeRemoveDisplayportClip();
   3965    const ActiveScrolledRoot* stickyItemASR = ActiveScrolledRoot::PickAncestor(
   3966        containerItemASR, aBuilder->CurrentActiveScrolledRoot());
   3967 
   3968    auto* stickyItem = MakeDisplayItem<nsDisplayStickyPosition>(
   3969        aBuilder, this, &resultList, stickyItemASR,
   3970        nsDisplayItem::ContainerASRType::AncestorOfContained,
   3971        aBuilder->CurrentActiveScrolledRoot());
   3972 
   3973    stickyItem->SetShouldFlatten(shouldFlattenStickyItem);
   3974 
   3975    resultList.AppendToTop(stickyItem);
   3976    createdContainer = true;
   3977 
   3978    // If the sticky element is inside a filter, annotate the scroll frame that
   3979    // scrolls the filter as having out-of-flow content inside a filter (this
   3980    // inhibits paint skipping).
   3981    if (aBuilder->GetFilterASR() && aBuilder->GetFilterASR() == stickyItemASR) {
   3982      aBuilder->GetFilterASR()
   3983          ->GetNearestScrollASR()
   3984          ->ScrollFrame()
   3985          ->SetHasOutOfFlowContentInsideFilter();
   3986    }
   3987  }
   3988 
   3989  // If there's blending, wrap up the list in a blend-mode item. Note that
   3990  // opacity can be applied before blending as the blend color is not affected
   3991  // by foreground opacity (only background alpha).
   3992  if (effects->mMixBlendMode != StyleBlend::Normal) {
   3993    stackingContextTracker.AddToParent(
   3994        StackingContextBits::ContainsMixBlendMode);
   3995    resultList.AppendNewToTop<nsDisplayBlendMode>(
   3996        aBuilder, this, &resultList, effects->mMixBlendMode, containerItemASR,
   3997        nsDisplayItem::ContainerASRType::AncestorOfContained, false);
   3998    createdContainer = true;
   3999    MarkAsIsolated();
   4000  }
   4001 
   4002  if (!isolated && localIsolationReasons != StackingContextBits::None) {
   4003    resultList.AppendToTop(nsDisplayBlendContainer::CreateForIsolation(
   4004        aBuilder, this, &resultList, containerItemASR,
   4005        nsDisplayItem::ContainerASRType::AncestorOfContained,
   4006        ShouldForceIsolation()));
   4007    createdContainer = true;
   4008  }
   4009 
   4010  if (!isolated && aBuilder->MayContainNonIsolated3DTransform()) {
   4011    stackingContextTracker.AddToParent(
   4012        StackingContextBits::MayContainNonIsolated3DTransform);
   4013  }
   4014 
   4015  if (aBuilder->IsReusingStackingContextItems()) {
   4016    if (resultList.IsEmpty()) {
   4017      return;
   4018    }
   4019 
   4020    nsDisplayItem* container = resultList.GetBottom();
   4021    if (resultList.Length() > 1 || container->Frame() != this) {
   4022      container = MakeDisplayItem<nsDisplayContainer>(
   4023          aBuilder, this, containerItemASR,
   4024          nsDisplayItem::ContainerASRType::AncestorOfContained, &resultList);
   4025    } else {
   4026      MOZ_ASSERT(resultList.Length() == 1);
   4027      resultList.Clear();
   4028    }
   4029 
   4030    // Mark the outermost display item as reusable. These display items and
   4031    // their chidren can be reused during the next paint if no ancestor or
   4032    // descendant frames have been modified.
   4033    if (!container->IsReusedItem()) {
   4034      container->SetReusable();
   4035    }
   4036    aList->AppendToTop(container);
   4037    createdContainer = true;
   4038  } else {
   4039    aList->AppendToTop(&resultList);
   4040  }
   4041 
   4042  if (aCreatedContainerItem) {
   4043    *aCreatedContainerItem = createdContainer;
   4044  }
   4045 }
   4046 
   4047 static nsDisplayItem* WrapInWrapList(nsDisplayListBuilder* aBuilder,
   4048                                     nsIFrame* aFrame, nsDisplayList* aList,
   4049                                     const ActiveScrolledRoot* aContainerASR,
   4050                                     bool aBuiltContainerItem = false) {
   4051  nsDisplayItem* item = aList->GetBottom();
   4052  if (!item) {
   4053    return nullptr;
   4054  }
   4055 
   4056  // We need a wrap list if there are multiple items, or if the single
   4057  // item has a different frame. This can change in a partial build depending
   4058  // on which items we build, so we need to ensure that we don't transition
   4059  // to/from a wrap list without invalidating correctly.
   4060  bool needsWrapList =
   4061      aList->Length() > 1 || item->Frame() != aFrame || item->GetChildren();
   4062 
   4063  // If we have an explicit container item (that can't change without an
   4064  // invalidation) or we're doing a full build and don't need a wrap list, then
   4065  // we can skip adding one.
   4066  if (aBuiltContainerItem || (!aBuilder->IsPartialUpdate() && !needsWrapList)) {
   4067    MOZ_ASSERT(aList->Length() == 1);
   4068    aList->Clear();
   4069    return item;
   4070  }
   4071 
   4072  // If we're doing a partial build and we didn't need a wrap list
   4073  // previously then we can try to work from there.
   4074  if (aBuilder->IsPartialUpdate() &&
   4075      !aFrame->HasDisplayItem(uint32_t(DisplayItemType::TYPE_CONTAINER))) {
   4076    // If we now need a wrap list, we must previously have had no display items
   4077    // or a single one belonging to this frame. Mark the item itself as
   4078    // discarded so that RetainedDisplayListBuilder uses the ones we just built.
   4079    // We don't want to mark the frame as modified as that would invalidate
   4080    // positioned descendants that might be outside of this list, and might not
   4081    // have been rebuilt this time.
   4082    if (needsWrapList) {
   4083      DiscardOldItems(aFrame);
   4084    } else {
   4085      MOZ_ASSERT(aList->Length() == 1);
   4086      aList->Clear();
   4087      return item;
   4088    }
   4089  }
   4090 
   4091  // The last case we could try to handle is when we previously had a wrap list,
   4092  // but no longer need it. Unfortunately we can't differentiate this case from
   4093  // a partial build where other children exist but we just didn't build them
   4094  // this time.
   4095  // TODO:RetainedDisplayListBuilder's merge phase has the full list and
   4096  // could strip them out.
   4097 
   4098  return MakeDisplayItem<nsDisplayContainer>(
   4099      aBuilder, aFrame, aContainerASR,
   4100      nsDisplayItem::ContainerASRType::AncestorOfContained, aList);
   4101 }
   4102 
   4103 /**
   4104 * Check if a frame should be visited for building display list.
   4105 */
   4106 static bool DescendIntoChild(nsDisplayListBuilder* aBuilder,
   4107                             const nsIFrame* aChild, const nsRect& aVisible,
   4108                             const nsRect& aDirty) {
   4109  if (aChild->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
   4110    return true;
   4111  }
   4112 
   4113  // If the child is a scrollframe that we want to ignore, then we need
   4114  // to descend into it because its scrolled child may intersect the dirty
   4115  // area even if the scrollframe itself doesn't.
   4116  if (aChild == aBuilder->GetIgnoreScrollFrame()) {
   4117    return true;
   4118  }
   4119 
   4120  // There are cases where the "ignore scroll frame" on the builder is not set
   4121  // correctly, and so we additionally want to catch cases where the child is
   4122  // a root scrollframe and we are ignoring scrolling on the viewport.
   4123  if (aChild == aBuilder->GetPresShellIgnoreScrollFrame()) {
   4124    return true;
   4125  }
   4126 
   4127  nsRect overflow = aChild->InkOverflowRect();
   4128 
   4129  // On mobile, there may be a dynamic toolbar. The root content document's
   4130  // root scroll frame's ink overflow rect does not include the toolbar
   4131  // height, but if the toolbar is hidden, we still want to be able to target
   4132  // content underneath the toolbar, so expand the overflow rect here to
   4133  // allow display list building to descend into the scroll frame.
   4134  if (aBuilder->IsForEventDelivery() &&
   4135      aChild == aChild->PresShell()->GetRootScrollContainerFrame() &&
   4136      aChild->PresContext()->IsRootContentDocumentCrossProcess() &&
   4137      aChild->PresContext()->HasDynamicToolbar()) {
   4138    overflow.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
   4139        aChild->PresContext(), overflow.Size()));
   4140  }
   4141 
   4142  if (aDirty.Intersects(overflow)) {
   4143    return true;
   4144  }
   4145 
   4146  if (aChild->ForceDescendIntoIfVisible() && aVisible.Intersects(overflow)) {
   4147    return true;
   4148  }
   4149 
   4150  if (aChild->IsTablePart()) {
   4151    // Relative positioning and transforms can cause table parts to move, but we
   4152    // will still paint the backgrounds for their ancestor parts under them at
   4153    // their 'normal' position. That means that we must consider the overflow
   4154    // rects at both positions.
   4155 
   4156    // We convert the overflow rect into the nsTableFrame's coordinate
   4157    // space, applying the normal position offset at each step. Then we
   4158    // compare that against the builder's cached dirty rect in table
   4159    // coordinate space.
   4160    const nsIFrame* f = aChild;
   4161    nsRect normalPositionOverflowRelativeToTable = overflow;
   4162 
   4163    while (f->IsTablePart()) {
   4164      normalPositionOverflowRelativeToTable += f->GetNormalPosition();
   4165      f = f->GetParent();
   4166    }
   4167 
   4168    nsDisplayTableBackgroundSet* tableBGs = aBuilder->GetTableBackgroundSet();
   4169    if (tableBGs && tableBGs->GetDirtyRect().Intersects(
   4170                        normalPositionOverflowRelativeToTable)) {
   4171      return true;
   4172    }
   4173  }
   4174 
   4175  return false;
   4176 }
   4177 
   4178 void nsIFrame::BuildDisplayListForSimpleChild(nsDisplayListBuilder* aBuilder,
   4179                                              nsIFrame* aChild,
   4180                                              const nsDisplayListSet& aLists) {
   4181  // This is the shortcut for frames been handled along the common
   4182  // path, the most common one of THE COMMON CASE mentioned later.
   4183  MOZ_ASSERT(aChild->Type() != LayoutFrameType::Placeholder);
   4184  MOZ_ASSERT(!aBuilder->GetSelectedFramesOnly() &&
   4185                 !aBuilder->GetIncludeAllOutOfFlows(),
   4186             "It should be held for painting to window");
   4187  MOZ_ASSERT(aChild->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST));
   4188 
   4189  const nsPoint offset = aChild->GetOffsetTo(this);
   4190  const nsRect visible = aBuilder->GetVisibleRect() - offset;
   4191  const nsRect dirty = aBuilder->GetDirtyRect() - offset;
   4192 
   4193  if (!DescendIntoChild(aBuilder, aChild, visible, dirty)) {
   4194    DL_LOGV("Skipped frame %p", aChild);
   4195    return;
   4196  }
   4197 
   4198  // Child cannot be transformed since it is not a stacking context.
   4199  nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
   4200      aBuilder, aChild, visible, dirty, false);
   4201 
   4202  UpdateCurrentHitTestInfo(aBuilder, aChild);
   4203 
   4204  aChild->MarkAbsoluteFramesForDisplayList(aBuilder);
   4205  aBuilder->AdjustWindowDraggingRegion(aChild);
   4206  aBuilder->Check();
   4207  aChild->BuildDisplayList(aBuilder, aLists);
   4208  aChild->SetBuiltDisplayList(true);
   4209  aBuilder->Check();
   4210  aBuilder->DisplayCaret(aChild, aLists.Outlines());
   4211 }
   4212 
   4213 static bool ShouldSkipFrame(nsDisplayListBuilder* aBuilder,
   4214                            const nsIFrame* aFrame) {
   4215  // If painting is restricted to just the background of the top level frame,
   4216  // then we have nothing to do here.
   4217  if (aBuilder->IsBackgroundOnly()) {
   4218    return true;
   4219  }
   4220  if (aBuilder->IsForGenerateGlyphMask()) {
   4221    if ((aFrame->IsLeaf() && !aFrame->IsTextFrame()) ||
   4222        aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
   4223      // Only in-flow text frames are painted for background-clip: text mask
   4224      // generation.
   4225      return true;
   4226    }
   4227  }
   4228  if (aBuilder->GetSelectedFramesOnly() && aFrame->IsLeaf() &&
   4229      !aFrame->IsSelected()) {
   4230    return true;
   4231  }
   4232  static const nsFrameState skipFlags = NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
   4233                                        NS_FRAME_IS_NONDISPLAY |
   4234                                        NS_FRAME_POSITION_VISIBILITY_HIDDEN;
   4235  if (aFrame->HasAnyStateBits(skipFlags)) {
   4236    return true;
   4237  }
   4238  // Checking mMozSubtreeHiddenOnlyVisually is relatively slow because it
   4239  // involves loading more memory. It's only allowed in chrome sheets so let's
   4240  // only support it in the parent process so we can mostly optimize this out in
   4241  // content processes.
   4242  return XRE_IsParentProcess() &&
   4243         aFrame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually;
   4244 }
   4245 
   4246 void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
   4247                                        nsIFrame* aChild,
   4248                                        const nsDisplayListSet& aLists,
   4249                                        DisplayChildFlags aFlags) {
   4250  AutoCheckBuilder check(aBuilder);
   4251  MOZ_ASSERT(!HidesContent(), "Caller should check");
   4252 #ifdef DEBUG
   4253  DL_LOGV("BuildDisplayListForChild (%p) <", aChild);
   4254  ScopeExit e(
   4255      [aChild]() { DL_LOGV("> BuildDisplayListForChild (%p)", aChild); });
   4256 #endif
   4257 
   4258  nsIFrame* child = aChild;
   4259  auto* placeholder = child->IsPlaceholderFrame()
   4260                          ? static_cast<nsPlaceholderFrame*>(child)
   4261                          : nullptr;
   4262  nsIFrame* childOrOutOfFlow =
   4263      placeholder ? placeholder->GetOutOfFlowFrame() : child;
   4264  if (ShouldSkipFrame(aBuilder, childOrOutOfFlow)) {
   4265    return;
   4266  }
   4267 
   4268  // If we're generating a display list for printing, include Link items for
   4269  // frames that correspond to HTML link elements so that we can have active
   4270  // links in saved PDF output. Note that the state of "within a link" is
   4271  // set on the display-list builder, such that all descendants of the link
   4272  // element will generate display-list links.
   4273  // TODO: we should be able to optimize this so as to avoid creating links
   4274  // for the same destination that entirely overlap each other, which adds
   4275  // nothing useful to the final PDF.
   4276  Maybe<nsDisplayListBuilder::Linkifier> linkifier;
   4277  if (StaticPrefs::print_save_as_pdf_links_enabled() &&
   4278      aBuilder->IsForPrinting()) {
   4279    linkifier.emplace(aBuilder, childOrOutOfFlow, aLists.Content());
   4280    linkifier->MaybeAppendLink(aBuilder, childOrOutOfFlow);
   4281  }
   4282 
   4283  nsIFrame* parent = childOrOutOfFlow->GetParent();
   4284  const auto* parentDisplay = parent->StyleDisplay();
   4285  const auto overflowClipAxes =
   4286      parent->ShouldApplyOverflowClipping(parentDisplay);
   4287 
   4288  const bool isPaintingToWindow = aBuilder->IsPaintingToWindow();
   4289  const bool doingShortcut =
   4290      isPaintingToWindow &&
   4291      child->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST) &&
   4292      // Animations may change the stacking context state.
   4293      // ShouldApplyOverflowClipping is affected by the parent style, which does
   4294      // not invalidate the NS_FRAME_SIMPLE_DISPLAYLIST bit.
   4295      !(!overflowClipAxes.isEmpty() || child->MayHaveTransformAnimation() ||
   4296        child->MayHaveOpacityAnimation());
   4297 
   4298  if (aBuilder->IsForPainting()) {
   4299    aBuilder->ClearWillChangeBudgetStatus(child);
   4300  }
   4301 
   4302  if (StaticPrefs::layout_css_scroll_anchoring_highlight()) {
   4303    if (child->FirstContinuation()->IsScrollAnchor()) {
   4304      nsRect bounds = child->GetContentRectRelativeToSelf() +
   4305                      aBuilder->ToReferenceFrame(child);
   4306      nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
   4307          aBuilder, child, bounds, NS_RGBA(255, 0, 255, 64));
   4308      if (color) {
   4309        color->SetOverrideZIndex(INT32_MAX);
   4310        aLists.PositionedDescendants()->AppendToTop(color);
   4311      }
   4312    }
   4313  }
   4314 
   4315  if (doingShortcut) {
   4316    BuildDisplayListForSimpleChild(aBuilder, child, aLists);
   4317    return;
   4318  }
   4319 
   4320  // dirty rect in child-relative coordinates
   4321  NS_ASSERTION(aBuilder->GetCurrentFrame() == this, "Wrong coord space!");
   4322  const nsPoint offset = child->GetOffsetTo(this);
   4323  nsRect visible = aBuilder->GetVisibleRect() - offset;
   4324  nsRect dirty = aBuilder->GetDirtyRect() - offset;
   4325 
   4326  nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
   4327  if (placeholder) {
   4328    if (placeholder->HasAnyStateBits(PLACEHOLDER_FOR_TOPLAYER)) {
   4329      // If the out-of-flow frame is in the top layer, the viewport frame
   4330      // will paint it. Skip it here. Note that, only out-of-flow frames
   4331      // with this property should be skipped, because non-HTML elements
   4332      // may stop their children from being out-of-flow. Those frames
   4333      // should still be handled in the normal in-flow path.
   4334      return;
   4335    }
   4336 
   4337    child = childOrOutOfFlow;
   4338    if (aBuilder->IsForPainting()) {
   4339      aBuilder->ClearWillChangeBudgetStatus(child);
   4340    }
   4341 
   4342    // If 'child' is a pushed out-of-flow then it's owned by a block that's not
   4343    // an ancestor of the placeholder, and it will be painted by that block and
   4344    // should not be painted through the placeholder. Also recheck
   4345    // NS_FRAME_TOO_DEEP_IN_FRAME_TREE and NS_FRAME_IS_NONDISPLAY.
   4346    static const nsFrameState skipFlags =
   4347        (NS_FRAME_IS_PUSHED_OUT_OF_FLOW | NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
   4348         NS_FRAME_IS_NONDISPLAY);
   4349    if (child->HasAnyStateBits(skipFlags) || nsLayoutUtils::IsPopup(child)) {
   4350      return;
   4351    }
   4352 
   4353    MOZ_ASSERT(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
   4354    savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(child);
   4355 
   4356    if (aBuilder->GetIncludeAllOutOfFlows()) {
   4357      visible = child->InkOverflowRect();
   4358      dirty = child->InkOverflowRect();
   4359    } else if (savedOutOfFlowData) {
   4360      visible =
   4361          savedOutOfFlowData->GetVisibleRectForFrame(aBuilder, child, &dirty);
   4362    } else {
   4363      // The out-of-flow frame did not intersect the dirty area. We may still
   4364      // need to traverse into it, since it may contain placeholders we need
   4365      // to enter to reach other out-of-flow frames that are visible.
   4366      visible.SetEmpty();
   4367      dirty.SetEmpty();
   4368    }
   4369  }
   4370 
   4371  NS_ASSERTION(!child->IsPlaceholderFrame(),
   4372               "Should have dealt with placeholders already");
   4373 
   4374  if (!DescendIntoChild(aBuilder, child, visible, dirty)) {
   4375    DL_LOGV("Skipped frame %p", child);
   4376    return;
   4377  }
   4378 
   4379  const bool isSVG = child->HasAnyStateBits(NS_FRAME_SVG_LAYOUT);
   4380 
   4381  // This flag is raised if the control flow strays off the common path.
   4382  // The common path is the most common one of THE COMMON CASE mentioned later.
   4383  bool awayFromCommonPath = !isPaintingToWindow;
   4384 
   4385  // true if this is a real or pseudo stacking context
   4386  bool pseudoStackingContext =
   4387      aFlags.contains(DisplayChildFlag::ForcePseudoStackingContext);
   4388 
   4389  if (!pseudoStackingContext && !isSVG &&
   4390      aFlags.contains(DisplayChildFlag::Inline) &&
   4391      !child->IsLineParticipant()) {
   4392    // child is a non-inline frame in an inline context, i.e.,
   4393    // it acts like inline-block or inline-table. Therefore it is a
   4394    // pseudo-stacking-context.
   4395    pseudoStackingContext = true;
   4396  }
   4397 
   4398  // Since we're now sure that we're adding this frame to the display list
   4399  // (which means we're painting it, modulo occlusion), mark it as visible
   4400  // within the displayport.
   4401  if (isPaintingToWindow && child->TrackingVisibility() &&
   4402      child->IsVisibleForPainting()) {
   4403    child->PresShell()->EnsureFrameInApproximatelyVisibleList(child);
   4404    awayFromCommonPath = true;
   4405  }
   4406 
   4407  // Child is composited if it's transformed, partially transparent, or has
   4408  // SVG effects or a blend mode..
   4409  const nsStyleDisplay* disp = child->StyleDisplay();
   4410  const nsStyleEffects* effects = child->StyleEffects();
   4411 
   4412  const bool isPositioned = disp->IsPositionedStyle();
   4413  const bool isStackingContext =
   4414      aFlags.contains(DisplayChildFlag::ForceStackingContext) ||
   4415      child->IsStackingContext(disp, effects);
   4416 
   4417  if (pseudoStackingContext || isStackingContext || isPositioned ||
   4418      placeholder || (!isSVG && disp->IsFloating(child)) ||
   4419      (isSVG && effects->mClip.IsRect() && IsSVGContentWithCSSClip(child))) {
   4420    pseudoStackingContext = true;
   4421    awayFromCommonPath = true;
   4422  }
   4423 
   4424  NS_ASSERTION(!isStackingContext || pseudoStackingContext,
   4425               "Stacking contexts must also be pseudo-stacking-contexts");
   4426 
   4427  nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
   4428      aBuilder, child, visible, dirty);
   4429 
   4430  UpdateCurrentHitTestInfo(aBuilder, child);
   4431 
   4432  DisplayListClipState::AutoClipMultiple clipState(aBuilder);
   4433  nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
   4434 
   4435  if (savedOutOfFlowData) {
   4436    aBuilder->SetBuildingInvisibleItems(false);
   4437 
   4438    nsIFrame* scrollsWithAnchor = nullptr;
   4439    if (aBuilder->IsPaintingToWindow() &&
   4440        // If we are in view transition capture we get a null asr no matter
   4441        // what, so don't bother checking for async scrolling with a CSS anchor
   4442        // pos anchor.
   4443        !aBuilder->IsInViewTransitionCapture() &&
   4444        child->IsAbsolutelyPositioned(disp) &&
   4445        // If there is an active view transition in this document it is tricky
   4446        // to determine what will be an active scroll frame outside of that
   4447        // frame's BuildDisplayList, so don't bother to async scroll with an
   4448        // anchor in that case. Bug 2001861 tracks removing this check.
   4449        !PresContext()->Document()->GetActiveViewTransition()) {
   4450      scrollsWithAnchor = AnchorPositioningUtils::GetAnchorThatFrameScrollsWith(
   4451          child, aBuilder);
   4452 
   4453      if (scrollsWithAnchor && aBuilder->IsRetainingDisplayList()) {
   4454        if (aBuilder->IsPartialUpdate()) {
   4455          aBuilder->SetPartialBuildFailed(true);
   4456        } else {
   4457          aBuilder->SetDisablePartialUpdates(true);
   4458        }
   4459      }
   4460    }
   4461 
   4462    const ActiveScrolledRoot* asr =
   4463        savedOutOfFlowData->mContainingBlockActiveScrolledRoot;
   4464 
   4465 #ifdef DEBUG
   4466    if (aBuilder->IsPaintingToWindow()) {
   4467      // Assert that the asr is as expected.
   4468      if (savedOutOfFlowData->mContainingBlockInViewTransitionCapture) {
   4469        MOZ_ASSERT(asr == nullptr);
   4470        MOZ_ASSERT(aBuilder->IsInViewTransitionCapture());
   4471      } else if ((asr ? FrameAndASRKind{asr->mFrame, asr->mKind}
   4472                      : FrameAndASRKind::default_value()) !=
   4473                 DisplayPortUtils::GetASRAncestorFrame(
   4474                     {child->GetParent(), ActiveScrolledRoot::ASRKind::Scroll},
   4475                     aBuilder)) {
   4476        // A weird case for native anonymous content in the custom content
   4477        // container when the root is captured by a view transition. This
   4478        // content is built outside of the view transition capture but the
   4479        // containing block (the canvas frame) was built inside the capture, so
   4480        // savedOutOfFlowData is saved as if we are inside the capture while we
   4481        // are outside it (bug 2002160).
   4482        MOZ_ASSERT(asr == nullptr);
   4483        MOZ_ASSERT(PresContext()->Document()->GetActiveViewTransition());
   4484        MOZ_ASSERT(
   4485            child->GetParent()->GetContent()->IsInNativeAnonymousSubtree());
   4486        bool inTopLayer = false;
   4487        nsIFrame* curr = child->GetParent();
   4488        while (curr) {
   4489          if (curr->StyleDisplay()->mTopLayer == StyleTopLayer::Auto) {
   4490            inTopLayer = true;
   4491            break;
   4492          }
   4493          curr = curr->GetParent();
   4494        }
   4495        MOZ_ASSERT(inTopLayer);
   4496      }
   4497    }
   4498 #endif
   4499 
   4500    if (scrollsWithAnchor) {
   4501      asr = DisplayPortUtils::ActivateDisplayportOnASRAncestors(
   4502          scrollsWithAnchor, child->GetParent(), asr, aBuilder);
   4503 
   4504      // TODO should we set the scroll parent id too?
   4505      // https://github.com/w3c/csswg-drafts/issues/12042
   4506    }
   4507 
   4508    if (aBuilder->IsInViewTransitionCapture()) {
   4509      if (!savedOutOfFlowData->mContainingBlockInViewTransitionCapture) {
   4510        clipState.Clear();
   4511      } else {
   4512        clipState.SetClipChainForContainingBlockDescendants(
   4513            savedOutOfFlowData->mContainingBlockClipChain);
   4514      }
   4515      asrSetter.SetCurrentActiveScrolledRoot(nullptr);
   4516    } else {
   4517      clipState.SetClipChainForContainingBlockDescendants(
   4518          savedOutOfFlowData->mContainingBlockClipChain);
   4519      asrSetter.SetCurrentActiveScrolledRoot(asr);
   4520      asrSetter.SetCurrentScrollParentId(savedOutOfFlowData->mScrollParentId);
   4521    }
   4522    MOZ_ASSERT(awayFromCommonPath,
   4523               "It is impossible when savedOutOfFlowData is true");
   4524  } else if (HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
   4525             placeholder) {
   4526    NS_ASSERTION(visible.IsEmpty(), "should have empty visible rect");
   4527    // Every item we build from now until we descent into an out of flow that
   4528    // does have saved out of flow data should be invisible. This state gets
   4529    // restored when AutoBuildingDisplayList gets out of scope.
   4530    aBuilder->SetBuildingInvisibleItems(true);
   4531 
   4532    // If we have nested out-of-flow frames and the outer one isn't visible
   4533    // then we won't have stored clip data for it. We can just clear the clip
   4534    // instead since we know we won't render anything, and the inner out-of-flow
   4535    // frame will setup the correct clip for itself.
   4536    clipState.SetClipChainForContainingBlockDescendants(nullptr);
   4537  }
   4538 
   4539  // Setup clipping for the parent's overflow:clip,
   4540  // or overflow:hidden on elements that don't support scrolling (and therefore
   4541  // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
   4542  // anything directly rendered by the parent, only the rendering of its
   4543  // children.
   4544  // Don't use overflowClip to restrict the dirty rect, since some of the
   4545  // descendants may not be clipped by it. Even if we end up with unnecessary
   4546  // display items, they'll be pruned during ComputeVisibility.
   4547  //
   4548  // FIXME(emilio): Why can't we handle this more similarly to `clip` (on the
   4549  // parent, rather than on the children)? Would ClipContentDescendants do what
   4550  // we want?
   4551  if (!overflowClipAxes.isEmpty()) {
   4552    ApplyOverflowClipping(aBuilder, parent, overflowClipAxes, clipState);
   4553    awayFromCommonPath = true;
   4554  }
   4555 
   4556  nsDisplayList list(aBuilder);
   4557  nsDisplayList extraPositionedDescendants(aBuilder);
   4558  const ActiveScrolledRoot* wrapListASR;
   4559  bool builtContainerItem = false;
   4560  if (isStackingContext) {
   4561    // True stacking context.
   4562    // For stacking contexts, BuildDisplayListForStackingContext handles
   4563    // clipping and MarkAbsoluteFramesForDisplayList.
   4564    nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
   4565    child->BuildDisplayListForStackingContext(aBuilder, &list,
   4566                                              &builtContainerItem);
   4567    wrapListASR = contASRTracker.GetContainerASR();
   4568    if (!aBuilder->IsReusingStackingContextItems() &&
   4569        aBuilder->GetCaretFrame() == child) {
   4570      builtContainerItem = false;
   4571    }
   4572  } else {
   4573    Maybe<nsRect> clipPropClip =
   4574        child->GetClipPropClipRect(disp, effects, child->GetSize());
   4575    if (clipPropClip) {
   4576      aBuilder->IntersectVisibleRect(*clipPropClip);
   4577      aBuilder->IntersectDirtyRect(*clipPropClip);
   4578      clipState.ClipContentDescendants(*clipPropClip +
   4579                                       aBuilder->ToReferenceFrame(child));
   4580      awayFromCommonPath = true;
   4581    }
   4582 
   4583    child->MarkAbsoluteFramesForDisplayList(aBuilder);
   4584    child->SetBuiltDisplayList(true);
   4585 
   4586    // Some SVG frames might change opacity without invalidating the frame, so
   4587    // exclude them from the fast-path.
   4588    if (!awayFromCommonPath && !child->IsSVGFrame()) {
   4589      // The shortcut is available for the child for next time.
   4590      child->AddStateBits(NS_FRAME_SIMPLE_DISPLAYLIST);
   4591    }
   4592 
   4593    if (!pseudoStackingContext) {
   4594      // THIS IS THE COMMON CASE.
   4595      // Not a pseudo or real stacking context. Do the simple thing and
   4596      // return early.
   4597      aBuilder->AdjustWindowDraggingRegion(child);
   4598      aBuilder->Check();
   4599      child->BuildDisplayList(aBuilder, aLists);
   4600      aBuilder->Check();
   4601      aBuilder->DisplayCaret(child, aLists.Outlines());
   4602      return;
   4603    }
   4604 
   4605    // A pseudo-stacking context (e.g., a positioned element with z-index auto).
   4606    // We allow positioned descendants of the child to escape to our parent
   4607    // stacking context's positioned descendant list, because they might be
   4608    // z-index:non-auto
   4609    nsDisplayListCollection pseudoStack(aBuilder);
   4610 
   4611    aBuilder->AdjustWindowDraggingRegion(child);
   4612    nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
   4613    aBuilder->Check();
   4614    child->BuildDisplayList(aBuilder, pseudoStack);
   4615    aBuilder->Check();
   4616    if (aBuilder->DisplayCaret(child, pseudoStack.Outlines())) {
   4617      builtContainerItem = false;
   4618    }
   4619    wrapListASR = contASRTracker.GetContainerASR();
   4620 
   4621    list.AppendToTop(pseudoStack.BorderBackground());
   4622    list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
   4623    list.AppendToTop(pseudoStack.Floats());
   4624    list.AppendToTop(pseudoStack.Content());
   4625    list.AppendToTop(pseudoStack.Outlines());
   4626    extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
   4627  }
   4628 
   4629  buildingForChild.RestoreBuildingInvisibleItemsValue();
   4630 
   4631  if (!list.IsEmpty()) {
   4632    if (isPositioned || isStackingContext) {
   4633      // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
   4634      // go in this level.
   4635      nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR,
   4636                                           builtContainerItem);
   4637      if (isSVG) {
   4638        aLists.Content()->AppendToTop(item);
   4639      } else {
   4640        aLists.PositionedDescendants()->AppendToTop(item);
   4641      }
   4642    } else if (!isSVG && disp->IsFloating(child)) {
   4643      aLists.Floats()->AppendToTop(
   4644          WrapInWrapList(aBuilder, child, &list, wrapListASR));
   4645    } else {
   4646      aLists.Content()->AppendToTop(&list);
   4647    }
   4648  }
   4649  // We delay placing the positioned descendants of positioned frames to here,
   4650  // because in the absence of z-index this is the correct order for them.
   4651  // This doesn't affect correctness because the positioned descendants list
   4652  // is sorted by z-order and content in BuildDisplayListForStackingContext,
   4653  // but it means that sort routine needs to do less work.
   4654  aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
   4655 }
   4656 
   4657 void nsIFrame::MarkAbsoluteFramesForDisplayList(
   4658    nsDisplayListBuilder* aBuilder) {
   4659  if (const auto* absCB = GetAbsoluteContainingBlock()) {
   4660    aBuilder->MarkFramesForDisplayList(this, absCB->GetChildList());
   4661  }
   4662 }
   4663 
   4664 nsIContent* nsIFrame::GetContentForEvent(const WidgetEvent* aEvent) const {
   4665  if (!IsGeneratedContentFrame()) {
   4666    return GetContent();
   4667  }
   4668  const nsIFrame* generatedRoot = this;
   4669  while (true) {
   4670    auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(generatedRoot);
   4671    if (!parent || !parent->IsGeneratedContentFrame()) {
   4672      break;
   4673    }
   4674    generatedRoot = parent;
   4675  }
   4676  // Return the non-generated ancestor.
   4677  return generatedRoot->GetContent()->GetParent();
   4678 }
   4679 
   4680 void nsIFrame::FireDOMEvent(const nsAString& aDOMEventName,
   4681                            nsIContent* aContent) {
   4682  nsIContent* target = aContent ? aContent : GetContent();
   4683 
   4684  if (target) {
   4685    RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
   4686        target, aDOMEventName, CanBubble::eYes, ChromeOnlyDispatch::eNo);
   4687    DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
   4688    NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch");
   4689  }
   4690 }
   4691 
   4692 nsresult nsIFrame::HandleEvent(nsPresContext* aPresContext,
   4693                               WidgetGUIEvent* aEvent,
   4694                               nsEventStatus* aEventStatus) {
   4695  if (aEvent->mMessage == eMouseMove) {
   4696    // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
   4697    //     the implementation becomes simpler.
   4698    return HandleDrag(aPresContext, aEvent, aEventStatus);
   4699  }
   4700 
   4701  if ((aEvent->mClass == eMouseEventClass &&
   4702       aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) ||
   4703      aEvent->mClass == eTouchEventClass) {
   4704    if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eTouchStart) {
   4705      HandlePress(aPresContext, aEvent, aEventStatus);
   4706    } else if (aEvent->mMessage == eMouseUp || aEvent->mMessage == eTouchEnd) {
   4707      HandleRelease(aPresContext, aEvent, aEventStatus);
   4708    }
   4709    return NS_OK;
   4710  }
   4711 
   4712  // When secondary buttion is down, we need to move selection to make users
   4713  // possible to paste something at click point quickly.
   4714  // When middle button is down, we need to just move selection and focus at
   4715  // the clicked point.  Note that even if middle click paste is not enabled,
   4716  // Chrome moves selection at middle mouse button down.  So, we should follow
   4717  // the behavior for the compatibility.
   4718  if (aEvent->mMessage == eMouseDown) {
   4719    WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   4720    if (mouseEvent && (mouseEvent->mButton == MouseButton::eSecondary ||
   4721                       mouseEvent->mButton == MouseButton::eMiddle)) {
   4722      if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
   4723        return NS_OK;
   4724      }
   4725      return MoveCaretToEventPoint(aPresContext, mouseEvent, aEventStatus);
   4726    }
   4727  }
   4728 
   4729  return NS_OK;
   4730 }
   4731 
   4732 nsresult nsIFrame::GetDataForTableSelection(
   4733    const nsFrameSelection* aFrameSelection, mozilla::PresShell* aPresShell,
   4734    WidgetMouseEvent* aMouseEvent, nsIContent** aParentContent,
   4735    int32_t* aContentOffset, TableSelectionMode* aTarget) {
   4736  if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent ||
   4737      !aContentOffset || !aTarget) {
   4738    return NS_ERROR_NULL_POINTER;
   4739  }
   4740 
   4741  *aParentContent = nullptr;
   4742  *aContentOffset = 0;
   4743  *aTarget = TableSelectionMode::None;
   4744 
   4745  int16_t displaySelection = aPresShell->GetSelectionFlags();
   4746 
   4747  bool selectingTableCells = aFrameSelection->IsInTableSelectionMode();
   4748 
   4749  // DISPLAY_ALL means we're in an editor.
   4750  // If already in cell selection mode,
   4751  //  continue selecting with mouse drag or end on mouse up,
   4752  //  or when using shift key to extend block of cells
   4753  //  (Mouse down does normal selection unless Ctrl/Cmd is pressed)
   4754  bool doTableSelection =
   4755      displaySelection == nsISelectionDisplay::DISPLAY_ALL &&
   4756      selectingTableCells &&
   4757      (aMouseEvent->mMessage == eMouseMove ||
   4758       (aMouseEvent->mMessage == eMouseUp &&
   4759        aMouseEvent->mButton == MouseButton::ePrimary) ||
   4760       aMouseEvent->IsShift());
   4761 
   4762  if (!doTableSelection) {
   4763    // In Browser, special 'table selection' key must be pressed for table
   4764    // selection or when just Shift is pressed and we're already in table/cell
   4765    // selection mode
   4766 #ifdef XP_MACOSX
   4767    doTableSelection = aMouseEvent->IsMeta() ||
   4768                       (aMouseEvent->IsShift() && selectingTableCells);
   4769 #else
   4770    doTableSelection = aMouseEvent->IsControl() ||
   4771                       (aMouseEvent->IsShift() && selectingTableCells);
   4772 #endif
   4773  }
   4774  if (!doTableSelection) {
   4775    return NS_OK;
   4776  }
   4777 
   4778  // Get the cell frame or table frame (or parent) of the current content node
   4779  nsIFrame* frame = this;
   4780  bool foundCell = false;
   4781  bool foundTable = false;
   4782 
   4783  // Get the limiting node to stop parent frame search
   4784  const Element* const independentSelectionLimiter =
   4785      aFrameSelection->GetIndependentSelectionRootElement();
   4786 
   4787  // If our content node is an ancestor of the limiting node,
   4788  // we should stop the search right now.
   4789  if (independentSelectionLimiter &&
   4790      independentSelectionLimiter->IsInclusiveDescendantOf(GetContent())) {
   4791    return NS_OK;
   4792  }
   4793 
   4794  // We don't initiate row/col selection from here now,
   4795  //  but we may in future
   4796  // bool selectColumn = false;
   4797  // bool selectRow = false;
   4798 
   4799  while (frame) {
   4800    // Check for a table cell by querying to a known CellFrame interface
   4801    nsITableCellLayout* cellElement = do_QueryFrame(frame);
   4802    if (cellElement) {
   4803      foundCell = true;
   4804      // TODO: If we want to use proximity to top or left border
   4805      //      for row and column selection, this is the place to do it
   4806      break;
   4807    } else {
   4808      // If not a cell, check for table
   4809      // This will happen when starting frame is the table or child of a table,
   4810      //  such as a row (we were inbetween cells or in table border)
   4811      nsTableWrapperFrame* tableFrame = do_QueryFrame(frame);
   4812      if (tableFrame) {
   4813        foundTable = true;
   4814        // TODO: How can we select row when along left table edge
   4815        //  or select column when along top edge?
   4816        break;
   4817      } else {
   4818        frame = frame->GetParent();
   4819        // Stop if we have hit the selection's limiting content node
   4820        if (frame && frame->GetContent() == independentSelectionLimiter) {
   4821          break;
   4822        }
   4823      }
   4824    }
   4825  }
   4826  // We aren't in a cell or table
   4827  if (!foundCell && !foundTable) {
   4828    return NS_OK;
   4829  }
   4830 
   4831  nsIContent* tableOrCellContent = frame->GetContent();
   4832  if (!tableOrCellContent) {
   4833    return NS_ERROR_FAILURE;
   4834  }
   4835 
   4836  nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent();
   4837  if (!parentContent) {
   4838    return NS_ERROR_FAILURE;
   4839  }
   4840 
   4841  const int32_t offset =
   4842      parentContent->ComputeIndexOf_Deprecated(tableOrCellContent);
   4843  // Not likely?
   4844  if (offset < 0) {
   4845    return NS_ERROR_FAILURE;
   4846  }
   4847 
   4848  // Everything is OK -- set the return values
   4849  parentContent.forget(aParentContent);
   4850 
   4851  *aContentOffset = offset;
   4852 
   4853 #if 0
   4854  if (selectRow)
   4855    *aTarget = TableSelectionMode::Row;
   4856  else if (selectColumn)
   4857    *aTarget = TableSelectionMode::Column;
   4858  else
   4859 #endif
   4860  if (foundCell) {
   4861    *aTarget = TableSelectionMode::Cell;
   4862  } else if (foundTable) {
   4863    *aTarget = TableSelectionMode::Table;
   4864  }
   4865 
   4866  return NS_OK;
   4867 }
   4868 
   4869 static bool IsEditingHost(const nsIFrame* aFrame) {
   4870  if (aFrame->Style()->GetPseudoType() ==
   4871      PseudoStyleType::mozTextControlEditingRoot) {
   4872    return true;
   4873  }
   4874  nsIContent* content = aFrame->GetContent();
   4875  return content && content->IsEditingHost();
   4876 }
   4877 
   4878 static StyleUserSelect UsedUserSelect(const nsIFrame* aFrame) {
   4879  if (aFrame->IsGeneratedContentFrame()) {
   4880    return StyleUserSelect::None;
   4881  }
   4882 
   4883  // Per https://drafts.csswg.org/css-ui-4/#content-selection:
   4884  //
   4885  // The used value is the same as the computed value, except:
   4886  //
   4887  //    1 - on editable elements where the used value is always 'contain'
   4888  //        regardless of the computed value
   4889  //    2 - when the computed value is auto, in which case the used value is one
   4890  //        of the other values...
   4891  //
   4892  // See https://github.com/w3c/csswg-drafts/issues/3344 to see why we do this
   4893  // at used-value time instead of at computed-value time.
   4894 
   4895  // Although the draft does not define that editable elements under an editing
   4896  // host should ignore `user-select`, Chrome ignores `user-select:none` and
   4897  // `user-select:all` and we've already considered as ignoring the
   4898  // `user-select` according to this test:
   4899  // https://searchfox.org/firefox-main/rev/6abddcb0a5076c3b888686ede6f4cf7d082460d3/dom/base/test/test_bug1101364.html#73-85
   4900  // https://searchfox.org/firefox-main/rev/4fd0fa7e5814c0b51f1dd075821988377bc56cc1/testing/web-platform/tests/css/css-ui/user-select-none-in-editable.html#23-24,32-36
   4901  // Therefore, we check aFrame->ContentIsEditable() instead of
   4902  // IsEditingHost(aFrame).
   4903  if (aFrame->IsTextInputFrame() || aFrame->ContentIsEditable()) {
   4904    // We don't implement 'contain' itself, but we make 'text' behave as
   4905    // 'contain' for contenteditable and <input> / <textarea> elements anyway so
   4906    // this is ok.
   4907    return StyleUserSelect::Text;
   4908  }
   4909 
   4910  auto style = aFrame->Style()->UserSelect();
   4911  if (style != StyleUserSelect::Auto) {
   4912    return style;
   4913  }
   4914 
   4915  auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
   4916  return parent ? UsedUserSelect(parent) : StyleUserSelect::Text;
   4917 }
   4918 
   4919 bool nsIFrame::IsSelectable(StyleUserSelect* aSelectStyle) const {
   4920  auto style = UsedUserSelect(this);
   4921  if (aSelectStyle) {
   4922    *aSelectStyle = style;
   4923  }
   4924  return style != StyleUserSelect::None;
   4925 }
   4926 
   4927 bool nsIFrame::ShouldPaintNormalSelection() const {
   4928  if (IsSelectable()) {
   4929    // NOTE: Ideally, we should return false if display selection is "OFF".
   4930    // However, here is a hot path at painting.  Therefore, it should be checked
   4931    // before and we shouldn't need to check it.
   4932    return true;
   4933  }
   4934  // If we're not selectable by user, we should paint selection only while the
   4935  // normal selection is styled as "attention" by "Find in Page" or something.
   4936  return GetDisplaySelection() == nsISelectionController::SELECTION_ATTENTION;
   4937 }
   4938 
   4939 bool nsIFrame::ShouldHaveLineIfEmpty() const {
   4940  switch (Style()->GetPseudoType()) {
   4941    case PseudoStyleType::NotPseudo:
   4942      break;
   4943    case PseudoStyleType::scrolledContent:
   4944      return GetParent()->ShouldHaveLineIfEmpty();
   4945    default:
   4946      return false;
   4947  }
   4948  return IsInputButtonControlFrame() || IsEditingHost(this);
   4949 }
   4950 
   4951 /**
   4952 * Handles the Mouse Press Event for the frame
   4953 */
   4954 NS_IMETHODIMP
   4955 nsIFrame::HandlePress(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
   4956                      nsEventStatus* aEventStatus) {
   4957  NS_ENSURE_ARG_POINTER(aEventStatus);
   4958  if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
   4959    return NS_OK;
   4960  }
   4961 
   4962  NS_ENSURE_ARG_POINTER(aEvent);
   4963  if (aEvent->mClass == eTouchEventClass) {
   4964    return NS_OK;
   4965  }
   4966 
   4967  return MoveCaretToEventPoint(aPresContext, aEvent->AsMouseEvent(),
   4968                               aEventStatus);
   4969 }
   4970 
   4971 nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
   4972                                         WidgetMouseEvent* aMouseEvent,
   4973                                         nsEventStatus* aEventStatus) {
   4974  MOZ_ASSERT(aPresContext);
   4975  MOZ_ASSERT(aMouseEvent);
   4976  MOZ_ASSERT(aMouseEvent->mMessage == eMouseDown);
   4977  MOZ_ASSERT(aEventStatus);
   4978  MOZ_ASSERT(nsEventStatus_eConsumeNoDefault != *aEventStatus);
   4979 
   4980  mozilla::PresShell* presShell = aPresContext->GetPresShell();
   4981  if (!presShell) {
   4982    return NS_ERROR_FAILURE;
   4983  }
   4984 
   4985  // We often get out of sync state issues with mousedown events that
   4986  // get interrupted by alerts/dialogs.
   4987  // Check with the ESM to see if we should process this one
   4988  if (!aPresContext->EventStateManager()->EventStatusOK(aMouseEvent)) {
   4989    return NS_OK;
   4990  }
   4991 
   4992  EventStateManager* const esm = aPresContext->EventStateManager();
   4993  if (nsIContent* dragGestureContent = esm->GetTrackingDragGestureContent()) {
   4994    if (dragGestureContent != this->GetContent()) {
   4995      // When the current tracked dragging gesture is different
   4996      // than this frame, it means this frame was being dragged, however
   4997      // it got moved/destroyed. So we should consider the drag is
   4998      // still happening, so return early here.
   4999      return NS_OK;
   5000    }
   5001  }
   5002 
   5003  const nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
   5004      aMouseEvent, RelativeTo{this});
   5005 
   5006  // When not using `alt`, and clicking on a draggable, but non-editable
   5007  // element, don't do anything, and let d&d handle the event.
   5008  //
   5009  // See bug 48876, bug 388659 and bug 55921 for context here.
   5010  //
   5011  // FIXME(emilio): The .Contains(pt) check looks a bit fishy. When would it be
   5012  // false given we're the event target? If it is needed, why not checking the
   5013  // actual draggable node rect instead?
   5014  if (!aMouseEvent->IsAlt() && GetRectRelativeToSelf().Contains(pt)) {
   5015    for (nsIContent* content = mContent; content;
   5016         content = content->GetFlattenedTreeParent()) {
   5017      if (nsContentUtils::ContentIsDraggable(content) &&
   5018          !content->IsEditable()) {
   5019        return NS_OK;
   5020      }
   5021    }
   5022  }
   5023 
   5024  // If we are in Navigator and the click is in a draggable node, we don't want
   5025  // to start selection because we don't want to interfere with a potential
   5026  // drag of said node and steal all its glory.
   5027  const bool isEditor =
   5028      presShell->GetSelectionFlags() == nsISelectionDisplay::DISPLAY_ALL;
   5029 
   5030  // Don't do something if it's middle button down event.
   5031  const bool isPrimaryButtonDown =
   5032      aMouseEvent->mButton == MouseButton::ePrimary;
   5033 
   5034  // Check whether this frame should handle selection events. If not, don't
   5035  // tell selection the mouse event even occurred.
   5036  if (!ShouldHandleSelectionMovementEvents()) {
   5037    return NS_OK;
   5038  }
   5039 
   5040  if (isPrimaryButtonDown) {
   5041    // If the mouse is dragged outside the nearest enclosing scrollable area
   5042    // while making a selection, the area will be scrolled. To do this, capture
   5043    // the mouse on the nearest scroll container frame. If there isn't a scroll
   5044    // container frame, or something else is already capturing the mouse,
   5045    // there's no reason to capture.
   5046    if (!PresShell::GetCapturingContent()) {
   5047      ScrollContainerFrame* scrollContainerFrame =
   5048          nsLayoutUtils::GetNearestScrollContainerFrame(
   5049              this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
   5050                        nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
   5051      if (scrollContainerFrame) {
   5052        nsIFrame* capturingFrame = scrollContainerFrame;
   5053        PresShell::SetCapturingContent(capturingFrame->GetContent(),
   5054                                       CaptureFlags::IgnoreAllowedState);
   5055      }
   5056    }
   5057  }
   5058 
   5059  // XXX This is screwy; it really should use the selection frame, not the
   5060  // event frame
   5061  const nsFrameSelection* frameselection = GetConstFrameSelection();
   5062  if (!frameselection || frameselection->GetDisplaySelection() ==
   5063                             nsISelectionController::SELECTION_OFF) {
   5064    return NS_OK;  // nothing to do we cannot affect selection from here
   5065  }
   5066 
   5067 #ifdef XP_MACOSX
   5068  // If Control key is pressed on macOS, it should be treated as right click.
   5069  // So, don't change selection.
   5070  if (aMouseEvent->IsControl()) {
   5071    return NS_OK;
   5072  }
   5073  const bool control = aMouseEvent->IsMeta();
   5074 #else
   5075  const bool control = aMouseEvent->IsControl();
   5076 #endif
   5077 
   5078  RefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
   5079  if (isPrimaryButtonDown && aMouseEvent->mClickCount > 1) {
   5080    // These methods aren't const but can't actually delete anything,
   5081    // so no need for AutoWeakFrame.
   5082    fc->SetDragState(true);
   5083    return HandleMultiplePress(aPresContext, aMouseEvent, aEventStatus,
   5084                               control);
   5085  }
   5086 
   5087  ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
   5088 
   5089  if (!offsets.content) {
   5090    return NS_ERROR_FAILURE;
   5091  }
   5092 
   5093  const bool isSecondaryButton =
   5094      aMouseEvent->mButton == MouseButton::eSecondary;
   5095  if (isSecondaryButton &&
   5096      !MovingCaretToEventPointAllowedIfSecondaryButtonEvent(
   5097          *frameselection, *aMouseEvent, *offsets.content,
   5098          // When we collapse selection in nsFrameSelection::TakeFocus,
   5099          // we always collapse selection to the start offset.  Therefore,
   5100          // we can ignore the end offset here.  E.g., when an <img> is clicked,
   5101          // set the primary offset to after it, but the the secondary offset
   5102          // may be before it, see OffsetsForSingleFrame for the detail.
   5103          offsets.StartOffset())) {
   5104    return NS_OK;
   5105  }
   5106 
   5107  if (aMouseEvent->mMessage == eMouseDown &&
   5108      aMouseEvent->mButton == MouseButton::eMiddle &&
   5109      !offsets.content->IsEditable()) {
   5110    // However, some users don't like the Chrome compatible behavior of
   5111    // middle mouse click.  They want to keep selection after starting
   5112    // autoscroll.  However, the selection change is important for middle
   5113    // mouse past.  Therefore, we should allow users to take the traditional
   5114    // behavior back by themselves unless middle click paste is enabled or
   5115    // autoscrolling is disabled.
   5116    if (!Preferences::GetBool("middlemouse.paste", false) &&
   5117        Preferences::GetBool("general.autoScroll", false) &&
   5118        Preferences::GetBool("general.autoscroll.prevent_to_collapse_selection_"
   5119                             "by_middle_mouse_down",
   5120                             false)) {
   5121      return NS_OK;
   5122    }
   5123  }
   5124 
   5125  if (isPrimaryButtonDown) {
   5126    // Let Ctrl/Cmd + left mouse down do table selection instead of drag
   5127    // initiation.
   5128    nsCOMPtr<nsIContent> parentContent;
   5129    int32_t contentOffset;
   5130    TableSelectionMode target;
   5131    nsresult rv = GetDataForTableSelection(
   5132        frameselection, presShell, aMouseEvent, getter_AddRefs(parentContent),
   5133        &contentOffset, &target);
   5134    if (NS_SUCCEEDED(rv) && parentContent) {
   5135      fc->SetDragState(true);
   5136      return fc->HandleTableSelection(parentContent, contentOffset, target,
   5137                                      aMouseEvent);
   5138    }
   5139  }
   5140 
   5141  fc->SetDelayedCaretData(0);
   5142 
   5143  if (isPrimaryButtonDown) {
   5144    // Check if any part of this frame is selected, and if the user clicked
   5145    // inside the selected region, and if it's the left button. If so, we delay
   5146    // starting a new selection since the user may be trying to drag the
   5147    // selected region to some other app.
   5148 
   5149    if (GetContent() && GetContent()->IsMaybeSelected()) {
   5150      bool inSelection = false;
   5151      UniquePtr<SelectionDetails> details = frameselection->LookUpSelection(
   5152          offsets.content, 0, offsets.EndOffset(),
   5153          nsFrameSelection::IgnoreNormalSelection::No);
   5154 
   5155      //
   5156      // If there are any details, check to see if the user clicked
   5157      // within any selected region of the frame.
   5158      //
   5159 
   5160      for (SelectionDetails* curDetail = details.get(); curDetail;
   5161           curDetail = curDetail->mNext.get()) {
   5162        //
   5163        // If the user clicked inside a selection, then just
   5164        // return without doing anything. We will handle placing
   5165        // the caret later on when the mouse is released. We ignore
   5166        // the spellcheck, find and url formatting selections.
   5167        //
   5168        if (curDetail->mSelectionType != SelectionType::eSpellCheck &&
   5169            curDetail->mSelectionType != SelectionType::eFind &&
   5170            curDetail->mSelectionType != SelectionType::eURLSecondary &&
   5171            curDetail->mSelectionType != SelectionType::eURLStrikeout &&
   5172            curDetail->mSelectionType != SelectionType::eHighlight &&
   5173            curDetail->mSelectionType != SelectionType::eTargetText &&
   5174            curDetail->mStart <= offsets.StartOffset() &&
   5175            offsets.EndOffset() <= curDetail->mEnd) {
   5176          inSelection = true;
   5177        }
   5178      }
   5179 
   5180      if (inSelection) {
   5181        fc->SetDragState(false);
   5182        fc->SetDelayedCaretData(aMouseEvent);
   5183        return NS_OK;
   5184      }
   5185    }
   5186 
   5187    fc->SetDragState(true);
   5188  }
   5189 
   5190  // Do not touch any nsFrame members after this point without adding
   5191  // weakFrame checks.
   5192  const nsFrameSelection::FocusMode focusMode = [&]() {
   5193    // If "Shift" and "Ctrl" are both pressed, "Shift" is given precedence. This
   5194    // mimics the old behaviour.
   5195    const bool isShift =
   5196        aMouseEvent->IsShift() &&
   5197        // If Shift + secondary button press shoud open context menu without a
   5198        // contextmenu event, user wants to open context menu like as a
   5199        // secondary button press without Shift key.
   5200        !(isSecondaryButton &&
   5201          StaticPrefs::dom_event_contextmenu_shift_suppresses_event());
   5202    if (isShift) {
   5203      // If clicked in a link when focused content is editable, we should
   5204      // collapse selection in the link for compatibility with Blink.
   5205      if (isEditor) {
   5206        for (Element* element : mContent->InclusiveAncestorsOfType<Element>()) {
   5207          if (element->IsLink()) {
   5208            return nsFrameSelection::FocusMode::kCollapseToNewPoint;
   5209          }
   5210        }
   5211      }
   5212      return nsFrameSelection::FocusMode::kExtendSelection;
   5213    }
   5214 
   5215    if (isPrimaryButtonDown && control) {
   5216      return nsFrameSelection::FocusMode::kMultiRangeSelection;
   5217    }
   5218 
   5219    return nsFrameSelection::FocusMode::kCollapseToNewPoint;
   5220  }();
   5221 
   5222  nsresult rv = fc->HandleClick(
   5223      MOZ_KnownLive(offsets.content) /* bug 1636889 */, offsets.StartOffset(),
   5224      offsets.EndOffset(), focusMode, offsets.associate);
   5225  if (NS_FAILED(rv)) {
   5226    return rv;
   5227  }
   5228 
   5229  // We don't handle mouse button up if it's middle button.
   5230  if (isPrimaryButtonDown && offsets.offset != offsets.secondaryOffset) {
   5231    fc->MaintainSelection();
   5232  }
   5233 
   5234  if (isPrimaryButtonDown && isEditor && !aMouseEvent->IsShift() &&
   5235      (offsets.EndOffset() - offsets.StartOffset()) == 1) {
   5236    // A single node is selected and we aren't extending an existing selection,
   5237    // which means the user clicked directly on an object (either
   5238    // `user-select: all` or a non-text node without children). Therefore,
   5239    // disable selection extension during mouse moves.
   5240    // XXX This is a bit hacky; shouldn't editor be able to deal with this?
   5241    fc->SetDragState(false);
   5242  }
   5243 
   5244  return NS_OK;
   5245 }
   5246 
   5247 bool nsIFrame::MovingCaretToEventPointAllowedIfSecondaryButtonEvent(
   5248    const nsFrameSelection& aFrameSelection,
   5249    WidgetMouseEvent& aSecondaryButtonEvent,
   5250    const nsIContent& aContentAtEventPoint, int32_t aOffsetAtEventPoint) const {
   5251  MOZ_ASSERT(aSecondaryButtonEvent.mButton == MouseButton::eSecondary);
   5252 
   5253  if (NS_WARN_IF(aOffsetAtEventPoint < 0)) {
   5254    return false;
   5255  }
   5256 
   5257  const bool contentIsEditable = aContentAtEventPoint.IsEditable();
   5258  const TextControlElement* const contentAsTextControl =
   5259      TextControlElement::FromNodeOrNull(
   5260          aContentAtEventPoint.IsTextControlElement()
   5261              ? &aContentAtEventPoint
   5262              : aContentAtEventPoint.GetClosestNativeAnonymousSubtreeRoot());
   5263  const Selection& selection = aFrameSelection.NormalSelection();
   5264  const bool selectionIsCollapsed =
   5265      selection.AreNormalAndCrossShadowBoundaryRangesCollapsed();
   5266  // If right click in a selection range, we should not collapse
   5267  // selection.
   5268  if (!selectionIsCollapsed && nsContentUtils::IsPointInSelection(
   5269                                   selection, aContentAtEventPoint,
   5270                                   static_cast<uint32_t>(aOffsetAtEventPoint),
   5271                                   true /* aAllowCrossShadowBoundary */)) {
   5272    return false;
   5273  }
   5274  const bool wantToPreventMoveCaret =
   5275      StaticPrefs::
   5276          ui_mouse_right_click_move_caret_stop_if_in_focused_editable_node() &&
   5277      selectionIsCollapsed && (contentIsEditable || contentAsTextControl);
   5278  const bool wantToPreventCollapseSelection =
   5279      StaticPrefs::
   5280          ui_mouse_right_click_collapse_selection_stop_if_non_collapsed_selection() &&
   5281      !selectionIsCollapsed;
   5282  if (wantToPreventMoveCaret || wantToPreventCollapseSelection) {
   5283    // If currently selection is limited in an editing host, we should not
   5284    // collapse selection nor move caret if the clicked point is in the
   5285    // ancestor limiter.  Otherwise, this mouse click moves focus from the
   5286    // editing host to different one or blur the editing host.  In this case,
   5287    // we need to update selection because keeping current selection in the
   5288    // editing host looks like it's not blurred.
   5289    // FIXME: If the active editing host is the document element, editor
   5290    // does not set ancestor limiter properly.  Fix it in the editor side.
   5291    if (nsIContent* ancestorLimiter = selection.GetAncestorLimiter()) {
   5292      MOZ_ASSERT(ancestorLimiter->IsEditable());
   5293      return !aContentAtEventPoint.IsInclusiveDescendantOf(ancestorLimiter);
   5294    }
   5295  }
   5296  // If selection is editable and `stop_if_in_focused_editable_node` pref is
   5297  // set to true, user does not want to move caret to right click place if
   5298  // clicked in the focused text control element.
   5299  if (wantToPreventMoveCaret && contentAsTextControl &&
   5300      contentAsTextControl == nsFocusManager::GetFocusedElementStatic()) {
   5301    return false;
   5302  }
   5303  // If currently selection is not limited in an editing host, we should
   5304  // collapse selection only when this click moves focus to an editing
   5305  // host because we need to update selection in this case.
   5306  if (wantToPreventCollapseSelection && !contentIsEditable) {
   5307    return false;
   5308  }
   5309 
   5310  return !StaticPrefs::
   5311             ui_mouse_right_click_collapse_selection_stop_if_non_editable_node() ||
   5312         // The user does not want to collapse selection into non-editable
   5313         // content by a right button click.
   5314         contentIsEditable ||
   5315         // Treat clicking in a text control as always clicked on editable
   5316         // content because we want a hack only for clicking in normal text
   5317         // nodes which is outside any editing hosts.
   5318         contentAsTextControl;
   5319 }
   5320 
   5321 nsresult nsIFrame::SelectByTypeAtPoint(const nsPoint& aPoint,
   5322                                       nsSelectionAmount aBeginAmountType,
   5323                                       nsSelectionAmount aEndAmountType,
   5324                                       uint32_t aSelectFlags) {
   5325  // No point in selecting if selection is turned off
   5326  if (!ShouldHandleSelectionMovementEvents()) {
   5327    return NS_OK;
   5328  }
   5329 
   5330  ContentOffsets offsets = GetContentOffsetsFromPoint(
   5331      aPoint, SKIP_HIDDEN | IGNORE_NATIVE_ANONYMOUS_SUBTREE);
   5332  if (!offsets.content) {
   5333    return NS_ERROR_FAILURE;
   5334  }
   5335 
   5336  FrameAndOffset frameAndOffset = SelectionMovementUtils::GetFrameForNodeOffset(
   5337      offsets.content, offsets.offset, offsets.associate);
   5338  if (!frameAndOffset) {
   5339    return NS_ERROR_FAILURE;
   5340  }
   5341  return frameAndOffset->PeekBackwardAndForwardForSelection(
   5342      aBeginAmountType, aEndAmountType,
   5343      static_cast<int32_t>(frameAndOffset.mOffsetInFrameContent),
   5344      aBeginAmountType != eSelectWord, aSelectFlags);
   5345 }
   5346 
   5347 /**
   5348 * Multiple Mouse Press -- line or paragraph selection -- for the frame.
   5349 * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
   5350 */
   5351 NS_IMETHODIMP
   5352 nsIFrame::HandleMultiplePress(nsPresContext* aPresContext,
   5353                              WidgetGUIEvent* aEvent,
   5354                              nsEventStatus* aEventStatus, bool aControlHeld) {
   5355  NS_ENSURE_ARG_POINTER(aEvent);
   5356  NS_ENSURE_ARG_POINTER(aEventStatus);
   5357 
   5358  if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
   5359      !ShouldHandleSelectionMovementEvents()) {
   5360    return NS_OK;
   5361  }
   5362 
   5363  // Find out whether we're doing line or paragraph selection.
   5364  // If browser.triple_click_selects_paragraph is true, triple-click selects
   5365  // paragraph. Otherwise, triple-click selects line, and quadruple-click
   5366  // selects paragraph (on platforms that support quadruple-click).
   5367  nsSelectionAmount beginAmount, endAmount;
   5368  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   5369  if (!mouseEvent) {
   5370    return NS_OK;
   5371  }
   5372 
   5373  if (mouseEvent->mClickCount == 4) {
   5374    beginAmount = endAmount = eSelectParagraph;
   5375  } else if (mouseEvent->mClickCount == 3) {
   5376    if (Preferences::GetBool("browser.triple_click_selects_paragraph")) {
   5377      beginAmount = endAmount = eSelectParagraph;
   5378    } else {
   5379      beginAmount = eSelectBeginLine;
   5380      endAmount = eSelectEndLine;
   5381    }
   5382  } else if (mouseEvent->mClickCount == 2) {
   5383    // We only want inline frames; PeekBackwardAndForward dislikes blocks
   5384    beginAmount = endAmount = eSelectWord;
   5385  } else {
   5386    return NS_OK;
   5387  }
   5388 
   5389  nsPoint relPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
   5390      mouseEvent, RelativeTo{this});
   5391  return SelectByTypeAtPoint(relPoint, beginAmount, endAmount,
   5392                             (aControlHeld ? SELECT_ACCUMULATE : 0));
   5393 }
   5394 
   5395 nsresult nsIFrame::PeekBackwardAndForwardForSelection(
   5396    nsSelectionAmount aAmountBack, nsSelectionAmount aAmountForward,
   5397    int32_t aStartPos, bool aJumpLines, uint32_t aSelectFlags) {
   5398  nsIFrame* baseFrame = this;
   5399  int32_t baseOffset = aStartPos;
   5400  nsresult rv;
   5401 
   5402  PeekOffsetOptions peekOffsetOptions{PeekOffsetOption::StopAtScroller};
   5403  if (aJumpLines) {
   5404    peekOffsetOptions += PeekOffsetOption::JumpLines;
   5405  }
   5406 
   5407  Element* const ancestorLimiter = [&]() -> Element* {
   5408    const nsFrameSelection* const frameSelection = GetConstFrameSelection();
   5409    return frameSelection
   5410               ? frameSelection
   5411                     ->GetAncestorLimiterOrIndependentSelectionRootElement()
   5412               : nullptr;
   5413  }();
   5414 
   5415  if (aAmountBack == eSelectWord) {
   5416    // To avoid selecting the previous word when at start of word,
   5417    // first move one character forward.
   5418    PeekOffsetStruct pos(eSelectCharacter, eDirNext, aStartPos, nsPoint(0, 0),
   5419                         peekOffsetOptions, eDefaultBehavior, ancestorLimiter);
   5420    rv = PeekOffset(&pos);
   5421    if (NS_SUCCEEDED(rv)) {
   5422      baseFrame = pos.mResultFrame;
   5423      baseOffset = pos.mContentOffset;
   5424    }
   5425  }
   5426 
   5427  // Search backward for a boundary.
   5428  PeekOffsetStruct startpos(aAmountBack, eDirPrevious, baseOffset,
   5429                            nsPoint(0, 0), peekOffsetOptions, eDefaultBehavior,
   5430                            ancestorLimiter);
   5431  rv = baseFrame->PeekOffset(&startpos);
   5432  if (NS_FAILED(rv)) {
   5433    return rv;
   5434  }
   5435 
   5436  // If the backward search stayed within the same frame, search forward from
   5437  // that position for the end boundary; but if it crossed out to a sibling or
   5438  // ancestor, start from the original position.
   5439  if (startpos.mResultFrame == baseFrame) {
   5440    baseOffset = startpos.mContentOffset;
   5441  } else {
   5442    baseFrame = this;
   5443    baseOffset = aStartPos;
   5444  }
   5445 
   5446  PeekOffsetStruct endpos(aAmountForward, eDirNext, baseOffset, nsPoint(0, 0),
   5447                          peekOffsetOptions, eDefaultBehavior, ancestorLimiter);
   5448  rv = baseFrame->PeekOffset(&endpos);
   5449  if (NS_FAILED(rv)) {
   5450    return rv;
   5451  }
   5452 
   5453  // Keep frameSelection alive.
   5454  RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
   5455 
   5456  const nsFrameSelection::FocusMode focusMode =
   5457      (aSelectFlags & SELECT_ACCUMULATE)
   5458          ? nsFrameSelection::FocusMode::kMultiRangeSelection
   5459          : nsFrameSelection::FocusMode::kCollapseToNewPoint;
   5460  rv = frameSelection->HandleClick(
   5461      MOZ_KnownLive(startpos.mResultContent) /* bug 1636889 */,
   5462      startpos.mContentOffset, startpos.mContentOffset, focusMode,
   5463      CaretAssociationHint::After);
   5464  if (NS_FAILED(rv)) {
   5465    return rv;
   5466  }
   5467 
   5468  rv = frameSelection->HandleClick(
   5469      MOZ_KnownLive(endpos.mResultContent) /* bug 1636889 */,
   5470      endpos.mContentOffset, endpos.mContentOffset,
   5471      nsFrameSelection::FocusMode::kExtendSelection,
   5472      CaretAssociationHint::Before);
   5473  if (NS_FAILED(rv)) {
   5474    return rv;
   5475  }
   5476  if (aAmountBack == eSelectWord) {
   5477    frameSelection->SetClickSelectionType(ClickSelectionType::Double);
   5478  } else if (aAmountBack == eSelectParagraph) {
   5479    frameSelection->SetClickSelectionType(ClickSelectionType::Triple);
   5480  }
   5481 
   5482  // maintain selection
   5483  return frameSelection->MaintainSelection(aAmountBack);
   5484 }
   5485 
   5486 NS_IMETHODIMP nsIFrame::HandleDrag(nsPresContext* aPresContext,
   5487                                   WidgetGUIEvent* aEvent,
   5488                                   nsEventStatus* aEventStatus) {
   5489  MOZ_ASSERT(aEvent->mClass == eMouseEventClass,
   5490             "HandleDrag can only handle mouse event");
   5491 
   5492  NS_ENSURE_ARG_POINTER(aEventStatus);
   5493 
   5494  RefPtr<nsFrameSelection> frameselection = GetFrameSelection();
   5495  if (!frameselection) {
   5496    return NS_OK;
   5497  }
   5498 
   5499  bool mouseDown = frameselection->GetDragState();
   5500  if (!mouseDown) {
   5501    return NS_OK;
   5502  }
   5503 
   5504  nsIFrame* scrollbar =
   5505      nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::Scrollbar);
   5506  if (!scrollbar && !ShouldHandleSelectionMovementEvents()) {
   5507    // XXX Do we really need to exclude non-selectable content here?
   5508    // GetContentOffsetsFromPoint can handle it just fine, although some
   5509    // other stuff might not like it.
   5510    return NS_OK;
   5511  }
   5512 
   5513  frameselection->StopAutoScrollTimer();
   5514 
   5515  // Check if we are dragging in a table cell
   5516  nsCOMPtr<nsIContent> parentContent;
   5517  int32_t contentOffset;
   5518  TableSelectionMode target;
   5519  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   5520  mozilla::PresShell* presShell = aPresContext->PresShell();
   5521  nsresult result;
   5522  result = GetDataForTableSelection(frameselection, presShell, mouseEvent,
   5523                                    getter_AddRefs(parentContent),
   5524                                    &contentOffset, &target);
   5525 
   5526  AutoWeakFrame weakThis = this;
   5527  if (NS_SUCCEEDED(result) && parentContent) {
   5528    result = frameselection->HandleTableSelection(parentContent, contentOffset,
   5529                                                  target, mouseEvent);
   5530    if (NS_WARN_IF(NS_FAILED(result))) {
   5531      return result;
   5532    }
   5533  } else {
   5534    nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
   5535                                                              RelativeTo{this});
   5536    frameselection->HandleDrag(this, pt);
   5537  }
   5538 
   5539  // The frameselection object notifies selection listeners synchronously above
   5540  // which might have killed us.
   5541  if (!weakThis.IsAlive()) {
   5542    return NS_OK;
   5543  }
   5544 
   5545  // Get the nearest scroll container frame.
   5546  ScrollContainerFrame* scrollContainerFrame =
   5547      nsLayoutUtils::GetNearestScrollContainerFrame(
   5548          this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
   5549                    nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
   5550 
   5551  if (scrollContainerFrame) {
   5552    nsIFrame* capturingFrame = scrollContainerFrame->GetScrolledFrame();
   5553    if (capturingFrame) {
   5554      nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
   5555          mouseEvent, RelativeTo{capturingFrame});
   5556      frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
   5557    }
   5558  }
   5559 
   5560  return NS_OK;
   5561 }
   5562 
   5563 /**
   5564 * This static method handles part of the nsIFrame::HandleRelease in a way
   5565 * which doesn't rely on the nsFrame object to stay alive.
   5566 */
   5567 MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult HandleFrameSelection(
   5568    nsFrameSelection* aFrameSelection, nsIFrame::ContentOffsets& aOffsets,
   5569    bool aHandleTableSel, int32_t aContentOffsetForTableSel,
   5570    TableSelectionMode aTargetForTableSel,
   5571    nsIContent* aParentContentForTableSel, WidgetGUIEvent* aEvent,
   5572    const nsEventStatus* aEventStatus) {
   5573  if (!aFrameSelection) {
   5574    return NS_OK;
   5575  }
   5576 
   5577  nsresult rv = NS_OK;
   5578 
   5579  if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
   5580    if (!aHandleTableSel) {
   5581      if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) {
   5582        return NS_ERROR_FAILURE;
   5583      }
   5584 
   5585      // We are doing this to simulate what we would have done on HandlePress.
   5586      // We didn't do it there to give the user an opportunity to drag
   5587      // the text, but since they didn't drag, we want to place the
   5588      // caret.
   5589      // However, we'll use the mouse position from the release, since:
   5590      //  * it's easier
   5591      //  * that's the normal click position to use (although really, in
   5592      //    the normal case, small movements that don't count as a drag
   5593      //    can do selection)
   5594      aFrameSelection->SetDragState(true);
   5595 
   5596      const nsFrameSelection::FocusMode focusMode =
   5597          aFrameSelection->IsShiftDownInDelayedCaretData()
   5598              ? nsFrameSelection::FocusMode::kExtendSelection
   5599              : nsFrameSelection::FocusMode::kCollapseToNewPoint;
   5600      rv = aFrameSelection->HandleClick(
   5601          MOZ_KnownLive(aOffsets.content) /* bug 1636889 */,
   5602          aOffsets.StartOffset(), aOffsets.EndOffset(), focusMode,
   5603          aOffsets.associate);
   5604      if (NS_FAILED(rv)) {
   5605        return rv;
   5606      }
   5607    } else if (aParentContentForTableSel) {
   5608      aFrameSelection->SetDragState(false);
   5609      rv = aFrameSelection->HandleTableSelection(
   5610          aParentContentForTableSel, aContentOffsetForTableSel,
   5611          aTargetForTableSel, aEvent->AsMouseEvent());
   5612      if (NS_FAILED(rv)) {
   5613        return rv;
   5614      }
   5615    }
   5616    aFrameSelection->SetDelayedCaretData(0);
   5617  }
   5618 
   5619  aFrameSelection->SetDragState(false);
   5620  aFrameSelection->StopAutoScrollTimer();
   5621 
   5622  return NS_OK;
   5623 }
   5624 
   5625 NS_IMETHODIMP nsIFrame::HandleRelease(nsPresContext* aPresContext,
   5626                                      WidgetGUIEvent* aEvent,
   5627                                      nsEventStatus* aEventStatus) {
   5628  if (aEvent->mClass != eMouseEventClass) {
   5629    return NS_OK;
   5630  }
   5631 
   5632  nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
   5633 
   5634  nsCOMPtr<nsIContent> captureContent = PresShell::GetCapturingContent();
   5635 
   5636  const bool selectionOff = !ShouldHandleSelectionMovementEvents();
   5637 
   5638  RefPtr<nsFrameSelection> frameselection;
   5639  ContentOffsets offsets;
   5640  nsCOMPtr<nsIContent> parentContent;
   5641  int32_t contentOffsetForTableSel = 0;
   5642  TableSelectionMode targetForTableSel = TableSelectionMode::None;
   5643  bool handleTableSelection = true;
   5644 
   5645  if (!selectionOff) {
   5646    frameselection = GetFrameSelection();
   5647    if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
   5648      // Check if the frameselection recorded the mouse going down.
   5649      // If not, the user must have clicked in a part of the selection.
   5650      // Place the caret before continuing!
   5651 
   5652      if (frameselection->MouseDownRecorded()) {
   5653        nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
   5654            aEvent, RelativeTo{this});
   5655        offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
   5656        handleTableSelection = false;
   5657      } else {
   5658        GetDataForTableSelection(frameselection, PresShell(),
   5659                                 aEvent->AsMouseEvent(),
   5660                                 getter_AddRefs(parentContent),
   5661                                 &contentOffsetForTableSel, &targetForTableSel);
   5662      }
   5663    }
   5664  }
   5665 
   5666  // We might be capturing in some other document and the event just happened to
   5667  // trickle down here. Make sure that document's frame selection is notified.
   5668  // Note, this may cause the current nsFrame object to be deleted, bug 336592.
   5669  RefPtr<nsFrameSelection> frameSelection;
   5670  if (activeFrame != this &&
   5671      activeFrame->ShouldHandleSelectionMovementEvents()) {
   5672    frameSelection = activeFrame->GetFrameSelection();
   5673  }
   5674 
   5675  // Also check the selection of the capturing content which might be in a
   5676  // different document.
   5677  if (!frameSelection && captureContent) {
   5678    if (Document* doc = captureContent->GetComposedDoc()) {
   5679      mozilla::PresShell* capturingPresShell = doc->GetPresShell();
   5680      if (capturingPresShell &&
   5681          capturingPresShell != PresContext()->GetPresShell()) {
   5682        frameSelection = capturingPresShell->FrameSelection();
   5683      }
   5684    }
   5685  }
   5686 
   5687  if (frameSelection) {
   5688    AutoWeakFrame wf(this);
   5689    frameSelection->SetDragState(false);
   5690    frameSelection->StopAutoScrollTimer();
   5691    if (wf.IsAlive()) {
   5692      ScrollContainerFrame* scrollContainerFrame =
   5693          nsLayoutUtils::GetNearestScrollContainerFrame(
   5694              this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
   5695                        nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
   5696      if (scrollContainerFrame) {
   5697        // Perform any additional scrolling needed to maintain CSS snap point
   5698        // requirements when autoscrolling is over.
   5699        scrollContainerFrame->ScrollSnap();
   5700      }
   5701    }
   5702  }
   5703 
   5704  // Do not call any methods of the current object after this point!!!
   5705  // The object is perhaps dead!
   5706 
   5707  return selectionOff ? NS_OK
   5708                      : HandleFrameSelection(
   5709                            frameselection, offsets, handleTableSelection,
   5710                            contentOffsetForTableSel, targetForTableSel,
   5711                            parentContent, aEvent, aEventStatus);
   5712 }
   5713 
   5714 struct MOZ_STACK_CLASS FrameContentRange {
   5715  FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd)
   5716      : content(aContent), start(aStart), end(aEnd) {}
   5717  nsCOMPtr<nsIContent> content;
   5718  int32_t start;
   5719  int32_t end;
   5720 };
   5721 
   5722 static bool IsRelevantBlockFrame(const nsIFrame* aFrame) {
   5723  if (!aFrame->IsBlockOutside() && !aFrame->IsTableCaption()) {
   5724    return false;
   5725  }
   5726  if (aFrame->GetContent() &&
   5727      aFrame->GetContent()->IsInNativeAnonymousSubtree() &&
   5728      !aFrame->GetContent()->HasBeenInUAWidget()) {
   5729    // This helps skipping things like scrollbar parts.
   5730    return false;
   5731  }
   5732  auto pseudoType = aFrame->Style()->GetPseudoType();
   5733  if (PseudoStyle::IsAnonBox(pseudoType)) {
   5734    // Table cell contents should be considered block boundaries for this
   5735    // purpose.
   5736    return pseudoType == PseudoStyleType::cellContent;
   5737  }
   5738  return true;
   5739 }
   5740 
   5741 // Retrieve the content offsets of a frame
   5742 static FrameContentRange GetRangeForFrame(const nsIFrame* aFrame) {
   5743  nsIContent* content = aFrame->GetContent();
   5744  if (!content) {
   5745    NS_WARNING("Frame has no content");
   5746    return FrameContentRange(nullptr, -1, -1);
   5747  }
   5748 
   5749  LayoutFrameType type = aFrame->Type();
   5750  if (type == LayoutFrameType::Text) {
   5751    auto [offset, offsetEnd] = aFrame->GetOffsets();
   5752    return FrameContentRange(content, offset, offsetEnd);
   5753  }
   5754 
   5755  if (type == LayoutFrameType::Br) {
   5756    nsIContent* parent = content->GetParent();
   5757    const int32_t beginOffset = parent->ComputeIndexOf_Deprecated(content);
   5758    return FrameContentRange(parent, beginOffset, beginOffset);
   5759  }
   5760 
   5761  while (content->IsRootOfNativeAnonymousSubtree()) {
   5762    content = content->GetParent();
   5763  }
   5764 
   5765  if (aFrame->IsReplaced()) {
   5766    if (auto* parent = content->GetParent()) {
   5767      // TODO(emilio): Revise this in presence of Shadow DOM / display:
   5768      // contents, it's likely that we don't want to just walk the light tree,
   5769      // and we need to change the representation of FrameContentRange.
   5770      Maybe<uint32_t> index = parent->ComputeIndexOf(content);
   5771      MOZ_ASSERT(index.isSome());
   5772      return FrameContentRange(parent, static_cast<int32_t>(*index),
   5773                               static_cast<int32_t>(*index + 1));
   5774    }
   5775  }
   5776 
   5777  return FrameContentRange(content, 0, content->GetChildCount());
   5778 }
   5779 
   5780 // The FrameTarget represents the closest frame to a point that can be selected
   5781 // The frame is the frame represented, frameEdge says whether one end of the
   5782 // frame is the result (in which case different handling is needed), and
   5783 // afterFrame says which end is represented if frameEdge is true
   5784 struct FrameTarget {
   5785  explicit operator bool() const { return !!frame; }
   5786 
   5787  nsIFrame* frame = nullptr;
   5788  bool frameEdge = false;
   5789  bool afterFrame = false;
   5790 };
   5791 
   5792 // See function implementation for information
   5793 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
   5794                                            const nsPoint& aPoint,
   5795                                            uint32_t aFlags);
   5796 
   5797 static bool SelfIsSelectable(nsIFrame* aFrame, nsIFrame* aParentFrame,
   5798                             uint32_t aFlags) {
   5799  // We should not move selection into a native anonymous subtree when handling
   5800  // selection outside it.
   5801  if ((aFlags & nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE) &&
   5802      aParentFrame->GetClosestNativeAnonymousSubtreeRoot() !=
   5803          aFrame->GetClosestNativeAnonymousSubtreeRoot()) {
   5804    return false;
   5805  }
   5806  if ((aFlags & nsIFrame::SKIP_HIDDEN) &&
   5807      !aFrame->StyleVisibility()->IsVisible()) {
   5808    return false;
   5809  }
   5810  if (aFrame->IsGeneratedContentFrame()) {
   5811    return false;
   5812  }
   5813  if (aFrame->Style()->UserSelect() == StyleUserSelect::None) {
   5814    return false;
   5815  }
   5816  if (aFrame->IsEmpty() &&
   5817      (!aFrame->IsTextFrame() || !aFrame->ContentIsEditable())) {
   5818    // FIXME(emilio): Historically we haven't treated empty frames as
   5819    // selectable, but also we had special-cases so that editable empty text
   5820    // frames returned false from IsEmpty(). Sort this out (probably by
   5821    // removing the IsEmpty() condition altogether).
   5822    return false;
   5823  }
   5824  return true;
   5825 }
   5826 
   5827 static bool FrameContentCanHaveParentSelectionRange(nsIFrame* aFrame) {
   5828  // If we are only near (not directly over) then don't traverse frames with
   5829  // independent selection (e.g. text controls, see bug 268497). Note that this
   5830  // prevents any of the users of this method from entering form controls.
   5831  // XXX We might want some way to allow using the up-arrow to go into a form
   5832  // control, but the focus didn't work right anyway; it'd probably be enough
   5833  // if the left and right arrows could enter textboxes (which I don't believe
   5834  // they can at the moment)
   5835  if (aFrame->IsTextInputFrame()) {
   5836    return false;
   5837  }
   5838  return !aFrame->IsGeneratedContentFrame();
   5839 }
   5840 
   5841 static bool SelectionDescendToKids(nsIFrame* aFrame) {
   5842  if (!FrameContentCanHaveParentSelectionRange(aFrame)) {
   5843    return false;
   5844  }
   5845  auto style = aFrame->Style()->UserSelect();
   5846  return style != StyleUserSelect::All && style != StyleUserSelect::None;
   5847 }
   5848 
   5849 static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild,
   5850                                                    const nsPoint& aPoint,
   5851                                                    uint32_t aFlags) {
   5852  nsIFrame* parent = aChild->GetParent();
   5853  if (SelectionDescendToKids(aChild)) {
   5854    nsPoint pt = aPoint - aChild->GetOffsetTo(parent);
   5855    return GetSelectionClosestFrame(aChild, pt, aFlags);
   5856  }
   5857  return FrameTarget{aChild, false, false};
   5858 }
   5859 
   5860 // When the cursor needs to be at the beginning of a block, it shouldn't be
   5861 // before the first child.  A click on a block whose first child is a block
   5862 // should put the cursor in the child.  The cursor shouldn't be between the
   5863 // blocks, because that's not where it's expected.
   5864 // Note that this method is guaranteed to succeed.
   5865 static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, bool aEndFrame,
   5866                                             uint32_t aFlags) {
   5867  if (SelectionDescendToKids(aFrame)) {
   5868    nsIFrame* result = nullptr;
   5869    nsIFrame* frame = aFrame->PrincipalChildList().FirstChild();
   5870    if (!aEndFrame) {
   5871      while (frame && !SelfIsSelectable(frame, aFrame, aFlags)) {
   5872        frame = frame->GetNextSibling();
   5873      }
   5874      if (frame) {
   5875        result = frame;
   5876      }
   5877    } else {
   5878      // Because the frame tree is singly linked, to find the last frame,
   5879      // we have to iterate through all the frames
   5880      // XXX I have a feeling this could be slow for long blocks, although
   5881      //     I can't find any slowdowns
   5882      while (frame) {
   5883        if (SelfIsSelectable(frame, aFrame, aFlags)) {
   5884          result = frame;
   5885        }
   5886        frame = frame->GetNextSibling();
   5887      }
   5888    }
   5889    if (result) {
   5890      return DrillDownToSelectionFrame(result, aEndFrame, aFlags);
   5891    }
   5892  }
   5893  // If the current frame has no targetable children, target the current frame
   5894  return FrameTarget{aFrame, true, aEndFrame};
   5895 }
   5896 
   5897 // This method finds the closest valid FrameTarget on a given line; if there is
   5898 // no valid FrameTarget on the line, it returns a null FrameTarget
   5899 static FrameTarget GetSelectionClosestFrameForLine(
   5900    nsBlockFrame* aParent, nsBlockFrame::LineIterator aLine,
   5901    const nsPoint& aPoint, uint32_t aFlags) {
   5902  // Account for end of lines (any iterator from the block is valid)
   5903  if (aLine == aParent->LinesEnd()) {
   5904    return DrillDownToSelectionFrame(aParent, true, aFlags);
   5905  }
   5906  nsIFrame* frame = aLine->mFirstChild;
   5907  nsIFrame* closestFromIStart = nullptr;
   5908  nsIFrame* closestFromIEnd = nullptr;
   5909  nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
   5910  WritingMode wm = aLine->mWritingMode;
   5911  LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
   5912  bool canSkipBr = false;
   5913  bool lastFrameWasEditable = false;
   5914  for (int32_t n = aLine->GetChildCount(); n;
   5915       --n, frame = frame->GetNextSibling()) {
   5916    // Skip brFrames. Can only skip if the line contains at least
   5917    // one selectable and non-empty frame before. Also, avoid skipping brs if
   5918    // the previous thing had a different editableness than us, since then we
   5919    // may end up not being able to select after it if the br is the last thing
   5920    // on the line.
   5921    if (!SelfIsSelectable(frame, aParent, aFlags) ||
   5922        (canSkipBr && frame->IsBrFrame() &&
   5923         lastFrameWasEditable == frame->GetContent()->IsEditable())) {
   5924      continue;
   5925    }
   5926    canSkipBr = true;
   5927    lastFrameWasEditable =
   5928        frame->GetContent() && frame->GetContent()->IsEditable();
   5929    LogicalRect frameRect =
   5930        LogicalRect(wm, frame->GetRect(), aLine->mContainerSize);
   5931    if (pt.I(wm) >= frameRect.IStart(wm)) {
   5932      if (pt.I(wm) < frameRect.IEnd(wm)) {
   5933        return GetSelectionClosestFrameForChild(frame, aPoint, aFlags);
   5934      }
   5935      if (frameRect.IEnd(wm) >= closestIStart) {
   5936        closestFromIStart = frame;
   5937        closestIStart = frameRect.IEnd(wm);
   5938      }
   5939    } else {
   5940      if (frameRect.IStart(wm) <= closestIEnd) {
   5941        closestFromIEnd = frame;
   5942        closestIEnd = frameRect.IStart(wm);
   5943      }
   5944    }
   5945  }
   5946  if (!closestFromIStart && !closestFromIEnd) {
   5947    // We should only get here if there are no selectable frames on a line
   5948    // XXX Do we need more elaborate handling here?
   5949    return FrameTarget();
   5950  }
   5951  if (closestFromIStart &&
   5952      (!closestFromIEnd ||
   5953       (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) {
   5954    return GetSelectionClosestFrameForChild(closestFromIStart, aPoint, aFlags);
   5955  }
   5956  return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags);
   5957 }
   5958 
   5959 // This method is for the special handling we do for block frames; they're
   5960 // special because they represent paragraphs and because they are organized
   5961 // into lines, which have bounds that are not stored elsewhere in the
   5962 // frame tree.  Returns a null FrameTarget for frames which are not
   5963 // blocks or blocks with no lines except editable one.
   5964 static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
   5965                                                    const nsPoint& aPoint,
   5966                                                    uint32_t aFlags) {
   5967  nsBlockFrame* bf = do_QueryFrame(aFrame);
   5968  if (!bf) {
   5969    return FrameTarget();
   5970  }
   5971 
   5972  // This code searches for the correct line
   5973  nsBlockFrame::LineIterator end = bf->LinesEnd();
   5974  nsBlockFrame::LineIterator curLine = bf->LinesBegin();
   5975  nsBlockFrame::LineIterator closestLine = end;
   5976 
   5977  if (curLine != end) {
   5978    // Convert aPoint into a LogicalPoint in the writing-mode of this block
   5979    WritingMode wm = curLine->mWritingMode;
   5980    LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
   5981    do {
   5982      // Check to see if our point lies within the line's block-direction bounds
   5983      nscoord BCoord = pt.B(wm) - curLine->BStart();
   5984      nscoord BSize = curLine->BSize();
   5985      if (BCoord >= 0 && BCoord < BSize) {
   5986        closestLine = curLine;
   5987        break;  // We found the line; stop looking
   5988      }
   5989      if (BCoord < 0) {
   5990        break;
   5991      }
   5992      ++curLine;
   5993    } while (curLine != end);
   5994 
   5995    if (closestLine == end) {
   5996      nsBlockFrame::LineIterator prevLine = curLine.prev();
   5997      nsBlockFrame::LineIterator nextLine = curLine;
   5998      // Avoid empty lines
   5999      while (nextLine != end && nextLine->IsEmpty()) {
   6000        ++nextLine;
   6001      }
   6002      while (prevLine != end && prevLine->IsEmpty()) {
   6003        --prevLine;
   6004      }
   6005 
   6006      // This hidden pref dictates whether a point above or below all lines
   6007      // comes up with a line or the beginning or end of the frame; 0 on
   6008      // Windows, 1 on other platforms by default at the writing of this code
   6009      int32_t dragOutOfFrame =
   6010          Preferences::GetInt("browser.drag_out_of_frame_style");
   6011 
   6012      if (prevLine == end) {
   6013        if (dragOutOfFrame == 1 || nextLine == end) {
   6014          return DrillDownToSelectionFrame(aFrame, false, aFlags);
   6015        }
   6016        closestLine = nextLine;
   6017      } else if (nextLine == end) {
   6018        if (dragOutOfFrame == 1) {
   6019          return DrillDownToSelectionFrame(aFrame, true, aFlags);
   6020        }
   6021        closestLine = prevLine;
   6022      } else {  // Figure out which line is closer
   6023        if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm)) {
   6024          closestLine = prevLine;
   6025        } else {
   6026          closestLine = nextLine;
   6027        }
   6028      }
   6029    }
   6030  }
   6031 
   6032  do {
   6033    if (auto target =
   6034            GetSelectionClosestFrameForLine(bf, closestLine, aPoint, aFlags)) {
   6035      return target;
   6036    }
   6037    ++closestLine;
   6038  } while (closestLine != end);
   6039 
   6040  // Fall back to just targeting the last targetable place
   6041  return DrillDownToSelectionFrame(aFrame, true, aFlags);
   6042 }
   6043 
   6044 // Use frame edge for grid, flex, table, and non-editable images. Choose the
   6045 // edge based on the point position past the frame rect. If past the middle,
   6046 // caret should be at end, otherwise at start. This behavior matches Blink.
   6047 //
   6048 // TODO(emilio): Can we use this code path for all other frames? We only get
   6049 // there when we didn't find selectable children... Editable images need _not_
   6050 // to use the frame edge tho, see below.
   6051 static bool UseFrameEdge(nsIFrame* aFrame) {
   6052  if (aFrame->IsFlexOrGridContainer() || aFrame->IsTableFrame()) {
   6053    return true;
   6054  }
   6055  // FIXME(bug 713387): The text frame check here shouldn't be needed.
   6056  if (aFrame->IsReplaced() && !aFrame->IsTextFrame() &&
   6057      !aFrame->GetContent()->IsEditable()) {
   6058    // Editable replaced elements are a special-case because editing relies
   6059    // on clicking on an editable image selecting it, for it to show resizers.
   6060    return true;
   6061  }
   6062  return false;
   6063 }
   6064 
   6065 static FrameTarget LastResortFrameTargetForFrame(nsIFrame* aFrame,
   6066                                                 const nsPoint& aPoint) {
   6067  if (!UseFrameEdge(aFrame)) {
   6068    return {aFrame, false, false};
   6069  }
   6070  const auto& rect = aFrame->GetRectRelativeToSelf();
   6071  nscoord reference;
   6072  nscoord middle;
   6073  if (aFrame->GetWritingMode().IsVertical()) {
   6074    reference = aPoint.y;
   6075    middle = rect.Height() / 2;
   6076  } else {
   6077    reference = aPoint.x;
   6078    middle = rect.Width() / 2;
   6079  }
   6080  const bool afterFrame = reference > middle;
   6081  return {aFrame, true, afterFrame};
   6082 }
   6083 
   6084 // GetSelectionClosestFrame is the helper function that calculates the closest
   6085 // frame to the given point.
   6086 // It doesn't completely account for offset styles, so needs to be used in
   6087 // restricted environments.
   6088 // Cannot handle overlapping frames correctly, so it should receive the output
   6089 // of GetFrameForPoint
   6090 // Guaranteed to return a valid FrameTarget.
   6091 // aPoint is relative to aFrame.
   6092 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
   6093                                            const nsPoint& aPoint,
   6094                                            uint32_t aFlags) {
   6095  // Handle blocks; if the frame isn't a block, the method fails
   6096  if (auto target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags)) {
   6097    return target;
   6098  }
   6099 
   6100  if (aFlags & nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE &&
   6101      !FrameContentCanHaveParentSelectionRange(aFrame)) {
   6102    return LastResortFrameTargetForFrame(aFrame, aPoint);
   6103  }
   6104 
   6105  if (nsIFrame* kid = aFrame->PrincipalChildList().FirstChild()) {
   6106    // Go through all the child frames to find the closest one
   6107    nsIFrame::FrameWithDistance closest = {nullptr, nscoord_MAX, nscoord_MAX};
   6108    for (; kid; kid = kid->GetNextSibling()) {
   6109      if (!SelfIsSelectable(kid, aFrame, aFlags)) {
   6110        continue;
   6111      }
   6112 
   6113      kid->FindCloserFrameForSelection(aPoint, &closest);
   6114    }
   6115    if (closest.mFrame) {
   6116      if (closest.mFrame->IsInSVGTextSubtree()) {
   6117        return FrameTarget{closest.mFrame, false, false};
   6118      }
   6119      return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
   6120    }
   6121  }
   6122 
   6123  return LastResortFrameTargetForFrame(aFrame, aPoint);
   6124 }
   6125 
   6126 static nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame,
   6127                                                      const nsPoint& aPoint) {
   6128  nsIFrame::ContentOffsets offsets;
   6129  FrameContentRange range = GetRangeForFrame(aFrame);
   6130  offsets.content = range.content;
   6131  // If there are continuations (meaning it's not one rectangle), this is the
   6132  // best this function can do
   6133  if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) {
   6134    offsets.offset = range.start;
   6135    offsets.secondaryOffset = range.end;
   6136    offsets.associate = CaretAssociationHint::After;
   6137    return offsets;
   6138  }
   6139 
   6140  // Figure out whether the offsets should be over, after, or before the frame
   6141  nsRect rect(nsPoint(0, 0), aFrame->GetSize());
   6142 
   6143  bool isBlock = !aFrame->StyleDisplay()->IsInlineFlow();
   6144  bool isRtl = (aFrame->StyleVisibility()->mDirection == StyleDirection::Rtl);
   6145  if ((isBlock && rect.y < aPoint.y) ||
   6146      (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
   6147                    (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
   6148    offsets.offset = range.end;
   6149    if (rect.Contains(aPoint)) {
   6150      offsets.secondaryOffset = range.start;
   6151    } else {
   6152      offsets.secondaryOffset = range.end;
   6153    }
   6154  } else {
   6155    offsets.offset = range.start;
   6156    if (rect.Contains(aPoint)) {
   6157      offsets.secondaryOffset = range.end;
   6158    } else {
   6159      offsets.secondaryOffset = range.start;
   6160    }
   6161  }
   6162  offsets.associate = offsets.offset == range.start
   6163                          ? CaretAssociationHint::After
   6164                          : CaretAssociationHint::Before;
   6165  return offsets;
   6166 }
   6167 
   6168 static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
   6169  nsIFrame* adjustedFrame = aFrame;
   6170  for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
   6171    // These are the conditions that make all children not able to handle
   6172    // a cursor.
   6173    auto userSelect = frame->Style()->UserSelect();
   6174    if (userSelect != StyleUserSelect::Auto &&
   6175        userSelect != StyleUserSelect::All) {
   6176      break;
   6177    }
   6178    if (userSelect == StyleUserSelect::All ||
   6179        frame->IsGeneratedContentFrame()) {
   6180      adjustedFrame = frame;
   6181    }
   6182  }
   6183  return adjustedFrame;
   6184 }
   6185 
   6186 nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(
   6187    const nsPoint& aPoint, uint32_t aFlags) {
   6188  nsIFrame* adjustedFrame;
   6189  if (aFlags & IGNORE_SELECTION_STYLE) {
   6190    adjustedFrame = this;
   6191  } else {
   6192    // This section of code deals with special selection styles.  Note that
   6193    // -moz-all exists, even though it doesn't need to be explicitly handled.
   6194    //
   6195    // The offset is forced not to end up in generated content; content offsets
   6196    // cannot represent content outside of the document's content tree.
   6197 
   6198    adjustedFrame = AdjustFrameForSelectionStyles(this);
   6199 
   6200    // `user-select: all` needs special handling, because clicking on it should
   6201    // lead to the whole frame being selected.
   6202    if (adjustedFrame->Style()->UserSelect() == StyleUserSelect::All) {
   6203      nsPoint adjustedPoint = aPoint + GetOffsetTo(adjustedFrame);
   6204      return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
   6205    }
   6206 
   6207    // For other cases, try to find a closest frame starting from the parent of
   6208    // the unselectable frame
   6209    if (adjustedFrame != this) {
   6210      adjustedFrame = adjustedFrame->GetParent();
   6211    }
   6212  }
   6213 
   6214  nsPoint adjustedPoint = aPoint + GetOffsetTo(adjustedFrame);
   6215 
   6216  FrameTarget closest =
   6217      GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags);
   6218 
   6219  // If the correct offset is at one end of a frame, use offset-based
   6220  // calculation method
   6221  if (closest.frameEdge) {
   6222    ContentOffsets offsets;
   6223    FrameContentRange range = GetRangeForFrame(closest.frame);
   6224    offsets.content = range.content;
   6225    if (closest.afterFrame) {
   6226      offsets.offset = range.end;
   6227    } else {
   6228      offsets.offset = range.start;
   6229    }
   6230    offsets.secondaryOffset = offsets.offset;
   6231    offsets.associate = offsets.offset == range.start
   6232                            ? CaretAssociationHint::After
   6233                            : CaretAssociationHint::Before;
   6234    return offsets;
   6235  }
   6236 
   6237  nsPoint pt;
   6238  if (closest.frame != this) {
   6239    if (closest.frame->IsInSVGTextSubtree()) {
   6240      pt = nsLayoutUtils::TransformAncestorPointToFrame(
   6241          RelativeTo{closest.frame}, aPoint, RelativeTo{this});
   6242    } else {
   6243      pt = aPoint - closest.frame->GetOffsetTo(this);
   6244    }
   6245  } else {
   6246    pt = aPoint;
   6247  }
   6248  return closest.frame->CalcContentOffsetsFromFramePoint(pt);
   6249 
   6250  // XXX should I add some kind of offset standardization?
   6251  // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
   6252  // x and first z put the cursor in the same logical position in addition
   6253  // to the same visual position?
   6254 }
   6255 
   6256 nsIFrame::ContentOffsets nsIFrame::CalcContentOffsetsFromFramePoint(
   6257    const nsPoint& aPoint) {
   6258  return OffsetsForSingleFrame(this, aPoint);
   6259 }
   6260 
   6261 bool nsIFrame::AssociateImage(const StyleImage& aImage) {
   6262  imgRequestProxy* req = aImage.GetImageRequest();
   6263  if (!req) {
   6264    return false;
   6265  }
   6266 
   6267  PresContext()->Document()->EnsureStyleImageLoader().AssociateRequestToFrame(
   6268      req, this);
   6269  return true;
   6270 }
   6271 
   6272 void nsIFrame::DisassociateImage(const StyleImage& aImage) {
   6273  imgRequestProxy* req = aImage.GetImageRequest();
   6274  if (!req) {
   6275    return;
   6276  }
   6277 
   6278  PresContext()
   6279      ->Document()
   6280      ->EnsureStyleImageLoader()
   6281      .DisassociateRequestFromFrame(req, this);
   6282 }
   6283 
   6284 StyleImageRendering nsIFrame::UsedImageRendering() const {
   6285  ComputedStyle* style;
   6286  if (IsCanvasFrame()) {
   6287    // XXXdholbert Maybe we should use FindCanvasBackground here (instead of
   6288    // FindBackground), since we're inside an IsCanvasFrame check? Though then
   6289    // we'd also have to copypaste or abstract-away the multi-part root-frame
   6290    // lookup that the canvas-flavored API requires.
   6291    style = nsCSSRendering::FindBackground(this);
   6292  } else {
   6293    style = Style();
   6294  }
   6295  return style->StyleVisibility()->mImageRendering;
   6296 }
   6297 
   6298 // The touch-action CSS property applies to: all elements except: non-replaced
   6299 // inline elements, table rows, row groups, table columns, and column groups.
   6300 StyleTouchAction nsIFrame::UsedTouchAction() const {
   6301  if (IsLineParticipant()) {
   6302    return StyleTouchAction::AUTO;
   6303  }
   6304  auto& disp = *StyleDisplay();
   6305  if (disp.IsInternalTableStyleExceptCell()) {
   6306    return StyleTouchAction::AUTO;
   6307  }
   6308  return disp.mTouchAction;
   6309 }
   6310 
   6311 nsIFrame::Cursor nsIFrame::GetCursor(const nsPoint&) {
   6312  StyleCursorKind kind = StyleUI()->Cursor().keyword;
   6313  if (kind == StyleCursorKind::Auto) {
   6314    // If this is editable, I-beam cursor is better for most elements.
   6315    kind = (mContent && mContent->IsEditable()) ? StyleCursorKind::Text
   6316                                                : StyleCursorKind::Default;
   6317  }
   6318  if (kind == StyleCursorKind::Text && GetWritingMode().IsVertical()) {
   6319    // Per CSS UI spec, UA may treat value 'text' as
   6320    // 'vertical-text' for vertical text.
   6321    kind = StyleCursorKind::VerticalText;
   6322  }
   6323 
   6324  return Cursor{kind, AllowCustomCursorImage::Yes};
   6325 }
   6326 
   6327 // Resize and incremental reflow
   6328 
   6329 /* virtual */
   6330 void nsIFrame::MarkIntrinsicISizesDirty() {
   6331  // If we're a flex item, clear our flex-item-specific cached measurements
   6332  // (which likely depended on our now-stale intrinsic isize).
   6333  if (IsFlexItem()) {
   6334    nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(this);
   6335  }
   6336 
   6337  if (IsGridItem()) {
   6338    nsGridContainerFrame::MarkCachedGridMeasurementsDirty(this);
   6339  }
   6340 
   6341  if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
   6342    nsFontInflationData::MarkFontInflationDataTextDirty(this);
   6343  }
   6344 }
   6345 
   6346 void nsIFrame::MarkSubtreeDirty() {
   6347  if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
   6348    return;
   6349  }
   6350  // Unconditionally mark given frame dirty.
   6351  AddStateBits(NS_FRAME_IS_DIRTY);
   6352 
   6353  // Mark all descendants dirty, unless:
   6354  // - Already dirty.
   6355  // - TableColGroup
   6356  AutoTArray<nsIFrame*, 32> stack;
   6357  for (const auto& childLists : ChildLists()) {
   6358    for (nsIFrame* kid : childLists.mList) {
   6359      stack.AppendElement(kid);
   6360    }
   6361  }
   6362  while (!stack.IsEmpty()) {
   6363    nsIFrame* f = stack.PopLastElement();
   6364    if (f->HasAnyStateBits(NS_FRAME_IS_DIRTY) || f->IsTableColGroupFrame()) {
   6365      continue;
   6366    }
   6367 
   6368    f->AddStateBits(NS_FRAME_IS_DIRTY);
   6369 
   6370    for (const auto& childLists : f->ChildLists()) {
   6371      for (nsIFrame* kid : childLists.mList) {
   6372        stack.AppendElement(kid);
   6373      }
   6374    }
   6375  }
   6376 }
   6377 
   6378 /* virtual */
   6379 void nsIFrame::AddInlineMinISize(const IntrinsicSizeInput& aInput,
   6380                                 InlineMinISizeData* aData) {
   6381  // Note: we are one of the children that mPercentageBasisForChildren was
   6382  // prepared for (i.e. our parent frame prepares the percentage basis for us,
   6383  // not for our own children). Hence it's fine that we're resolving our
   6384  // percentages sizes against this basis in IntrinsicForContainer().
   6385  nscoord isize = nsLayoutUtils::IntrinsicForContainer(
   6386      aInput.mContext, this, IntrinsicISizeType::MinISize,
   6387      aInput.mPercentageBasisForChildren);
   6388  aData->DefaultAddInlineMinISize(this, isize);
   6389 }
   6390 
   6391 /* virtual */
   6392 void nsIFrame::AddInlinePrefISize(const IntrinsicSizeInput& aInput,
   6393                                  nsIFrame::InlinePrefISizeData* aData) {
   6394  // Note: we are one of the children that mPercentageBasisForChildren was
   6395  // prepared for (i.e. our parent frame prepares the percentage basis for us,
   6396  // not for our own children). Hence it's fine that we're resolving our
   6397  // percentages sizes against this basis in IntrinsicForContainer().
   6398  nscoord isize = nsLayoutUtils::IntrinsicForContainer(
   6399      aInput.mContext, this, IntrinsicISizeType::PrefISize,
   6400      aInput.mPercentageBasisForChildren);
   6401  aData->DefaultAddInlinePrefISize(isize);
   6402 }
   6403 
   6404 void nsIFrame::InlineMinISizeData::DefaultAddInlineMinISize(nsIFrame* aFrame,
   6405                                                            nscoord aISize,
   6406                                                            bool aAllowBreak) {
   6407  auto parent = aFrame->GetParent();
   6408  MOZ_ASSERT(parent, "Must have a parent if we get here!");
   6409  const bool mayBreak = aAllowBreak && !aFrame->CanContinueTextRun() &&
   6410                        !parent->Style()->ShouldSuppressLineBreak() &&
   6411                        parent->StyleText()->WhiteSpaceCanWrap(parent);
   6412  if (mayBreak) {
   6413    OptionallyBreak();
   6414  }
   6415  mTrailingWhitespace = 0;
   6416  mSkipWhitespace = false;
   6417  mCurrentLine += aISize;
   6418  mAtStartOfLine = false;
   6419  if (mayBreak) {
   6420    OptionallyBreak();
   6421  }
   6422 }
   6423 
   6424 void nsIFrame::InlinePrefISizeData::DefaultAddInlinePrefISize(nscoord aISize) {
   6425  mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, aISize);
   6426  mTrailingWhitespace = 0;
   6427  mSkipWhitespace = false;
   6428  mLineIsEmpty = false;
   6429 }
   6430 
   6431 void nsIFrame::InlineMinISizeData::ForceBreak() {
   6432  mCurrentLine -= mTrailingWhitespace;
   6433  mPrevLines = std::max(mPrevLines, mCurrentLine);
   6434  mCurrentLine = mTrailingWhitespace = 0;
   6435 
   6436  for (const FloatInfo& floatInfo : mFloats) {
   6437    mPrevLines = std::max(floatInfo.ISize(), mPrevLines);
   6438  }
   6439  mFloats.Clear();
   6440  mSkipWhitespace = true;
   6441 }
   6442 
   6443 void nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth) {
   6444  // If we can fit more content into a smaller width by staying on this
   6445  // line (because we're still at a negative offset due to negative
   6446  // text-indent or negative margin), don't break.  Otherwise, do the
   6447  // same as ForceBreak.  it doesn't really matter when we accumulate
   6448  // floats.
   6449  if (mCurrentLine + aHyphenWidth < 0 || mAtStartOfLine) {
   6450    return;
   6451  }
   6452  mCurrentLine += aHyphenWidth;
   6453  ForceBreak();
   6454 }
   6455 
   6456 void nsIFrame::InlinePrefISizeData::ForceBreak(UsedClear aClearType) {
   6457  // If this force break is not clearing any float, we can leave all the
   6458  // floats to the next force break.
   6459  if (!mFloats.IsEmpty() && aClearType != UsedClear::None) {
   6460    // Preferred isize accumulated for floats that have already
   6461    // been cleared past
   6462    nscoord floatsDone = 0;
   6463    // Preferred isize accumulated for floats that have not yet
   6464    // been cleared past
   6465    nscoord floatsCurLeft = 0, floatsCurRight = 0;
   6466 
   6467    for (const FloatInfo& floatInfo : mFloats) {
   6468      const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
   6469      auto cbWM = floatInfo.Frame()->GetParent()->GetWritingMode();
   6470      UsedClear clearType = floatDisp->UsedClear(cbWM);
   6471      if (clearType == UsedClear::Left || clearType == UsedClear::Right ||
   6472          clearType == UsedClear::Both) {
   6473        nscoord floatsCur = NSCoordSaturatingAdd(floatsCurLeft, floatsCurRight);
   6474        if (floatsCur > floatsDone) {
   6475          floatsDone = floatsCur;
   6476        }
   6477        if (clearType != UsedClear::Right) {
   6478          floatsCurLeft = 0;
   6479        }
   6480        if (clearType != UsedClear::Left) {
   6481          floatsCurRight = 0;
   6482        }
   6483      }
   6484 
   6485      UsedFloat floatStyle = floatDisp->UsedFloat(cbWM);
   6486      nscoord& floatsCur =
   6487          floatStyle == UsedFloat::Left ? floatsCurLeft : floatsCurRight;
   6488      nscoord floatISize = floatInfo.ISize();
   6489      // Negative-width floats don't change the available space so they
   6490      // shouldn't change our intrinsic line isize either.
   6491      floatsCur = NSCoordSaturatingAdd(floatsCur, std::max(0, floatISize));
   6492    }
   6493 
   6494    nscoord floatsCur = NSCoordSaturatingAdd(floatsCurLeft, floatsCurRight);
   6495    if (floatsCur > floatsDone) {
   6496      floatsDone = floatsCur;
   6497    }
   6498 
   6499    mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, floatsDone);
   6500 
   6501    if (aClearType == UsedClear::Both) {
   6502      mFloats.Clear();
   6503    } else {
   6504      // If the break type does not clear all floats, it means there may
   6505      // be some floats whose isize should contribute to the intrinsic
   6506      // isize of the next line. The code here scans the current mFloats
   6507      // and keeps floats which are not cleared by this break. Note that
   6508      // floats may be cleared directly or indirectly. See below.
   6509      nsTArray<FloatInfo> newFloats;
   6510      MOZ_ASSERT(
   6511          aClearType == UsedClear::Left || aClearType == UsedClear::Right,
   6512          "Other values should have been handled in other branches");
   6513      UsedFloat clearFloatType =
   6514          aClearType == UsedClear::Left ? UsedFloat::Left : UsedFloat::Right;
   6515      // Iterate the array in reverse so that we can stop when there are
   6516      // no longer any floats we need to keep. See below.
   6517      for (FloatInfo& floatInfo : Reversed(mFloats)) {
   6518        const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
   6519        auto cbWM = floatInfo.Frame()->GetParent()->GetWritingMode();
   6520        if (floatDisp->UsedFloat(cbWM) != clearFloatType) {
   6521          newFloats.AppendElement(floatInfo);
   6522        } else {
   6523          // This is a float on the side that this break directly clears
   6524          // which means we're not keeping it in mFloats. However, if
   6525          // this float clears floats on the opposite side (via a value
   6526          // of either 'both' or one of 'left'/'right'), any remaining
   6527          // (earlier) floats on that side would be indirectly cleared
   6528          // as well. Thus, we should break out of this loop and stop
   6529          // considering earlier floats to be kept in mFloats.
   6530          UsedClear clearType = floatDisp->UsedClear(cbWM);
   6531          if (clearType != aClearType && clearType != UsedClear::None) {
   6532            break;
   6533          }
   6534        }
   6535      }
   6536      newFloats.Reverse();
   6537      mFloats = std::move(newFloats);
   6538    }
   6539  }
   6540 
   6541  mCurrentLine =
   6542      NSCoordSaturatingSubtract(mCurrentLine, mTrailingWhitespace, nscoord_MAX);
   6543  mPrevLines = std::max(mPrevLines, mCurrentLine);
   6544  mCurrentLine = mTrailingWhitespace = 0;
   6545  mSkipWhitespace = true;
   6546  mLineIsEmpty = true;
   6547 }
   6548 
   6549 static nscoord ResolvePadding(const LengthPercentage& aStyle,
   6550                              nscoord aPercentageBasis) {
   6551  return nsLayoutUtils::ResolveToLength<true>(aStyle, aPercentageBasis);
   6552 }
   6553 
   6554 static nscoord ResolveMargin(const AnchorResolvedMargin& aStyle,
   6555                             nscoord aPercentageBasis) {
   6556  if (!aStyle->IsLengthPercentage()) {
   6557    return nscoord(0);
   6558  }
   6559  return nsLayoutUtils::ResolveToLength<false>(aStyle->AsLengthPercentage(),
   6560                                               aPercentageBasis);
   6561 }
   6562 static nsIFrame::IntrinsicSizeOffsetData IntrinsicSizeOffsets(
   6563    nsIFrame* aFrame, nscoord aPercentageBasis, bool aForISize) {
   6564  nsIFrame::IntrinsicSizeOffsetData result;
   6565  WritingMode wm = aFrame->GetWritingMode();
   6566  bool verticalAxis = aForISize == wm.IsVertical();
   6567  const auto* styleMargin = aFrame->StyleMargin();
   6568  const auto anchorResolutionParams = AnchorPosResolutionParams::From(aFrame);
   6569  if (verticalAxis) {
   6570    result.margin +=
   6571        ResolveMargin(styleMargin->GetMargin(eSideTop, anchorResolutionParams),
   6572                      aPercentageBasis);
   6573    result.margin += ResolveMargin(
   6574        styleMargin->GetMargin(eSideBottom, anchorResolutionParams),
   6575        aPercentageBasis);
   6576  } else {
   6577    result.margin +=
   6578        ResolveMargin(styleMargin->GetMargin(eSideLeft, anchorResolutionParams),
   6579                      aPercentageBasis);
   6580    result.margin += ResolveMargin(
   6581        styleMargin->GetMargin(eSideRight, anchorResolutionParams),
   6582        aPercentageBasis);
   6583  }
   6584 
   6585  const auto& padding = aFrame->StylePadding()->mPadding;
   6586  if (verticalAxis) {
   6587    result.padding += ResolvePadding(padding.Get(eSideTop), aPercentageBasis);
   6588    result.padding +=
   6589        ResolvePadding(padding.Get(eSideBottom), aPercentageBasis);
   6590  } else {
   6591    result.padding += ResolvePadding(padding.Get(eSideLeft), aPercentageBasis);
   6592    result.padding += ResolvePadding(padding.Get(eSideRight), aPercentageBasis);
   6593  }
   6594 
   6595  const nsStyleBorder* styleBorder = aFrame->StyleBorder();
   6596  if (verticalAxis) {
   6597    result.border += styleBorder->GetComputedBorderWidth(eSideTop);
   6598    result.border += styleBorder->GetComputedBorderWidth(eSideBottom);
   6599  } else {
   6600    result.border += styleBorder->GetComputedBorderWidth(eSideLeft);
   6601    result.border += styleBorder->GetComputedBorderWidth(eSideRight);
   6602  }
   6603 
   6604  const nsStyleDisplay* disp = aFrame->StyleDisplay();
   6605  if (aFrame->IsThemed(disp)) {
   6606    nsPresContext* presContext = aFrame->PresContext();
   6607 
   6608    LayoutDeviceIntMargin border = presContext->Theme()->GetWidgetBorder(
   6609        presContext->DeviceContext(), aFrame, disp->EffectiveAppearance());
   6610    result.border = presContext->DevPixelsToAppUnits(
   6611        verticalAxis ? border.TopBottom() : border.LeftRight());
   6612 
   6613    LayoutDeviceIntMargin padding;
   6614    if (presContext->Theme()->GetWidgetPadding(
   6615            presContext->DeviceContext(), aFrame, disp->EffectiveAppearance(),
   6616            &padding)) {
   6617      result.padding = presContext->DevPixelsToAppUnits(
   6618          verticalAxis ? padding.TopBottom() : padding.LeftRight());
   6619    }
   6620  }
   6621  return result;
   6622 }
   6623 
   6624 /* virtual */ nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicISizeOffsets(
   6625    nscoord aPercentageBasis) {
   6626  return IntrinsicSizeOffsets(this, aPercentageBasis, true);
   6627 }
   6628 
   6629 nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicBSizeOffsets(
   6630    nscoord aPercentageBasis) {
   6631  return IntrinsicSizeOffsets(this, aPercentageBasis, false);
   6632 }
   6633 
   6634 /* virtual */
   6635 IntrinsicSize nsIFrame::GetIntrinsicSize() {
   6636  // Defaults to no intrinsic size.
   6637  return IntrinsicSize();
   6638 }
   6639 
   6640 AspectRatio nsIFrame::GetAspectRatio() const {
   6641  // Per spec, 'aspect-ratio' property applies to all elements except inline
   6642  // boxes and internal ruby or table boxes.
   6643  // https://drafts.csswg.org/css-sizing-4/#aspect-ratio
   6644  // For those frame types that don't support aspect-ratio, they must not have
   6645  // the natural ratio, so this early return is fine.
   6646  if (!SupportsAspectRatio()) {
   6647    return AspectRatio();
   6648  }
   6649 
   6650  const StyleAspectRatio& aspectRatio = StylePosition()->mAspectRatio;
   6651  // If aspect-ratio is zero or infinite, it's a degenerate ratio and behaves
   6652  // as auto.
   6653  // https://drafts.csswg.org/css-sizing-4/#valdef-aspect-ratio-ratio
   6654  if (!aspectRatio.BehavesAsAuto()) {
   6655    // Non-auto. Return the preferred aspect ratio from the aspect-ratio style.
   6656    return aspectRatio.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::Yes);
   6657  }
   6658 
   6659  // The rest of the cases are when aspect-ratio has 'auto'.
   6660  if (auto intrinsicRatio = GetIntrinsicRatio()) {
   6661    return intrinsicRatio;
   6662  }
   6663 
   6664  if (aspectRatio.HasRatio()) {
   6665    // If it's a degenerate ratio, this returns 0. Just the same as the auto
   6666    // case.
   6667    return aspectRatio.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::No);
   6668  }
   6669 
   6670  return AspectRatio();
   6671 }
   6672 
   6673 /* virtual */
   6674 AspectRatio nsIFrame::GetIntrinsicRatio() const { return AspectRatio(); }
   6675 
   6676 static bool ShouldApplyAutomaticMinimumOnInlineAxis(
   6677    WritingMode aWM, bool aIsScrollableOverflow,
   6678    const AnchorPosResolutionParams& aParams,
   6679    const nsStylePosition* aPosition) {
   6680  // Apply the automatic minimum size for aspect ratio:
   6681  // Note: The replaced elements shouldn't be here, so we only check the scroll
   6682  // container.
   6683  // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
   6684  return !aIsScrollableOverflow && aPosition->MinISize(aWM, aParams)->IsAuto();
   6685 }
   6686 
   6687 /* virtual */
   6688 nsIFrame::SizeComputationResult nsIFrame::ComputeSize(
   6689    const SizeComputationInput& aSizingInput, WritingMode aWM,
   6690    const LogicalSize& aCBSize, nscoord aAvailableISize,
   6691    const LogicalSize& aMargin, const LogicalSize& aBorderPadding,
   6692    const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
   6693  MOZ_ASSERT(!GetIntrinsicRatio(),
   6694             "Please override this method and call "
   6695             "nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead.");
   6696  LogicalSize result =
   6697      ComputeAutoSize(aSizingInput, aWM, aCBSize, aAvailableISize, aMargin,
   6698                      aBorderPadding, aSizeOverrides, aFlags);
   6699  const nsStylePosition* stylePos = StylePosition();
   6700  const nsStyleDisplay* disp = StyleDisplay();
   6701  const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   6702  auto aspectRatioUsage = AspectRatioUsage::None;
   6703 
   6704  const auto boxSizingAdjust = stylePos->mBoxSizing == StyleBoxSizing::Border
   6705                                   ? aBorderPadding
   6706                                   : LogicalSize(aWM);
   6707  nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) +
   6708                                       aBorderPadding.ISize(aWM) -
   6709                                       boxSizingAdjust.ISize(aWM);
   6710 
   6711  const auto& aspectRatio = aSizeOverrides.mAspectRatio
   6712                                ? *aSizeOverrides.mAspectRatio
   6713                                : GetAspectRatio();
   6714  const auto styleISize =
   6715      aSizeOverrides.mStyleISize
   6716          ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
   6717          : stylePos->ISize(aWM, anchorResolutionParams);
   6718  // For bsize, we consider overrides *and then* we resolve 'stretch' to a
   6719  // nscoord value, for convenience (so that we can assume that either
   6720  // isAutoBSize is true, or styleBSize is of type LengthPercentage()).
   6721  const auto styleBSize = [&] {
   6722    auto styleBSizeConsideringOverrides =
   6723        aSizeOverrides.mStyleBSize
   6724            ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize)
   6725            : stylePos->BSize(aWM, anchorResolutionParams);
   6726    if (styleBSizeConsideringOverrides->BehavesLikeStretchOnBlockAxis() &&
   6727        aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
   6728      // We've got a 'stretch' BSize; resolve it to a length:
   6729      nscoord stretchBSize = nsLayoutUtils::ComputeStretchBSize(
   6730          aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
   6731          stylePos->mBoxSizing);
   6732      // Note(dshin): This allocates.
   6733      return AnchorResolvedSizeHelper::LengthPercentage(
   6734          LengthPercentage::FromAppUnits(stretchBSize));
   6735    }
   6736    return styleBSizeConsideringOverrides;
   6737  }();
   6738 
   6739  auto parentFrame = GetParent();
   6740  auto alignCB = parentFrame;
   6741  bool isGridItem = IsGridItem();
   6742  const bool isSubgrid = IsSubgrid();
   6743  if (parentFrame && parentFrame->IsTableWrapperFrame() && IsTableFrame()) {
   6744    // An inner table frame is sized as a grid item if its table wrapper is,
   6745    // because they actually have the same CB (the wrapper's CB).
   6746    // @see ReflowInput::InitCBReflowInput
   6747    auto tableWrapper = GetParent();
   6748    auto grandParent = tableWrapper->GetParent();
   6749    isGridItem = grandParent->IsGridContainerFrame() &&
   6750                 !tableWrapper->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
   6751    if (isGridItem) {
   6752      // When resolving justify/align-self below, we want to use the grid
   6753      // container's justify/align-items value and WritingMode.
   6754      alignCB = grandParent;
   6755    }
   6756  }
   6757 
   6758  // flexItemMainAxis is set if this frame is a flex item in a modern flexbox
   6759  // layout. It indicates which logical axis (in this frame's own WM)
   6760  // corresponds to its flex container's main axis.
   6761  Maybe<LogicalAxis> flexItemMainAxis;
   6762  if (IsFlexItem() && !parentFrame->HasAnyStateBits(
   6763                          NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX)) {
   6764    flexItemMainAxis = Some(nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
   6765                                ? LogicalAxis::Inline
   6766                                : LogicalAxis::Block);
   6767  }
   6768 
   6769  const bool isAutoISize = styleISize->IsAuto();
   6770  const bool isAutoBSize =
   6771      nsLayoutUtils::IsAutoBSize(*styleBSize, aCBSize.BSize(aWM));
   6772 
   6773  MOZ_ASSERT(isAutoBSize || styleBSize->IsLengthPercentage(),
   6774             "We should have resolved away any non-'auto'-like flavors "
   6775             "of styleBSize into a LengthPercentage. (If this fails, we "
   6776             "might run afoul of some AsLengthPercentage() call below.)");
   6777 
   6778  // Compute inline-axis size
   6779  const bool isSubgriddedInInlineAxis =
   6780      isSubgrid && static_cast<nsGridContainerFrame*>(this)->IsColSubgrid();
   6781 
   6782  // Per https://drafts.csswg.org/css-grid/#subgrid-box-alignment, if we are
   6783  // subgridded in the inline-axis, ignore our style inline-size, and stretch to
   6784  // fill the CB.
   6785  const bool shouldComputeISize = !isAutoISize && !isSubgriddedInInlineAxis;
   6786  if (shouldComputeISize) {
   6787    auto iSizeResult =
   6788        ComputeISizeValue(aSizingInput.mRenderingContext, aWM, aCBSize,
   6789                          boxSizingAdjust, boxSizingToMarginEdgeISize,
   6790                          *styleISize, *styleBSize, aspectRatio, aFlags);
   6791    result.ISize(aWM) = iSizeResult.mISize;
   6792    aspectRatioUsage = iSizeResult.mAspectRatioUsage;
   6793  } else if (MOZ_UNLIKELY(isGridItem) && !IsTrueOverflowContainer()) {
   6794    // 'auto' inline-size for grid-level box - fill the CB for 'stretch' /
   6795    // 'normal' and clamp it to the CB if requested:
   6796    bool isStretchAligned = false;
   6797    bool mayUseAspectRatio = aspectRatio && !isAutoBSize;
   6798    if (!aFlags.contains(ComputeSizeFlag::ShrinkWrap) &&
   6799        !StyleMargin()->HasInlineAxisAuto(aWM, anchorResolutionParams) &&
   6800        !alignCB->IsMasonry(aWM, LogicalAxis::Inline)) {
   6801      auto inlineAxisAlignment = stylePos->UsedSelfAlignment(
   6802          aWM, LogicalAxis::Inline, alignCB->GetWritingMode(),
   6803          alignCB->Style());
   6804      isStretchAligned = inlineAxisAlignment == StyleAlignFlags::STRETCH ||
   6805                         (inlineAxisAlignment == StyleAlignFlags::NORMAL &&
   6806                          !mayUseAspectRatio);
   6807    }
   6808 
   6809    // Apply the preferred aspect ratio for alignments other than *stretch* and
   6810    // *normal without aspect ratio*.
   6811    // The spec says all other values should size the items as fit-content, and
   6812    // the intrinsic size should respect the preferred aspect ratio, so we also
   6813    // apply aspect ratio for all other values.
   6814    // https://drafts.csswg.org/css-grid/#grid-item-sizing
   6815    if (!isStretchAligned && mayUseAspectRatio) {
   6816      result.ISize(aWM) = ComputeISizeValueFromAspectRatio(
   6817          aWM, aCBSize, boxSizingAdjust, styleBSize->AsLengthPercentage(),
   6818          aspectRatio);
   6819      aspectRatioUsage = AspectRatioUsage::ToComputeISize;
   6820    }
   6821 
   6822    if (isStretchAligned ||
   6823        aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
   6824      auto iSizeToFillCB =
   6825          std::max(nscoord(0), aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) -
   6826                                   aMargin.ISize(aWM));
   6827      if (isStretchAligned || result.ISize(aWM) > iSizeToFillCB) {
   6828        result.ISize(aWM) = iSizeToFillCB;
   6829      }
   6830    }
   6831  } else if (aspectRatio && !isAutoBSize) {
   6832    // Note: if both the inline size and the block size are auto, the block axis
   6833    // is the ratio-dependent axis by default. That means we only need to
   6834    // transfer the resolved inline size via aspect-ratio to block axis later in
   6835    // this method, but not the other way around.
   6836    //
   6837    // In this branch, we transfer the non-auto block size via aspect-ration to
   6838    // inline axis.
   6839    result.ISize(aWM) = ComputeISizeValueFromAspectRatio(
   6840        aWM, aCBSize, boxSizingAdjust, styleBSize->AsLengthPercentage(),
   6841        aspectRatio);
   6842    aspectRatioUsage = AspectRatioUsage::ToComputeISize;
   6843  }
   6844 
   6845  // Calculate and apply transferred min & max size contraints.
   6846  // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers
   6847  //
   6848  // Note: The basic principle is that sizing constraints transfer through the
   6849  // aspect-ratio to the other side to preserve the aspect ratio to the extent
   6850  // that they can without violating any sizes specified explicitly on that
   6851  // affected axis.
   6852  //
   6853  // FIXME: The spec words may not be correct, so we may have to update this
   6854  // tentative solution once this spec issue gets resolved. Here, we clamp the
   6855  // flex base size by the transferred min and max sizes, and don't include
   6856  // the transferred min & max sizes into its used min & max sizes. So this
   6857  // lets us match other browsers' current behaviors.
   6858  // https://github.com/w3c/csswg-drafts/issues/6071
   6859  //
   6860  // Note: This may make more sense if we clamp the flex base size in
   6861  // FlexItem::ResolveFlexBaseSizeFromAspectRatio(). However, the result should
   6862  // be identical. FlexItem::ResolveFlexBaseSizeFromAspectRatio() only handles
   6863  // the case of the definite cross size, and the definite cross size is clamped
   6864  // by the min & max cross sizes below in this function. This means its flex
   6865  // base size has been clamped by the transferred min & max size already after
   6866  // generating the flex items. So here we make the code more general for both
   6867  // definite cross size and indefinite cross size.
   6868  const bool isDefiniteISize = styleISize->IsLengthPercentage();
   6869  const auto minBSizeCoord = stylePos->MinBSize(aWM, anchorResolutionParams);
   6870  const auto maxBSizeCoord = stylePos->MaxBSize(aWM, anchorResolutionParams);
   6871  const bool isAutoMinBSize =
   6872      nsLayoutUtils::IsAutoBSize(*minBSizeCoord, aCBSize.BSize(aWM));
   6873  const bool isAutoMaxBSize =
   6874      nsLayoutUtils::IsAutoBSize(*maxBSizeCoord, aCBSize.BSize(aWM));
   6875  if (aspectRatio && !isDefiniteISize) {
   6876    // Note: the spec mentions that
   6877    // 1. This transferred minimum is capped by any definite preferred or
   6878    //    maximum size in the destination axis.
   6879    // 2. This transferred maximum is floored by any definite preferred or
   6880    //    minimum size in the destination axis.
   6881    //
   6882    // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers
   6883    //
   6884    // The spec requires us to clamp these by the specified size (it calls it
   6885    // the preferred size). However, we actually don't need to worry about that,
   6886    // because we are here only if the inline size is indefinite.
   6887    //
   6888    // We do not need to clamp the transferred minimum and maximum as long as we
   6889    // always apply the transferred min/max size before the explicit min/max
   6890    // size; the result will be identical.
   6891    const nscoord transferredMinISize =
   6892        isAutoMinBSize ? 0
   6893                       : ComputeISizeValueFromAspectRatio(
   6894                             aWM, aCBSize, boxSizingAdjust,
   6895                             minBSizeCoord->AsLengthPercentage(), aspectRatio);
   6896    const nscoord transferredMaxISize =
   6897        isAutoMaxBSize ? nscoord_MAX
   6898                       : ComputeISizeValueFromAspectRatio(
   6899                             aWM, aCBSize, boxSizingAdjust,
   6900                             maxBSizeCoord->AsLengthPercentage(), aspectRatio);
   6901 
   6902    result.ISize(aWM) =
   6903        CSSMinMax(result.ISize(aWM), transferredMinISize, transferredMaxISize);
   6904  }
   6905 
   6906  // Flex items ignore their min & max sizing properties in their flex
   6907  // container's main-axis. (Those properties get applied later in the flexbox
   6908  // algorithm.)
   6909  const bool isFlexItemInlineAxisMainAxis =
   6910      flexItemMainAxis && *flexItemMainAxis == LogicalAxis::Inline;
   6911  // Grid items that are subgridded in inline-axis also ignore their min & max
   6912  // sizing properties in that axis.
   6913  const bool shouldIgnoreMinMaxISize =
   6914      isFlexItemInlineAxisMainAxis || isSubgriddedInInlineAxis;
   6915  const auto maxISizeCoord = stylePos->MaxISize(aWM, anchorResolutionParams);
   6916  nscoord maxISize = NS_UNCONSTRAINEDSIZE;
   6917  if (!maxISizeCoord->IsNone() && !shouldIgnoreMinMaxISize) {
   6918    maxISize =
   6919        ComputeISizeValue(aSizingInput.mRenderingContext, aWM, aCBSize,
   6920                          boxSizingAdjust, boxSizingToMarginEdgeISize,
   6921                          *maxISizeCoord, *styleBSize, aspectRatio, aFlags)
   6922            .mISize;
   6923    result.ISize(aWM) = std::min(maxISize, result.ISize(aWM));
   6924  }
   6925 
   6926  const nscoord bSizeAsPercentageBasis = ComputeBSizeValueAsPercentageBasis(
   6927      *styleBSize, *minBSizeCoord, *maxBSizeCoord, aCBSize.BSize(aWM),
   6928      boxSizingAdjust.BSize(aWM));
   6929  const IntrinsicSizeInput input(
   6930      aSizingInput.mRenderingContext,
   6931      Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
   6932      Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSizeAsPercentageBasis)
   6933               .ConvertTo(GetWritingMode(), aWM)));
   6934  const auto minISizeCoord = stylePos->MinISize(aWM, anchorResolutionParams);
   6935  nscoord minISize;
   6936  if (!minISizeCoord->IsAuto() && !shouldIgnoreMinMaxISize) {
   6937    minISize =
   6938        ComputeISizeValue(aSizingInput.mRenderingContext, aWM, aCBSize,
   6939                          boxSizingAdjust, boxSizingToMarginEdgeISize,
   6940                          *minISizeCoord, *styleBSize, aspectRatio, aFlags)
   6941            .mISize;
   6942  } else if (MOZ_UNLIKELY(
   6943                 aFlags.contains(ComputeSizeFlag::IApplyAutoMinSize))) {
   6944    // This implements "Implied Minimum Size of Grid Items".
   6945    // https://drafts.csswg.org/css-grid/#min-size-auto
   6946    minISize = std::min(maxISize, GetMinISize(input));
   6947    if (styleISize->IsLengthPercentage()) {
   6948      minISize = std::min(minISize, result.ISize(aWM));
   6949    } else if (aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
   6950      // "if the grid item spans only grid tracks that have a fixed max track
   6951      // sizing function, its automatic minimum size in that dimension is
   6952      // further clamped to less than or equal to the size necessary to fit
   6953      // its margin box within the resulting grid area (flooring at zero)"
   6954      // https://drafts.csswg.org/css-grid/#min-size-auto
   6955      auto maxMinISize =
   6956          std::max(nscoord(0), aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) -
   6957                                   aMargin.ISize(aWM));
   6958      minISize = std::min(minISize, maxMinISize);
   6959    }
   6960  } else if (aspectRatioUsage == AspectRatioUsage::ToComputeISize &&
   6961             ShouldApplyAutomaticMinimumOnInlineAxis(
   6962                 aWM, disp->IsScrollableOverflow(), anchorResolutionParams,
   6963                 stylePos)) {
   6964    // This means we successfully applied aspect-ratio and now need to check
   6965    // if we need to apply the automatic content-based minimum size:
   6966    // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
   6967    MOZ_ASSERT(!HasReplacedSizing(),
   6968               "aspect-ratio minimums should not apply to replaced elements");
   6969    // The inline size computed by aspect-ratio shouldn't less than the
   6970    // min-content size, which should be capped by its maximum inline size.
   6971    minISize = std::min(GetMinISize(input), maxISize);
   6972  } else {
   6973    // Treat "min-width: auto" as 0.
   6974    // NOTE: Technically, "auto" is supposed to behave like "min-content" on
   6975    // flex items. However, we don't need to worry about that here, because
   6976    // flex items' min-sizes are intentionally ignored until the flex
   6977    // container explicitly considers them during space distribution.
   6978    minISize = 0;
   6979  }
   6980  result.ISize(aWM) = std::max(minISize, result.ISize(aWM));
   6981 
   6982  // Compute block-axis size
   6983  // (but not if we have auto bsize  -- then, we'll just stick with the bsize
   6984  // that we already calculated in the initial ComputeAutoSize() call. However,
   6985  // if we have a valid preferred aspect ratio, we still have to compute the
   6986  // block size because aspect ratio affects the intrinsic content size.)
   6987  const bool isSubgriddedInBlockAxis =
   6988      isSubgrid && static_cast<nsGridContainerFrame*>(this)->IsRowSubgrid();
   6989 
   6990  // Per https://drafts.csswg.org/css-grid/#subgrid-box-alignment, if we are
   6991  // subgridded in the block-axis, ignore our style block-size, and stretch to
   6992  // fill the CB.
   6993  const bool shouldComputeBSize = !isAutoBSize && !isSubgriddedInBlockAxis;
   6994  if (shouldComputeBSize) {
   6995    result.BSize(aWM) = nsLayoutUtils::ComputeBSizeValue(
   6996        aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
   6997        styleBSize->AsLengthPercentage());
   6998  } else if (MOZ_UNLIKELY(isGridItem) && styleBSize->IsAuto() &&
   6999             !aFlags.contains(ComputeSizeFlag::IsGridMeasuringReflow) &&
   7000             !IsTrueOverflowContainer() &&
   7001             !alignCB->IsMasonry(aWM, LogicalAxis::Block)) {
   7002    auto cbSize = aCBSize.BSize(aWM);
   7003    if (cbSize != NS_UNCONSTRAINEDSIZE) {
   7004      // 'auto' block-size for grid-level box - fill the CB for 'stretch' /
   7005      // 'normal' and clamp it to the CB if requested:
   7006      bool isStretchAligned = false;
   7007      bool mayUseAspectRatio =
   7008          aspectRatio && result.ISize(aWM) != NS_UNCONSTRAINEDSIZE;
   7009      if (!StyleMargin()->HasBlockAxisAuto(aWM, anchorResolutionParams)) {
   7010        auto blockAxisAlignment = stylePos->UsedSelfAlignment(
   7011            aWM, LogicalAxis::Block, alignCB->GetWritingMode(),
   7012            alignCB->Style());
   7013        isStretchAligned = blockAxisAlignment == StyleAlignFlags::STRETCH ||
   7014                           (blockAxisAlignment == StyleAlignFlags::NORMAL &&
   7015                            !mayUseAspectRatio);
   7016      }
   7017 
   7018      // Apply the preferred aspect ratio for alignments other than *stretch*
   7019      // and *normal without aspect ratio*.
   7020      // The spec says all other values should size the items as fit-content,
   7021      // and the intrinsic size should respect the preferred aspect ratio, so
   7022      // we also apply aspect ratio for all other values.
   7023      // https://drafts.csswg.org/css-grid/#grid-item-sizing
   7024      if (!isStretchAligned && mayUseAspectRatio) {
   7025        result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
   7026            LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust);
   7027        MOZ_ASSERT(aspectRatioUsage == AspectRatioUsage::None);
   7028        aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
   7029      }
   7030 
   7031      if (isStretchAligned ||
   7032          aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize)) {
   7033        auto bSizeToFillCB = nsLayoutUtils::ComputeStretchContentBoxBSize(
   7034            cbSize, aMargin.BSize(aWM), aBorderPadding.BSize(aWM));
   7035        if (isStretchAligned || (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
   7036                                 result.BSize(aWM) > bSizeToFillCB)) {
   7037          result.BSize(aWM) = bSizeToFillCB;
   7038        }
   7039      }
   7040    }
   7041  } else if (aspectRatio) {
   7042    // If both inline and block dimensions are auto, the block axis is the
   7043    // ratio-dependent axis by default.
   7044    // If we have a super large inline size, aspect-ratio should still be
   7045    // applied (so aspectRatioUsage flag is set as expected). That's why we
   7046    // apply aspect-ratio unconditionally for auto block size here.
   7047    result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
   7048        LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust);
   7049    MOZ_ASSERT(aspectRatioUsage == AspectRatioUsage::None);
   7050    aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
   7051  }
   7052 
   7053  if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
   7054    // Flex items ignore their min & max sizing properties in their flex
   7055    // container's main-axis. (Those properties get applied later in the flexbox
   7056    // algorithm.)
   7057    const bool isFlexItemBlockAxisMainAxis =
   7058        flexItemMainAxis && *flexItemMainAxis == LogicalAxis::Block;
   7059    // Grid items that are subgridded in block-axis also ignore their min & max
   7060    // sizing properties in that axis.
   7061    const bool shouldIgnoreMinMaxBSize =
   7062        isFlexItemBlockAxisMainAxis || isSubgriddedInBlockAxis;
   7063    if (!isAutoMaxBSize && !shouldIgnoreMinMaxBSize) {
   7064      nscoord maxBSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
   7065          aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
   7066          boxSizingAdjust.BSize(aWM), *maxBSizeCoord);
   7067      result.BSize(aWM) = std::min(maxBSize, result.BSize(aWM));
   7068    }
   7069 
   7070    if (!isAutoMinBSize && !shouldIgnoreMinMaxBSize) {
   7071      nscoord minBSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
   7072          aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
   7073          boxSizingAdjust.BSize(aWM), *minBSizeCoord);
   7074      result.BSize(aWM) = std::max(minBSize, result.BSize(aWM));
   7075    }
   7076  }
   7077 
   7078  if (IsThemed(disp)) {
   7079    nsPresContext* pc = PresContext();
   7080    const LayoutDeviceIntSize widget = pc->Theme()->GetMinimumWidgetSize(
   7081        pc, this, disp->EffectiveAppearance());
   7082 
   7083    // Convert themed widget's physical dimensions to logical coords
   7084    LogicalSize size(aWM, LayoutDeviceIntSize::ToAppUnits(
   7085                              widget, pc->AppUnitsPerDevPixel()));
   7086 
   7087    // GetMinimumWidgetSize() returns border-box; we need content-box.
   7088    size -= aBorderPadding;
   7089 
   7090    if (size.BSize(aWM) > result.BSize(aWM)) {
   7091      result.BSize(aWM) = size.BSize(aWM);
   7092    }
   7093    if (size.ISize(aWM) > result.ISize(aWM)) {
   7094      result.ISize(aWM) = size.ISize(aWM);
   7095    }
   7096  }
   7097 
   7098  result.ISize(aWM) = std::max(0, result.ISize(aWM));
   7099  result.BSize(aWM) = std::max(0, result.BSize(aWM));
   7100 
   7101  return {result, aspectRatioUsage};
   7102 }
   7103 
   7104 nscoord nsIFrame::ComputeBSizeValueAsPercentageBasis(
   7105    const StyleSize& aStyleBSize, const StyleSize& aStyleMinBSize,
   7106    const StyleMaxSize& aStyleMaxBSize, nscoord aCBBSize,
   7107    nscoord aContentEdgeToBoxSizingBSize) {
   7108  if (nsLayoutUtils::IsAutoBSize(aStyleBSize, aCBBSize)) {
   7109    return NS_UNCONSTRAINEDSIZE;
   7110  }
   7111 
   7112  // TODO(dholbert): This is a temporary hack, to be fixed up in bug 1933604.
   7113  // We don't know have aMargin or aBorderPadding args available,
   7114  // so we use these dummy zero-valued variables as placeholders in
   7115  // our call to ComputeBSizeValueHandlingStretch. (This might mean we
   7116  // end up resolving 'stretch' to something slighlty-too-large for the
   7117  // purposes of this call, if there's actually nonzero margin/border/padding).
   7118  const nscoord dummyMargin = 0;
   7119  const nscoord dummyBorderPadding = 0;
   7120 
   7121  const nscoord bSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
   7122      aCBBSize, dummyMargin, dummyBorderPadding, aContentEdgeToBoxSizingBSize,
   7123      aStyleBSize);
   7124 
   7125  const nscoord minBSize =
   7126      nsLayoutUtils::IsAutoBSize(aStyleMinBSize, aCBBSize)
   7127          ? 0
   7128          : nsLayoutUtils::ComputeBSizeValueHandlingStretch(
   7129                aCBBSize, dummyMargin, dummyBorderPadding,
   7130                aContentEdgeToBoxSizingBSize, aStyleMinBSize);
   7131 
   7132  const nscoord maxBSize =
   7133      nsLayoutUtils::IsAutoBSize(aStyleMaxBSize, aCBBSize)
   7134          ? NS_UNCONSTRAINEDSIZE
   7135          : nsLayoutUtils::ComputeBSizeValueHandlingStretch(
   7136                aCBBSize, dummyMargin, dummyBorderPadding,
   7137                aContentEdgeToBoxSizingBSize, aStyleMaxBSize);
   7138 
   7139  return CSSMinMax(bSize, minBSize, maxBSize);
   7140 }
   7141 
   7142 nsRect nsIFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const {
   7143  return InkOverflowRect();
   7144 }
   7145 
   7146 /* virtual */
   7147 nsresult nsIFrame::GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX,
   7148                                           nscoord* aXMost) {
   7149  return NS_ERROR_NOT_IMPLEMENTED;
   7150 }
   7151 
   7152 /* virtual */
   7153 LogicalSize nsIFrame::ComputeAutoSize(
   7154    const SizeComputationInput& aSizingInput, WritingMode aWM,
   7155    const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
   7156    const mozilla::LogicalSize& aMargin,
   7157    const mozilla::LogicalSize& aBorderPadding,
   7158    const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
   7159  if (IsAbsolutelyPositionedWithDefiniteContainingBlock()) {
   7160    return ComputeAbsolutePosAutoSize(aSizingInput, aWM, aCBSize,
   7161                                      aAvailableISize, aMargin, aBorderPadding,
   7162                                      aSizeOverrides, aFlags);
   7163  }
   7164 
   7165  // Use basic shrink-wrapping as a default implementation.
   7166  LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
   7167 
   7168  const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   7169  // don't bother setting it if the result won't be used
   7170  const auto styleISize =
   7171      aSizeOverrides.mStyleISize
   7172          ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
   7173          : StylePosition()->ISize(aWM, anchorResolutionParams);
   7174  if (styleISize->IsAuto()) {
   7175    nscoord availBased = nsLayoutUtils::ComputeStretchContentBoxISize(
   7176        aAvailableISize, aMargin.ISize(aWM), aBorderPadding.ISize(aWM));
   7177    const auto* stylePos = StylePosition();
   7178    const auto styleBSize =
   7179        aSizeOverrides.mStyleBSize
   7180            ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize)
   7181            : stylePos->BSize(aWM, anchorResolutionParams);
   7182    const LogicalSize contentEdgeToBoxSizing =
   7183        stylePos->mBoxSizing == StyleBoxSizing::Border ? aBorderPadding
   7184                                                       : LogicalSize(aWM);
   7185    const nscoord bSize = ComputeBSizeValueAsPercentageBasis(
   7186        *styleBSize, *stylePos->MinBSize(aWM, anchorResolutionParams),
   7187        *stylePos->MaxBSize(aWM, anchorResolutionParams), aCBSize.BSize(aWM),
   7188        contentEdgeToBoxSizing.BSize(aWM));
   7189    const IntrinsicSizeInput input(
   7190        aSizingInput.mRenderingContext,
   7191        Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
   7192        Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSize)
   7193                 .ConvertTo(GetWritingMode(), aWM)));
   7194    result.ISize(aWM) = ShrinkISizeToFit(input, availBased, aFlags);
   7195  }
   7196  return result;
   7197 }
   7198 
   7199 bool nsIFrame::IsAbsolutelyPositionedWithDefiniteContainingBlock() const {
   7200  // TODO(dshin, Bug 1927861): Even if an absolute container should have a
   7201  // definite size, in a continuation context, the full extent of the containing
   7202  // block is not known.
   7203  return MOZ_UNLIKELY(IsAbsolutelyPositioned()) && !GetPrevInFlow();
   7204 }
   7205 
   7206 LogicalSize nsIFrame::ComputeAbsolutePosAutoSize(
   7207    const SizeComputationInput& aSizingInput, WritingMode aWM,
   7208    const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
   7209    const mozilla::LogicalSize& aMargin,
   7210    const mozilla::LogicalSize& aBorderPadding,
   7211    const StyleSizeOverrides& aSizeOverrides, const ComputeSizeFlags& aFlags) {
   7212  MOZ_ASSERT(IsAbsolutelyPositionedWithDefiniteContainingBlock(),
   7213             "Asking for absolute auto size when not absolute");
   7214  // Ideally, this is an assertion, but the containing block could just be
   7215  // really big.
   7216  NS_WARNING_ASSERTION(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE &&
   7217                           aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE,
   7218                       "Absolute containing block size not definite?");
   7219  LogicalSize result(aWM, static_cast<nscoord>(0xdeadbeef),
   7220                     NS_UNCONSTRAINEDSIZE);
   7221 
   7222  const auto* stylePos = StylePosition();
   7223  const auto anchorResolutionParams =
   7224      AnchorPosOffsetResolutionParams::UseCBFrameSize(
   7225          AnchorPosResolutionParams::From(&aSizingInput));
   7226  const auto& styleISize =
   7227      aSizeOverrides.mStyleISize
   7228          ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
   7229          : stylePos->ISize(aWM, anchorResolutionParams.mBaseParams);
   7230  const auto& styleBSize =
   7231      aSizeOverrides.mStyleBSize
   7232          ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize)
   7233          : stylePos->BSize(aWM, anchorResolutionParams.mBaseParams);
   7234  const auto iStartOffsetIsAuto =
   7235      stylePos
   7236          ->GetAnchorResolvedInset(LogicalSide::IStart, aWM,
   7237                                   anchorResolutionParams)
   7238          ->IsAuto();
   7239  const auto iEndOffsetIsAuto =
   7240      stylePos
   7241          ->GetAnchorResolvedInset(LogicalSide::IEnd, aWM,
   7242                                   anchorResolutionParams)
   7243          ->IsAuto();
   7244  const auto bStartOffsetIsAuto =
   7245      stylePos
   7246          ->GetAnchorResolvedInset(LogicalSide::BStart, aWM,
   7247                                   anchorResolutionParams)
   7248          ->IsAuto();
   7249  const auto bEndOffsetIsAuto =
   7250      stylePos
   7251          ->GetAnchorResolvedInset(LogicalSide::BEnd, aWM,
   7252                                   anchorResolutionParams)
   7253          ->IsAuto();
   7254  const auto boxSizingAdjust = stylePos->mBoxSizing == StyleBoxSizing::Border
   7255                                   ? aBorderPadding
   7256                                   : LogicalSize(aWM);
   7257  auto shouldStretch = [](StyleAlignFlags aAlignment, const nsIFrame* aFrame,
   7258                          bool aStartIsAuto, bool aEndIsAuto) {
   7259    if (aStartIsAuto || aEndIsAuto) {
   7260      // Note(dshin, bug 1930427): This is not part of the current spec [1];
   7261      // however, no one implements the new inset behaviour [2], and the old
   7262      // behaviour [3] ends up computing the static size if both or one inset is
   7263      // auto.
   7264      //
   7265      // [1]: https://drafts.csswg.org/css-position-3/#abspos-auto-size
   7266      // [2]: https://drafts.csswg.org/css-position-3/#resolving-insets
   7267      // [3]: https://drafts.csswg.org/css-position-3/#abspos-old
   7268      return false;
   7269    }
   7270    // Don't care about flag bits for auto-sizing.
   7271    aAlignment &= ~StyleAlignFlags::FLAG_BITS;
   7272 
   7273    if (aAlignment == StyleAlignFlags::STRETCH) {
   7274      return true;
   7275    }
   7276 
   7277    if (aAlignment == StyleAlignFlags::NORMAL) {
   7278      // Some replaced elements behave as semi-replaced elements - we want them
   7279      // to stretch (See bug 1740580).
   7280      return !aFrame->HasReplacedSizing() && !aFrame->IsTableWrapperFrame();
   7281    }
   7282 
   7283    return false;
   7284  };
   7285 
   7286  // i.e. Absolute containing block
   7287 
   7288  // Self alignment properties translate `auto` to normal for this purpose.
   7289  // https://drafts.csswg.org/css-align-3/#valdef-justify-self-auto
   7290  nsContainerFrame* contFrame = static_cast<nsContainerFrame*>(this);
   7291  const StylePositionArea posArea = stylePos->mPositionArea;
   7292  const auto containerWM = GetParent()->GetWritingMode();
   7293  auto containerAxis = [&](LogicalAxis aSubjectAxis) {
   7294    return aWM.ConvertAxisTo(aSubjectAxis, containerWM);
   7295  };
   7296  const auto inlineSelfAlign =
   7297      contFrame->CSSAlignmentForAbsPosChildWithinContainingBlock(
   7298          aSizingInput, containerAxis(LogicalAxis::Inline), posArea, aCBSize);
   7299  const auto blockSelfAlign =
   7300      contFrame->CSSAlignmentForAbsPosChildWithinContainingBlock(
   7301          aSizingInput, containerAxis(LogicalAxis::Block), posArea, aCBSize);
   7302  const auto iShouldStretch = shouldStretch(
   7303      inlineSelfAlign, this, iStartOffsetIsAuto, iEndOffsetIsAuto);
   7304  const auto bShouldStretch =
   7305      shouldStretch(blockSelfAlign, this, bStartOffsetIsAuto, bEndOffsetIsAuto);
   7306  const auto iSizeIsAuto = styleISize->IsAuto();
   7307  // Note(dshin, bug 1789477): `auto` in the context of abs-element uses
   7308  // stretch-fit sizing, given specific alignment conditions [1]. Effectively,
   7309  // `auto` is `stretch`. `nsLayoutUtils::IsAutoBSize` is not the right tool
   7310  // here, since the mapping is explicit, and it's incorrect to e.g. map
   7311  // `fit-content` to `stretch`.
   7312  // `-moz-available` behaves like `auto` in general, so map the same way.
   7313  // When Bug 567039 brings `-moz-available` into alignment with `stretch`, this
   7314  // special check can be removed. TODO(dshin): we're probably duplicating the
   7315  // `stretch` logic here, since `stretch` is `stretch-fit` sizing [2].
   7316  //
   7317  // [1]: https://drafts.csswg.org/css-position/#abspos-auto-size
   7318  // [2]: https://drafts.csswg.org/css-sizing-4/#valdef-width-stretch
   7319  const auto bSizeIsAuto = styleBSize->IsAuto() || styleBSize->IsMozAvailable();
   7320  if (bSizeIsAuto && bShouldStretch) {
   7321    result.BSize(aWM) = nsLayoutUtils::ComputeStretchContentBoxBSize(
   7322        aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM));
   7323  }
   7324  if (iSizeIsAuto) {
   7325    if (iShouldStretch) {
   7326      // inline-size to make our margin-box fill the containing block:
   7327      result.ISize(aWM) = nsLayoutUtils::ComputeStretchContentBoxISize(
   7328          aCBSize.ISize(aWM), aMargin.ISize(aWM), aBorderPadding.ISize(aWM));
   7329    } else {
   7330      // inline-size to make our margin-box fill aAvailableISize:
   7331      nscoord availBased = nsLayoutUtils::ComputeStretchContentBoxISize(
   7332          aAvailableISize, aMargin.ISize(aWM), aBorderPadding.ISize(aWM));
   7333 
   7334      const nscoord bSize = ComputeBSizeValueAsPercentageBasis(
   7335          styleBSize->IsAuto() && result.BSize(aWM) != NS_UNCONSTRAINEDSIZE
   7336              ? StyleSize::LengthPercentage(
   7337                    StyleLengthPercentage::FromAppUnits(result.BSize(aWM)))
   7338              : *styleBSize,
   7339          *stylePos->MinBSize(aWM, anchorResolutionParams.mBaseParams),
   7340          *stylePos->MaxBSize(aWM, anchorResolutionParams.mBaseParams),
   7341          aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM));
   7342 
   7343      const IntrinsicSizeInput input(
   7344          aSizingInput.mRenderingContext,
   7345          Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
   7346          Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSize)
   7347                   .ConvertTo(GetWritingMode(), aWM)));
   7348      result.ISize(aWM) = ShrinkISizeToFit(input, availBased, aFlags);
   7349    }
   7350  }
   7351 
   7352  const auto& aspectRatio = aSizeOverrides.mAspectRatio
   7353                                ? *aSizeOverrides.mAspectRatio
   7354                                : GetAspectRatio();
   7355  if (aspectRatio) {
   7356    auto aspectRatioUsage = AspectRatioUsage::None;
   7357    if (iSizeIsAuto != bSizeIsAuto) {
   7358      // Auto axis is dependent.
   7359      if (iSizeIsAuto) {
   7360        aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
   7361      } else {
   7362        aspectRatioUsage = AspectRatioUsage::ToComputeISize;
   7363      }
   7364    } else if (iSizeIsAuto) {
   7365      // Both axes are `auto`.
   7366      if (iShouldStretch != bShouldStretch) {
   7367        // If an axis has stretch, that behaves like a definite size.
   7368        aspectRatioUsage = iShouldStretch ? AspectRatioUsage::ToComputeBSize
   7369                                          : AspectRatioUsage::ToComputeISize;
   7370      } else if (!iShouldStretch) {
   7371        // If one axis has `auto` inset, that is the ratio dependent axis,
   7372        // otherwise the block axis is.
   7373        const bool inlineInsetHasAuto = iStartOffsetIsAuto || iEndOffsetIsAuto;
   7374        const bool blockInsetHasAuto = bStartOffsetIsAuto || bEndOffsetIsAuto;
   7375        aspectRatioUsage = inlineInsetHasAuto && !blockInsetHasAuto
   7376                               ? AspectRatioUsage::ToComputeISize
   7377                               : AspectRatioUsage::ToComputeBSize;
   7378      }
   7379    }
   7380 
   7381    if (aspectRatioUsage == AspectRatioUsage::ToComputeBSize &&
   7382        !bShouldStretch) {
   7383      result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
   7384          LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust);
   7385    } else if (aspectRatioUsage == AspectRatioUsage::ToComputeISize &&
   7386               !iShouldStretch && result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
   7387      result.ISize(aWM) = aspectRatio.ComputeRatioDependentSize(
   7388          LogicalAxis::Inline, aWM, result.BSize(aWM), boxSizingAdjust);
   7389    }
   7390  }
   7391 
   7392  return result;
   7393 }
   7394 
   7395 nscoord nsIFrame::ShrinkISizeToFit(const IntrinsicSizeInput& aInput,
   7396                                   nscoord aISizeInCB,
   7397                                   ComputeSizeFlags aFlags) {
   7398  // If we're a container for font size inflation, then shrink
   7399  // wrapping inside of us should not apply font size inflation.
   7400  AutoMaybeDisableFontInflation an(this);
   7401 
   7402  nscoord result;
   7403  nscoord minISize = GetMinISize(aInput);
   7404  if (minISize > aISizeInCB) {
   7405    const bool clamp = aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize);
   7406    result = MOZ_UNLIKELY(clamp) ? aISizeInCB : minISize;
   7407  } else {
   7408    nscoord prefISize = GetPrefISize(aInput);
   7409    if (prefISize > aISizeInCB) {
   7410      result = aISizeInCB;
   7411    } else {
   7412      result = prefISize;
   7413    }
   7414  }
   7415  return result;
   7416 }
   7417 
   7418 nscoord nsIFrame::IntrinsicISizeFromInline(const IntrinsicSizeInput& aInput,
   7419                                           IntrinsicISizeType aType) {
   7420  MOZ_ASSERT(!IsContainerForFontSizeInflation(),
   7421             "Should not be a container for font size inflation!");
   7422 
   7423  if (aType == IntrinsicISizeType::MinISize) {
   7424    InlineMinISizeData data;
   7425    AddInlineMinISize(aInput, &data);
   7426    data.ForceBreak();
   7427    return data.mPrevLines;
   7428  }
   7429 
   7430  InlinePrefISizeData data;
   7431  AddInlinePrefISize(aInput, &data);
   7432  data.ForceBreak();
   7433  return data.mPrevLines;
   7434 }
   7435 
   7436 nscoord nsIFrame::ComputeISizeValueFromAspectRatio(
   7437    WritingMode aWM, const LogicalSize& aCBSize,
   7438    const LogicalSize& aContentEdgeToBoxSizing, const LengthPercentage& aBSize,
   7439    const AspectRatio& aAspectRatio) const {
   7440  MOZ_ASSERT(aAspectRatio, "Must have a valid AspectRatio!");
   7441  const nscoord bSize = nsLayoutUtils::ComputeBSizeValue(
   7442      aCBSize.BSize(aWM), aContentEdgeToBoxSizing.BSize(aWM), aBSize);
   7443  return aAspectRatio.ComputeRatioDependentSize(LogicalAxis::Inline, aWM, bSize,
   7444                                                aContentEdgeToBoxSizing);
   7445 }
   7446 
   7447 nsIFrame::ISizeComputationResult nsIFrame::ComputeISizeValue(
   7448    gfxContext* aRenderingContext, const WritingMode aWM,
   7449    const LogicalSize& aCBSize, const LogicalSize& aContentEdgeToBoxSizing,
   7450    nscoord aBoxSizingToMarginEdge, ExtremumLength aSize,
   7451    Maybe<nscoord> aAvailableISizeOverride, const StyleSize& aStyleBSize,
   7452    const AspectRatio& aAspectRatio, ComputeSizeFlags aFlags) {
   7453  auto GetAvailableISize = [&]() {
   7454    return aCBSize.ISize(aWM) - aBoxSizingToMarginEdge -
   7455           aContentEdgeToBoxSizing.ISize(aWM);
   7456  };
   7457 
   7458  // If 'this' is a container for font size inflation, then shrink
   7459  // wrapping inside of it should not apply font size inflation.
   7460  AutoMaybeDisableFontInflation an(this);
   7461  // If we have an aspect-ratio and a definite block size, we should use them to
   7462  // resolve the sizes with intrinsic keywords.
   7463  // https://github.com/w3c/csswg-drafts/issues/5032
   7464  Maybe<nscoord> iSizeFromAspectRatio = [&]() -> Maybe<nscoord> {
   7465    if (aSize == ExtremumLength::MozAvailable ||
   7466        aSize == ExtremumLength::Stretch) {
   7467      return Nothing();
   7468    }
   7469    if (!aAspectRatio) {
   7470      return Nothing();
   7471    }
   7472    if (nsLayoutUtils::IsAutoBSize(aStyleBSize, aCBSize.BSize(aWM))) {
   7473      return Nothing();
   7474    }
   7475 
   7476    // Helper used below to resolve aStyleBSize if it's 'stretch' or an alias.
   7477    // XXXdholbert Really we should be resolving 'stretch' and its aliases
   7478    // sooner; see bug 2000035.
   7479    auto ResolveStretchBSize = [&]() {
   7480      MOZ_ASSERT(aStyleBSize.BehavesLikeStretchOnBlockAxis(),
   7481                 "Only call me for 'stretch'-like BSizes");
   7482      MOZ_ASSERT(aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE,
   7483                 "If aStyleBSize is stretch-like, then unconstrained "
   7484                 "aCBSize.BSize should make us return via the IsAutoBSize "
   7485                 "check above");
   7486 
   7487      // NOTE: the borderPadding and margin variables might be zero-filled
   7488      // instead of having the true values, if those values haven't been
   7489      // stashed in our property-table yet (e.g. if we're in the midst of
   7490      // setting up a ReflowInput for our first reflow). So ideally, we should
   7491      // be resolving 'stretch' **in our callers** rather than here, if those
   7492      // callers have more up-to-date resolved margin/border/padding values.
   7493      // We'll still make a best-effort attempt to resolve 'stretch' here,
   7494      // though, for the benefit of callers that might not have handled it, to
   7495      // be sure we don't abort in aStyleBSize.AsLengthPercentage(). Ultimately
   7496      // this all can be removed when we fix bug 2000035.
   7497      const auto borderPadding = GetLogicalUsedBorderAndPadding(aWM);
   7498      const auto margin = GetLogicalUsedMargin(aWM);
   7499      nscoord stretchBSize = nsLayoutUtils::ComputeStretchBSize(
   7500          aCBSize.BSize(aWM), margin.BStartEnd(aWM),
   7501          borderPadding.BStartEnd(aWM), StylePosition()->mBoxSizing);
   7502      return LengthPercentage::FromAppUnits(stretchBSize);
   7503    };
   7504 
   7505    return Some(ComputeISizeValueFromAspectRatio(
   7506        aWM, aCBSize, aContentEdgeToBoxSizing,
   7507        aStyleBSize.BehavesLikeStretchOnBlockAxis()
   7508            ? ResolveStretchBSize()
   7509            : aStyleBSize.AsLengthPercentage(),
   7510        aAspectRatio));
   7511  }();
   7512 
   7513  const auto* stylePos = StylePosition();
   7514  const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   7515  const nscoord bSize = ComputeBSizeValueAsPercentageBasis(
   7516      aStyleBSize, *stylePos->MinBSize(aWM, anchorResolutionParams),
   7517      *stylePos->MaxBSize(aWM, anchorResolutionParams), aCBSize.BSize(aWM),
   7518      aContentEdgeToBoxSizing.BSize(aWM));
   7519  const IntrinsicSizeInput input(
   7520      aRenderingContext, Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
   7521      Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSize)
   7522               .ConvertTo(GetWritingMode(), aWM)));
   7523  nscoord result;
   7524  switch (aSize) {
   7525    case ExtremumLength::MaxContent:
   7526      result =
   7527          iSizeFromAspectRatio ? *iSizeFromAspectRatio : GetPrefISize(input);
   7528      NS_ASSERTION(result >= 0, "inline-size less than zero");
   7529      return {result, iSizeFromAspectRatio ? AspectRatioUsage::ToComputeISize
   7530                                           : AspectRatioUsage::None};
   7531    case ExtremumLength::MinContent:
   7532      result =
   7533          iSizeFromAspectRatio ? *iSizeFromAspectRatio : GetMinISize(input);
   7534      NS_ASSERTION(result >= 0, "inline-size less than zero");
   7535      if (MOZ_UNLIKELY(
   7536              aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize))) {
   7537        result = std::min(GetAvailableISize(), result);
   7538      }
   7539      return {result, iSizeFromAspectRatio ? AspectRatioUsage::ToComputeISize
   7540                                           : AspectRatioUsage::None};
   7541    case ExtremumLength::FitContentFunction:
   7542    case ExtremumLength::FitContent: {
   7543      nscoord pref = NS_UNCONSTRAINEDSIZE;
   7544      nscoord min = 0;
   7545      if (iSizeFromAspectRatio) {
   7546        // The min-content and max-content size are identical and equal to the
   7547        // size computed from the block size and the aspect ratio.
   7548        pref = min = *iSizeFromAspectRatio;
   7549      } else {
   7550        pref = GetPrefISize(input);
   7551        min = GetMinISize(input);
   7552      }
   7553 
   7554      const nscoord fill = aAvailableISizeOverride ? *aAvailableISizeOverride
   7555                                                   : GetAvailableISize();
   7556      if (MOZ_UNLIKELY(
   7557              aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize))) {
   7558        min = std::min(min, fill);
   7559      }
   7560      result = std::max(min, std::min(pref, fill));
   7561      NS_ASSERTION(result >= 0, "inline-size less than zero");
   7562      return {result};
   7563    }
   7564    case ExtremumLength::MozAvailable:
   7565    case ExtremumLength::Stretch:
   7566      return {GetAvailableISize()};
   7567  }
   7568  MOZ_ASSERT_UNREACHABLE("Unknown extremum length?");
   7569  return {};
   7570 }
   7571 
   7572 nscoord nsIFrame::ComputeISizeValue(const WritingMode aWM,
   7573                                    const LogicalSize& aCBSize,
   7574                                    const LogicalSize& aContentEdgeToBoxSizing,
   7575                                    const LengthPercentage& aSize) const {
   7576  LAYOUT_WARN_IF_FALSE(
   7577      aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,
   7578      "have unconstrained inline-size; this should only result from "
   7579      "very large sizes, not attempts at intrinsic inline-size "
   7580      "calculation");
   7581  NS_ASSERTION(aCBSize.ISize(aWM) >= 0, "inline-size less than zero");
   7582 
   7583  nscoord result = aSize.Resolve(aCBSize.ISize(aWM));
   7584  // The result of a calc() expression might be less than 0; we
   7585  // should clamp at runtime (below).  (Percentages and coords that
   7586  // are less than 0 have already been dropped by the parser.)
   7587  result -= aContentEdgeToBoxSizing.ISize(aWM);
   7588  return std::max(0, result);
   7589 }
   7590 
   7591 void nsIFrame::DidReflow(nsPresContext* aPresContext,
   7592                         const ReflowInput* aReflowInput) {
   7593  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsIFrame::DidReflow"));
   7594 
   7595  if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
   7596    RemoveStateBits(NS_FRAME_IN_REFLOW);
   7597    return;
   7598  }
   7599 
   7600  SVGObserverUtils::InvalidateDirectRenderingObservers(
   7601      this, SVGObserverUtils::INVALIDATE_REFLOW);
   7602 
   7603  RemoveStateBits(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW |
   7604                  NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
   7605 
   7606  // Clear bits that were used in ReflowInput::InitResizeFlags (see
   7607  // comment there for why we can't clear it there).
   7608  SetHasBSizeChange(false);
   7609  SetHasPaddingChange(false);
   7610 
   7611  // Notify the percent bsize observer if there is a percent bsize.
   7612  // The observer may be able to initiate another reflow with a computed
   7613  // bsize. This happens in the case where a table cell has no computed
   7614  // bsize but can fabricate one when the cell bsize is known.
   7615  if (aReflowInput && aReflowInput->mPercentBSizeObserver && !GetPrevInFlow()) {
   7616    const auto bsize = aReflowInput->mStylePosition->BSize(
   7617        aReflowInput->GetWritingMode(),
   7618        AnchorPosResolutionParams::From(aReflowInput));
   7619    if (bsize->HasPercent()) {
   7620      aReflowInput->mPercentBSizeObserver->NotifyPercentBSize(*aReflowInput);
   7621    }
   7622  }
   7623 
   7624  aPresContext->ReflowedFrame();
   7625 }
   7626 
   7627 /* virtual */
   7628 bool nsIFrame::CanContinueTextRun() const {
   7629  // By default, a frame will *not* allow a text run to be continued
   7630  // through it.
   7631  return false;
   7632 }
   7633 
   7634 void nsIFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
   7635                      const ReflowInput& aReflowInput,
   7636                      nsReflowStatus& aStatus) {
   7637  MarkInReflow();
   7638  DO_GLOBAL_REFLOW_COUNT("nsFrame");
   7639  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
   7640  aDesiredSize.ClearSize();
   7641 }
   7642 
   7643 bool nsIFrame::IsContentDisabled() const {
   7644  auto* element = nsGenericHTMLElement::FromNodeOrNull(GetContent());
   7645  return element && element->IsDisabled();
   7646 }
   7647 
   7648 bool nsIFrame::IsContentRelevant() const {
   7649  MOZ_ASSERT(StyleDisplay()->ContentVisibility(*this) ==
   7650             StyleContentVisibility::Auto);
   7651 
   7652  auto* element = Element::FromNodeOrNull(GetContent());
   7653  MOZ_ASSERT(element);
   7654 
   7655  Maybe<ContentRelevancy> relevancy = element->GetContentRelevancy();
   7656  return relevancy.isSome() && !relevancy->isEmpty();
   7657 }
   7658 
   7659 bool nsIFrame::HidesContent(
   7660    const EnumSet<IncludeContentVisibility>& aInclude) const {
   7661  auto effectiveContentVisibility = StyleDisplay()->ContentVisibility(*this);
   7662  if (aInclude.contains(IncludeContentVisibility::Hidden) &&
   7663      effectiveContentVisibility == StyleContentVisibility::Hidden) {
   7664    return true;
   7665  }
   7666 
   7667  if (aInclude.contains(IncludeContentVisibility::Auto) &&
   7668      effectiveContentVisibility == StyleContentVisibility::Auto) {
   7669    return !IsContentRelevant();
   7670  }
   7671 
   7672  return false;
   7673 }
   7674 
   7675 bool nsIFrame::HidesContentForLayout() const {
   7676  return HidesContent() && !PresShell()->IsForcingLayoutForHiddenContent(this);
   7677 }
   7678 
   7679 bool nsIFrame::IsHiddenByContentVisibilityOfInFlowParentForLayout() const {
   7680  const auto* parent = GetInFlowParent();
   7681  // The anonymous children owned by parent are important for properly sizing
   7682  // their parents.
   7683  return parent && parent->HidesContentForLayout() &&
   7684         !(parent->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES) &&
   7685           Style()->IsAnonBox());
   7686 }
   7687 
   7688 nsIFrame* nsIFrame::GetClosestContentVisibilityAncestor(
   7689    const EnumSet<IncludeContentVisibility>& aInclude) const {
   7690  auto* parent = GetInFlowParent();
   7691  bool isAnonymousBlock = Style()->IsAnonBox() && parent &&
   7692                          parent->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES);
   7693  for (nsIFrame* cur = parent; cur; cur = cur->GetInFlowParent()) {
   7694    if (!isAnonymousBlock && cur->HidesContent(aInclude)) {
   7695      return cur;
   7696    }
   7697 
   7698    // Anonymous boxes are not hidden by the content-visibility of their first
   7699    // non-anonymous ancestor, but can be hidden by ancestors further up the
   7700    // tree.
   7701    isAnonymousBlock = false;
   7702  }
   7703 
   7704  return nullptr;
   7705 }
   7706 
   7707 static bool IsClosedDetailsSlot(const Element* aElement) {
   7708  const auto* slot = HTMLSlotElement::FromNodeOrNull(aElement);
   7709  if (!slot || slot->HasName()) {
   7710    return false;
   7711  }
   7712  const auto* details =
   7713      HTMLDetailsElement::FromNodeOrNull(slot->GetContainingShadowHost());
   7714  return details && !details->GetBoolAttr(nsGkAtoms::open);
   7715 }
   7716 
   7717 bool nsIFrame::IsHiddenUntilFoundOrClosedDetails() const {
   7718  for (const auto* f = this; f; f = f->GetInFlowParent()) {
   7719    if (f->HidesContent(nsIFrame::IncludeContentVisibility::Hidden)) {
   7720      if (const auto* element = Element::FromNode(f->GetContent());
   7721          element &&
   7722          !element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
   7723                                nsGkAtoms::untilFound, eIgnoreCase) &&
   7724          !IsClosedDetailsSlot(element)) {
   7725        return false;
   7726      }
   7727    }
   7728  }
   7729  return true;
   7730 }
   7731 
   7732 bool nsIFrame::IsHiddenByContentVisibilityOnAnyAncestor(
   7733    const EnumSet<IncludeContentVisibility>& aInclude) const {
   7734  return !!GetClosestContentVisibilityAncestor(aInclude);
   7735 }
   7736 
   7737 bool nsIFrame::HasSelectionInSubtree() {
   7738  if (IsSelected()) {
   7739    return true;
   7740  }
   7741 
   7742  RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
   7743  if (!frameSelection) {
   7744    return false;
   7745  }
   7746 
   7747  const Selection& selection = frameSelection->NormalSelection();
   7748 
   7749  for (uint32_t i = 0; i < selection.RangeCount(); i++) {
   7750    auto* range = selection.GetRangeAt(i);
   7751    MOZ_ASSERT(range);
   7752 
   7753    const auto* commonAncestorNode =
   7754        range->GetRegisteredClosestCommonInclusiveAncestor();
   7755    if (commonAncestorNode &&
   7756        commonAncestorNode->IsInclusiveDescendantOf(GetContent())) {
   7757      return true;
   7758    }
   7759  }
   7760 
   7761  return false;
   7762 }
   7763 
   7764 bool nsIFrame::UpdateIsRelevantContent(
   7765    const ContentRelevancy& aRelevancyToUpdate) {
   7766  MOZ_ASSERT(StyleDisplay()->ContentVisibility(*this) ==
   7767             StyleContentVisibility::Auto);
   7768 
   7769  auto* element = Element::FromNodeOrNull(GetContent());
   7770  MOZ_ASSERT(element);
   7771 
   7772  ContentRelevancy newRelevancy;
   7773  Maybe<ContentRelevancy> oldRelevancy = element->GetContentRelevancy();
   7774  if (oldRelevancy.isSome()) {
   7775    newRelevancy = *oldRelevancy;
   7776  }
   7777 
   7778  auto setRelevancyValue = [&](ContentRelevancyReason reason, bool value) {
   7779    if (value) {
   7780      newRelevancy += reason;
   7781    } else {
   7782      newRelevancy -= reason;
   7783    }
   7784  };
   7785 
   7786  if (!oldRelevancy ||
   7787      aRelevancyToUpdate.contains(ContentRelevancyReason::Visible)) {
   7788    Maybe<bool> visible = element->GetVisibleForContentVisibility();
   7789    if (visible.isSome()) {
   7790      setRelevancyValue(ContentRelevancyReason::Visible, *visible);
   7791    }
   7792  }
   7793 
   7794  if (!oldRelevancy ||
   7795      aRelevancyToUpdate.contains(ContentRelevancyReason::FocusInSubtree)) {
   7796    setRelevancyValue(ContentRelevancyReason::FocusInSubtree,
   7797                      element->State().HasAtLeastOneOfStates(
   7798                          ElementState::FOCUS_WITHIN | ElementState::FOCUS));
   7799  }
   7800 
   7801  if (!oldRelevancy ||
   7802      aRelevancyToUpdate.contains(ContentRelevancyReason::Selected)) {
   7803    setRelevancyValue(ContentRelevancyReason::Selected,
   7804                      HasSelectionInSubtree());
   7805  }
   7806 
   7807  // If the proximity to the viewport has not been determined yet,
   7808  // and neither the element nor its contents are focused or selected,
   7809  // we should wait for the determination of the proximity. Otherwise,
   7810  // there might be a redundant contentvisibilityautostatechange event.
   7811  // See https://github.com/w3c/csswg-drafts/issues/9803
   7812  bool isProximityToViewportDetermined =
   7813      oldRelevancy ? true : element->GetVisibleForContentVisibility().isSome();
   7814  if (!isProximityToViewportDetermined && newRelevancy.isEmpty()) {
   7815    return false;
   7816  }
   7817 
   7818  bool overallRelevancyChanged =
   7819      !oldRelevancy || oldRelevancy->isEmpty() != newRelevancy.isEmpty();
   7820  if (!oldRelevancy || *oldRelevancy != newRelevancy) {
   7821    element->SetContentRelevancy(newRelevancy);
   7822  }
   7823 
   7824  if (!overallRelevancyChanged) {
   7825    return false;
   7826  }
   7827 
   7828  HandleLastRememberedSize();
   7829  PresContext()->SetNeedsToUpdateHiddenByContentVisibilityForAnimations();
   7830  PresShell()->FrameNeedsReflow(
   7831      this, IntrinsicDirty::FrameAncestorsAndDescendants, NS_FRAME_IS_DIRTY);
   7832  InvalidateFrame();
   7833 
   7834  ContentVisibilityAutoStateChangeEventInit init;
   7835  init.mSkipped = newRelevancy.isEmpty();
   7836  RefPtr<ContentVisibilityAutoStateChangeEvent> event =
   7837      ContentVisibilityAutoStateChangeEvent::Constructor(
   7838          element, u"contentvisibilityautostatechange"_ns, init);
   7839 
   7840  // Per
   7841  // https://drafts.csswg.org/css-contain/#content-visibility-auto-state-changed
   7842  // "This event is dispatched by posting a task at the time when the state
   7843  // change occurs."
   7844  RefPtr<AsyncEventDispatcher> asyncDispatcher =
   7845      new AsyncEventDispatcher(element, event.forget());
   7846  DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
   7847  NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch");
   7848  return true;
   7849 }
   7850 
   7851 nsresult nsIFrame::CharacterDataChanged(const CharacterDataChangeInfo&) {
   7852  MOZ_ASSERT_UNREACHABLE("should only be called for text frames");
   7853  return NS_OK;
   7854 }
   7855 
   7856 nsresult nsIFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
   7857                                    AttrModType) {
   7858  return NS_OK;
   7859 }
   7860 
   7861 nsIFrame* nsIFrame::GetPrevContinuation() const { return nullptr; }
   7862 
   7863 void nsIFrame::SetPrevContinuation(nsIFrame*) {
   7864  MOZ_ASSERT_UNREACHABLE("Not splittable!");
   7865 }
   7866 
   7867 nsIFrame* nsIFrame::GetNextContinuation() const { return nullptr; }
   7868 
   7869 void nsIFrame::SetNextContinuation(nsIFrame*) {
   7870  MOZ_ASSERT_UNREACHABLE("Not splittable!");
   7871 }
   7872 
   7873 nsIFrame* nsIFrame::GetPrevInFlow() const { return nullptr; }
   7874 
   7875 void nsIFrame::SetPrevInFlow(nsIFrame*) {
   7876  MOZ_ASSERT_UNREACHABLE("Not splittable!");
   7877 }
   7878 
   7879 nsIFrame* nsIFrame::GetNextInFlow() const { return nullptr; }
   7880 
   7881 void nsIFrame::SetNextInFlow(nsIFrame*) {
   7882  MOZ_ASSERT_UNREACHABLE("Not splittable!");
   7883 }
   7884 
   7885 nsIFrame* nsIFrame::GetTailContinuation() {
   7886  nsIFrame* frame = this;
   7887  while (frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   7888    frame = frame->GetPrevContinuation();
   7889    NS_ASSERTION(frame, "first continuation can't be overflow container");
   7890  }
   7891  for (nsIFrame* next = frame->GetNextContinuation();
   7892       next && !next->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
   7893       next = frame->GetNextContinuation()) {
   7894    frame = next;
   7895  }
   7896 
   7897  MOZ_ASSERT(frame, "illegal state in continuation chain.");
   7898  return frame;
   7899 }
   7900 
   7901 nsIWidget* nsIFrame::GetOwnWidget() const {
   7902  if (IsMenuPopupFrame()) {
   7903    return static_cast<const nsMenuPopupFrame*>(this)->GetWidget();
   7904  }
   7905  if (!GetParent()) {
   7906    return PresShell()->GetOwnWidget();
   7907  }
   7908  return nullptr;
   7909 }
   7910 
   7911 template <nsPoint (nsIFrame::*PositionGetter)() const>
   7912 static nsPoint OffsetCalculator(const nsIFrame* aThis, const nsIFrame* aOther) {
   7913  MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!");
   7914 
   7915  NS_ASSERTION(aThis->PresContext() == aOther->PresContext(),
   7916               "GetOffsetTo called on frames in different documents");
   7917 
   7918  nsPoint offset(0, 0);
   7919  const nsIFrame* f;
   7920  for (f = aThis; f != aOther && f; f = f->GetParent()) {
   7921    offset += (f->*PositionGetter)();
   7922  }
   7923 
   7924  if (f != aOther) {
   7925    // Looks like aOther wasn't an ancestor of |this|.  So now we have
   7926    // the root-frame-relative position of |this| in |offset|.  Convert back
   7927    // to the coordinates of aOther
   7928    while (aOther) {
   7929      offset -= (aOther->*PositionGetter)();
   7930      aOther = aOther->GetParent();
   7931    }
   7932  }
   7933 
   7934  return offset;
   7935 }
   7936 
   7937 nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const {
   7938  return OffsetCalculator<&nsIFrame::GetPosition>(this, aOther);
   7939 }
   7940 
   7941 nsPoint nsIFrame::GetOffsetToRootFrame() const {
   7942  return GetOffsetTo(PresShell()->GetRootFrame());
   7943 }
   7944 
   7945 nsPoint nsIFrame::GetOffsetToIgnoringScrolling(const nsIFrame* aOther) const {
   7946  return OffsetCalculator<&nsIFrame::GetPositionIgnoringScrolling>(this,
   7947                                                                   aOther);
   7948 }
   7949 
   7950 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const {
   7951  return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
   7952 }
   7953 
   7954 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther,
   7955                                      const int32_t aAPD) const {
   7956  MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!");
   7957  MOZ_DIAGNOSTIC_ASSERT(
   7958      PresContext()->GetRootPresContext() ==
   7959          aOther->PresContext()->GetRootPresContext(),
   7960      "trying to get the offset between frames in different document "
   7961      "hierarchies?");
   7962 
   7963  const nsIFrame* root = nullptr;
   7964  // offset will hold the final offset
   7965  // docOffset holds the currently accumulated offset at the current APD, it
   7966  // will be converted and added to offset when the current APD changes.
   7967  nsPoint offset(0, 0), docOffset(0, 0);
   7968  const nsIFrame* f = this;
   7969  int32_t currAPD = PresContext()->AppUnitsPerDevPixel();
   7970  while (f && f != aOther) {
   7971    docOffset += f->GetPosition();
   7972    nsIFrame* parent = f->GetParent();
   7973    if (parent) {
   7974      f = parent;
   7975    } else {
   7976      nsPoint newOffset(0, 0);
   7977      root = f;
   7978      f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f, &newOffset);
   7979      int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
   7980      if (!f || newAPD != currAPD) {
   7981        // Convert docOffset to the right APD and add it to offset.
   7982        offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
   7983        docOffset.x = docOffset.y = 0;
   7984      }
   7985      currAPD = newAPD;
   7986      docOffset += newOffset;
   7987    }
   7988  }
   7989  if (f == aOther) {
   7990    offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
   7991  } else {
   7992    // Looks like aOther wasn't an ancestor of |this|.  So now we have
   7993    // the root-document-relative position of |this| in |offset|. Subtract the
   7994    // root-document-relative position of |aOther| from |offset|.
   7995    // This call won't try to recurse again because root is an ancestor of
   7996    // aOther.
   7997    nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
   7998    offset -= negOffset;
   7999  }
   8000 
   8001  return offset;
   8002 }
   8003 
   8004 CSSIntRect nsIFrame::GetScreenRect() const {
   8005  return CSSIntRect::FromAppUnitsToNearest(GetScreenRectInAppUnits());
   8006 }
   8007 
   8008 nsRect nsIFrame::GetScreenRectInAppUnits() const {
   8009  nsPresContext* presContext = PresContext();
   8010  nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
   8011  nsPoint rootScreenPos(0, 0);
   8012  nsPoint rootFrameOffsetInParent(0, 0);
   8013  nsIFrame* rootFrameParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(
   8014      rootFrame, &rootFrameOffsetInParent);
   8015  if (rootFrameParent) {
   8016    nsRect parentScreenRectAppUnits =
   8017        rootFrameParent->GetScreenRectInAppUnits();
   8018    nsPresContext* parentPresContext = rootFrameParent->PresContext();
   8019    double parentScale = double(presContext->AppUnitsPerDevPixel()) /
   8020                         parentPresContext->AppUnitsPerDevPixel();
   8021    nsPoint rootPt =
   8022        parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent;
   8023    rootScreenPos.x = NS_round(parentScale * rootPt.x);
   8024    rootScreenPos.y = NS_round(parentScale * rootPt.y);
   8025  } else if (nsCOMPtr<nsIWidget> rootWidget = presContext->GetRootWidget()) {
   8026    LayoutDeviceIntPoint rootDevPx = rootWidget->WidgetToScreenOffset();
   8027    rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x);
   8028    rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y);
   8029  }
   8030 
   8031  return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize());
   8032 }
   8033 
   8034 nsIWidget* nsIFrame::GetNearestWidget() const {
   8035  if (!HasAnyStateBits(NS_FRAME_IN_POPUP)) {
   8036    return PresContext()->GetRootWidget();
   8037  }
   8038  nsPoint unused;
   8039  return GetNearestWidget(unused);
   8040 }
   8041 
   8042 nsIWidget* nsIFrame::GetNearestWidget(nsPoint& aOffset) const {
   8043  aOffset.MoveTo(0, 0);
   8044  nsIFrame* frame = const_cast<nsIFrame*>(this);
   8045  const auto targetAPD = PresContext()->AppUnitsPerDevPixel();
   8046  auto curAPD = targetAPD;
   8047  do {
   8048    if (auto* widget = frame->GetOwnWidget()) {
   8049      aOffset = aOffset.ScaleToOtherAppUnits(curAPD, targetAPD);
   8050      return widget;
   8051    }
   8052    aOffset += frame->GetPosition();
   8053    nsPoint crossDocOffset;
   8054    frame =
   8055        nsLayoutUtils::GetCrossDocParentFrameInProcess(frame, &crossDocOffset);
   8056    if (!frame) {
   8057      break;
   8058    }
   8059    auto newAPD = frame->PresContext()->AppUnitsPerDevPixel();
   8060    aOffset = aOffset.ScaleToOtherAppUnits(curAPD, newAPD);
   8061    aOffset += crossDocOffset;
   8062    curAPD = newAPD;
   8063  } while (true);
   8064  aOffset = aOffset.ScaleToOtherAppUnits(curAPD, targetAPD);
   8065  return PresContext()->GetRootWidget();
   8066 }
   8067 
   8068 Matrix4x4Flagged nsIFrame::GetTransformMatrix(ViewportType aViewportType,
   8069                                              RelativeTo aStopAtAncestor,
   8070                                              nsIFrame** aOutAncestor,
   8071                                              uint32_t aFlags) const {
   8072  MOZ_ASSERT(aOutAncestor, "Need a place to put the ancestor!");
   8073 
   8074  /* If we're transformed, we want to hand back the combination
   8075   * transform/translate matrix that will apply our current transform, then
   8076   * shift us to our parent.
   8077   */
   8078  const bool isTransformed = IsTransformed();
   8079  const nsIFrame* zoomedContentRoot = nullptr;
   8080  if (aStopAtAncestor.mViewportType == ViewportType::Visual) {
   8081    zoomedContentRoot = ViewportUtils::IsZoomedContentRoot(this);
   8082    if (zoomedContentRoot) {
   8083      MOZ_ASSERT(aViewportType != ViewportType::Visual);
   8084    }
   8085  }
   8086 
   8087  if (isTransformed || zoomedContentRoot) {
   8088    MOZ_ASSERT(GetParent());
   8089    Matrix4x4Flagged result;
   8090    int32_t scaleFactor =
   8091        ((aFlags & IN_CSS_UNITS) ? AppUnitsPerCSSPixel()
   8092                                 : PresContext()->AppUnitsPerDevPixel());
   8093 
   8094    /* Compute the delta to the parent, which we need because we are converting
   8095     * coordinates to our parent.
   8096     */
   8097    if (isTransformed) {
   8098      // Note: this converts from Matrix4x4 to Matrix4x4Flagged.
   8099      result = nsDisplayTransform::GetResultingTransformMatrix(
   8100          this, nsPoint(), scaleFactor,
   8101          nsDisplayTransform::INCLUDE_PERSPECTIVE);
   8102    }
   8103 
   8104    // The offset from a zoomed content root to its parent (e.g. from
   8105    // a canvas frame to a scroll frame) is in layout coordinates, so
   8106    // apply it before applying any layout-to-visual transform.
   8107    *aOutAncestor = GetParent();
   8108    nsPoint delta = GetPosition();
   8109    /* Combine the raw transform with a translation to our parent. */
   8110    result.PostTranslate(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
   8111                         NSAppUnitsToFloatPixels(delta.y, scaleFactor), 0.0f);
   8112 
   8113    if (zoomedContentRoot) {
   8114      Matrix4x4Flagged layoutToVisual;
   8115      if (aFlags & nsIFrame::IN_CSS_UNITS) {
   8116        layoutToVisual = ViewportUtils::GetVisualToLayoutTransform(
   8117                             zoomedContentRoot->GetContent())
   8118                             .Inverse()
   8119                             .ToUnknownMatrix();
   8120      } else {
   8121        layoutToVisual =
   8122            ViewportUtils::GetVisualToLayoutTransform<LayoutDevicePixel>(
   8123                zoomedContentRoot->GetContent())
   8124                .Inverse()
   8125                .ToUnknownMatrix();
   8126      }
   8127      result = result * layoutToVisual;
   8128    }
   8129 
   8130    return result;
   8131  }
   8132 
   8133  // We are not transformed, so the returned transform is just going to be a
   8134  // translation up to whatever ancestor we decide to stop at.
   8135 
   8136  nsPoint crossdocOffset;
   8137  *aOutAncestor =
   8138      nsLayoutUtils::GetCrossDocParentFrameInProcess(this, &crossdocOffset);
   8139 
   8140  /* Otherwise, we're not transformed.  In that case, we'll walk up the frame
   8141   * tree until we either hit the root frame or something that may be
   8142   * transformed.  We'll then change coordinates into that frame, since we're
   8143   * guaranteed that nothing in-between can be transformed.  First, however,
   8144   * we have to check to see if we have a parent.  If not, we'll set the
   8145   * outparam to null (indicating that there's nothing left) and will hand back
   8146   * the identity matrix.
   8147   */
   8148  if (!*aOutAncestor) {
   8149    return Matrix4x4Flagged();
   8150  }
   8151 
   8152  /* Keep iterating while the frame can't possibly be transformed. */
   8153  const nsIFrame* current = this;
   8154  auto shouldStopAt = [](const nsIFrame* aCurrent, RelativeTo& aStopAtAncestor,
   8155                         nsIFrame* aOutAncestor, uint32_t aFlags) {
   8156    return aOutAncestor->IsTransformed() ||
   8157           ((aStopAtAncestor.mViewportType == ViewportType::Visual) &&
   8158            ViewportUtils::IsZoomedContentRoot(aOutAncestor)) ||
   8159           ((aFlags & STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) &&
   8160            (aOutAncestor->IsStackingContext() ||
   8161             DisplayPortUtils::FrameHasDisplayPort(aOutAncestor, aCurrent)));
   8162  };
   8163 
   8164  // We run the GetOffsetToCrossDoc code here as an optimization, instead of
   8165  // walking the parent chain here and then asking GetOffsetToCrossDoc to walk
   8166  // the same parent chain and compute the offset.
   8167  const int32_t finalAPD = PresContext()->AppUnitsPerDevPixel();
   8168  // offset accumulates the offset at finalAPD.
   8169  nsPoint offset = GetPosition();
   8170 
   8171  int32_t currAPD = (*aOutAncestor)->PresContext()->AppUnitsPerDevPixel();
   8172  // docOffset accumulates the current offset at currAPD, and then flushes to
   8173  // offset at finalAPD when the APD changes or we finish.
   8174  nsPoint docOffset = crossdocOffset;
   8175  MOZ_ASSERT(crossdocOffset == nsPoint(0, 0) || !GetParent());
   8176 
   8177  while (*aOutAncestor != aStopAtAncestor.mFrame &&
   8178         !shouldStopAt(current, aStopAtAncestor, *aOutAncestor, aFlags)) {
   8179    docOffset += (*aOutAncestor)->GetPosition();
   8180 
   8181    nsIFrame* parent = (*aOutAncestor)->GetParent();
   8182    if (!parent) {
   8183      crossdocOffset.x = crossdocOffset.y = 0;
   8184      parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(*aOutAncestor,
   8185                                                              &crossdocOffset);
   8186 
   8187      int32_t newAPD =
   8188          parent ? parent->PresContext()->AppUnitsPerDevPixel() : currAPD;
   8189      if (!parent || newAPD != currAPD) {
   8190        // Convert docOffset to finalAPD and add it to offset.
   8191        offset += docOffset.ScaleToOtherAppUnits(currAPD, finalAPD);
   8192        docOffset.x = docOffset.y = 0;
   8193      }
   8194      currAPD = newAPD;
   8195      docOffset += crossdocOffset;
   8196 
   8197      if (!parent) {
   8198        break;
   8199      }
   8200    }
   8201 
   8202    current = *aOutAncestor;
   8203    *aOutAncestor = parent;
   8204  }
   8205  offset += docOffset.ScaleToOtherAppUnits(currAPD, finalAPD);
   8206 
   8207  NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?");
   8208 
   8209  int32_t scaleFactor =
   8210      ((aFlags & IN_CSS_UNITS) ? AppUnitsPerCSSPixel()
   8211                               : PresContext()->AppUnitsPerDevPixel());
   8212  return Matrix4x4Flagged::Translation2d(
   8213      NSAppUnitsToFloatPixels(offset.x, scaleFactor),
   8214      NSAppUnitsToFloatPixels(offset.y, scaleFactor));
   8215 }
   8216 
   8217 static void InvalidateRenderingObservers(nsIFrame* aDisplayRoot,
   8218                                         nsIFrame* aFrame,
   8219                                         bool aFrameChanged = true) {
   8220  MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
   8221  SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
   8222  nsIFrame* parent = aFrame;
   8223  while (parent != aDisplayRoot &&
   8224         (parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent)) &&
   8225         !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
   8226    SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
   8227  }
   8228 
   8229  if (!aFrameChanged) {
   8230    return;
   8231  }
   8232 
   8233  aFrame->MarkNeedsDisplayItemRebuild();
   8234 }
   8235 
   8236 static void SchedulePaintInternal(
   8237    nsIFrame* aDisplayRoot, nsIFrame* aFrame,
   8238    nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT) {
   8239  MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
   8240  nsPresContext* pres = aDisplayRoot->PresContext()->GetRootPresContext();
   8241 
   8242  // No need to schedule a paint for an external document since they aren't
   8243  // painted directly.
   8244  if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) {
   8245    return;
   8246  }
   8247  if (!pres->GetContainerWeak()) {
   8248    NS_WARNING("Shouldn't call SchedulePaint in a detached pres context");
   8249    return;
   8250  }
   8251 
   8252  pres->PresShell()->SchedulePaint();
   8253 
   8254  if (aType == nsIFrame::PAINT_DEFAULT) {
   8255    aDisplayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
   8256  }
   8257 }
   8258 
   8259 static void InvalidateFrameInternal(nsIFrame* aFrame, bool aHasDisplayItem,
   8260                                    bool aRebuildDisplayItems) {
   8261  if (aHasDisplayItem) {
   8262    aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
   8263  }
   8264 
   8265  if (aRebuildDisplayItems) {
   8266    aFrame->MarkNeedsDisplayItemRebuild();
   8267  }
   8268  SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
   8269  bool needsSchedulePaint = false;
   8270  if (nsLayoutUtils::IsPopup(aFrame)) {
   8271    needsSchedulePaint = true;
   8272  } else {
   8273    nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
   8274    while (parent &&
   8275           !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
   8276      if (aHasDisplayItem && !parent->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
   8277        parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
   8278      }
   8279      SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
   8280 
   8281      // If we're inside a popup, then we need to make sure that we
   8282      // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
   8283      // flag gets added to the popup display root frame.
   8284      if (nsLayoutUtils::IsPopup(parent)) {
   8285        needsSchedulePaint = true;
   8286        break;
   8287      }
   8288      parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent);
   8289    }
   8290    if (!parent) {
   8291      needsSchedulePaint = true;
   8292    }
   8293  }
   8294  if (!aHasDisplayItem) {
   8295    return;
   8296  }
   8297  if (needsSchedulePaint) {
   8298    nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
   8299    SchedulePaintInternal(displayRoot, aFrame);
   8300  }
   8301  if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
   8302    aFrame->RemoveProperty(nsIFrame::InvalidationRect());
   8303    aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
   8304  }
   8305 }
   8306 
   8307 void nsIFrame::InvalidateFrameSubtree(bool aRebuildDisplayItems /* = true */) {
   8308  InvalidateFrame(0, aRebuildDisplayItems);
   8309 
   8310  if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
   8311    return;
   8312  }
   8313 
   8314  AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
   8315 
   8316  for (const auto& childList : CrossDocChildLists()) {
   8317    for (nsIFrame* child : childList.mList) {
   8318      // Don't explicitly rebuild display items for our descendants,
   8319      // since we should be marked and it implicitly includes all
   8320      // descendants.
   8321      child->InvalidateFrameSubtree(false);
   8322    }
   8323  }
   8324 }
   8325 
   8326 void nsIFrame::ClearInvalidationStateBits() {
   8327  if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
   8328    for (const auto& childList : CrossDocChildLists()) {
   8329      for (nsIFrame* child : childList.mList) {
   8330        child->ClearInvalidationStateBits();
   8331      }
   8332    }
   8333  }
   8334 
   8335  RemoveStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT |
   8336                  NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
   8337 }
   8338 
   8339 bool HasRetainedDataFor(const nsIFrame* aFrame, uint32_t aDisplayItemKey) {
   8340  if (RefPtr<WebRenderUserData> data =
   8341          GetWebRenderUserData<WebRenderFallbackData>(aFrame,
   8342                                                      aDisplayItemKey)) {
   8343    return true;
   8344  }
   8345 
   8346  return false;
   8347 }
   8348 
   8349 void nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey,
   8350                               bool aRebuildDisplayItems /* = true */) {
   8351  bool hasDisplayItem =
   8352      !aDisplayItemKey || HasRetainedDataFor(this, aDisplayItemKey);
   8353  InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
   8354 }
   8355 
   8356 void nsIFrame::InvalidateFrameWithRect(const nsRect& aRect,
   8357                                       uint32_t aDisplayItemKey,
   8358                                       bool aRebuildDisplayItems /* = true */) {
   8359  if (aRect.IsEmpty()) {
   8360    return;
   8361  }
   8362  bool hasDisplayItem =
   8363      !aDisplayItemKey || HasRetainedDataFor(this, aDisplayItemKey);
   8364  bool alreadyInvalid = false;
   8365  if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
   8366    InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
   8367  } else {
   8368    alreadyInvalid = true;
   8369  }
   8370 
   8371  if (!hasDisplayItem) {
   8372    return;
   8373  }
   8374 
   8375  nsRect* rect;
   8376  if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
   8377    rect = GetProperty(InvalidationRect());
   8378    MOZ_ASSERT(rect);
   8379  } else {
   8380    if (alreadyInvalid) {
   8381      return;
   8382    }
   8383    rect = new nsRect();
   8384    AddProperty(InvalidationRect(), rect);
   8385    AddStateBits(NS_FRAME_HAS_INVALID_RECT);
   8386  }
   8387 
   8388  *rect = rect->Union(aRect);
   8389 }
   8390 
   8391 bool nsIFrame::IsInvalid(nsRect& aRect) {
   8392  if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
   8393    return false;
   8394  }
   8395 
   8396  if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
   8397    nsRect* rect = GetProperty(InvalidationRect());
   8398    NS_ASSERTION(
   8399        rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!");
   8400    aRect = *rect;
   8401  } else {
   8402    aRect.SetEmpty();
   8403  }
   8404  return true;
   8405 }
   8406 
   8407 void nsIFrame::SchedulePaint(PaintType aType, bool aFrameChanged) {
   8408  if (PresShell()->IsPaintingSuppressed()) {
   8409    // We can't have any display items yet, and when we unsuppress we will
   8410    // invalidate the root frame.
   8411    return;
   8412  }
   8413  nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
   8414  InvalidateRenderingObservers(displayRoot, this, aFrameChanged);
   8415  SchedulePaintInternal(displayRoot, this, aType);
   8416 }
   8417 
   8418 void nsIFrame::SchedulePaintWithoutInvalidatingObservers(PaintType aType) {
   8419  nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
   8420  SchedulePaintInternal(displayRoot, this, aType);
   8421 }
   8422 
   8423 void nsIFrame::InvalidateLayer(DisplayItemType aDisplayItemKey,
   8424                               const nsIntRect* aDamageRect,
   8425                               const nsRect* aFrameDamageRect,
   8426                               uint32_t aFlags /* = 0 */) {
   8427  NS_ASSERTION(aDisplayItemKey > DisplayItemType::TYPE_ZERO, "Need a key");
   8428 
   8429  nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
   8430  InvalidateRenderingObservers(displayRoot, this, false);
   8431 
   8432  // Check if frame supports WebRender's async update
   8433  if ((aFlags & UPDATE_IS_ASYNC) &&
   8434      WebRenderUserData::SupportsAsyncUpdate(this)) {
   8435    // WebRender does not use layer, then return nullptr.
   8436    return;
   8437  }
   8438 
   8439  if (aFrameDamageRect && aFrameDamageRect->IsEmpty()) {
   8440    return;
   8441  }
   8442 
   8443  // In the bug 930056, dialer app startup but not shown on the
   8444  // screen because sometimes we don't have any retainned data
   8445  // for remote type displayitem and thus Repaint event is not
   8446  // triggered. So, always invalidate in this case.
   8447  DisplayItemType displayItemKey = aDisplayItemKey;
   8448  if (aDisplayItemKey == DisplayItemType::TYPE_REMOTE) {
   8449    displayItemKey = DisplayItemType::TYPE_ZERO;
   8450  }
   8451 
   8452  if (aFrameDamageRect) {
   8453    InvalidateFrameWithRect(*aFrameDamageRect,
   8454                            static_cast<uint32_t>(displayItemKey));
   8455  } else {
   8456    InvalidateFrame(static_cast<uint32_t>(displayItemKey));
   8457  }
   8458 }
   8459 
   8460 static nsRect ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
   8461                                 const nsSize& aNewSize) {
   8462  nsRect r = aOverflowRect;
   8463 
   8464  if (aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
   8465    // For SVG frames, we only need to account for filters.
   8466    // TODO: We could also take account of clipPath and mask to reduce the
   8467    // ink overflow, but that's not essential.
   8468    if (aFrame->StyleEffects()->HasFilters()) {
   8469      aFrame->SetOrUpdateDeletableProperty(nsIFrame::PreEffectsBBoxProperty(),
   8470                                           r);
   8471      r = SVGUtils::GetPostFilterInkOverflowRect(aFrame, aOverflowRect);
   8472    }
   8473    return r;
   8474  }
   8475 
   8476  // box-shadow
   8477  r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
   8478 
   8479  // border-image-outset.
   8480  // We need to include border-image-outset because it can cause the
   8481  // border image to be drawn beyond the border box.
   8482 
   8483  // (1) It's important we not check whether there's a border-image
   8484  //     since the style hint for a change in border image doesn't cause
   8485  //     reflow, and that's probably more important than optimizing the
   8486  //     overflow areas for the silly case of border-image-outset without
   8487  //     border-image
   8488  // (2) It's important that we not check whether the border-image
   8489  //     is actually loaded, since that would require us to reflow when
   8490  //     the image loads.
   8491  const nsStyleBorder* styleBorder = aFrame->StyleBorder();
   8492  nsMargin outsetMargin = styleBorder->GetImageOutset();
   8493 
   8494  if (outsetMargin != nsMargin(0, 0, 0, 0)) {
   8495    nsRect outsetRect(nsPoint(0, 0), aNewSize);
   8496    outsetRect.Inflate(outsetMargin);
   8497    r.UnionRect(r, outsetRect);
   8498  }
   8499 
   8500  // Note that we don't remove the outlineInnerRect if a frame loses outline
   8501  // style. That would require an extra property lookup for every frame,
   8502  // or a new frame state bit to track whether a property had been stored,
   8503  // or something like that. It's not worth doing that here. At most it's
   8504  // only one heap-allocated rect per frame and it will be cleaned up when
   8505  // the frame dies.
   8506 
   8507  if (SVGIntegrationUtils::UsingOverflowAffectingEffects(aFrame)) {
   8508    aFrame->SetOrUpdateDeletableProperty(nsIFrame::PreEffectsBBoxProperty(), r);
   8509    r = SVGIntegrationUtils::ComputePostEffectsInkOverflowRect(aFrame, r);
   8510  }
   8511 
   8512  return r;
   8513 }
   8514 
   8515 void nsIFrame::SetPosition(const nsPoint& aPt) {
   8516  if (mRect.TopLeft() == aPt) {
   8517    return;
   8518  }
   8519  mRect.MoveTo(aPt);
   8520  MarkNeedsDisplayItemRebuild();
   8521 }
   8522 
   8523 void nsIFrame::MovePositionBy(const nsPoint& aTranslation) {
   8524  nsPoint position = GetNormalPosition() + aTranslation;
   8525 
   8526  const nsMargin* computedOffsets = nullptr;
   8527  if (IsRelativelyOrStickyPositioned()) {
   8528    computedOffsets = GetProperty(nsIFrame::ComputedOffsetProperty());
   8529  }
   8530  ReflowInput::ApplyRelativePositioning(
   8531      this, computedOffsets ? *computedOffsets : nsMargin(), &position);
   8532  SetPosition(position);
   8533 }
   8534 
   8535 nsRect nsIFrame::GetNormalRect() const {
   8536  bool hasProperty;
   8537  nsPoint normalPosition = GetProperty(NormalPositionProperty(), &hasProperty);
   8538  if (hasProperty) {
   8539    return nsRect(normalPosition, GetSize());
   8540  }
   8541  return GetRect();
   8542 }
   8543 
   8544 nsRect nsIFrame::GetBoundingClientRect() {
   8545  return nsLayoutUtils::GetAllInFlowRectsUnion(
   8546      this, nsLayoutUtils::GetContainingBlockForClientRect(this),
   8547      nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms);
   8548 }
   8549 
   8550 nsPoint nsIFrame::GetPositionIgnoringScrolling() const {
   8551  return GetParent() ? GetParent()->GetPositionOfChildIgnoringScrolling(this)
   8552                     : GetPosition();
   8553 }
   8554 
   8555 nsRect nsIFrame::GetOverflowRect(OverflowType aType) const {
   8556  // Note that in some cases the overflow area might not have been
   8557  // updated (yet) to reflect any outline set on the frame or the area
   8558  // of child frames. That's OK because any reflow that updates these
   8559  // areas will invalidate the appropriate area, so any (mis)uses of
   8560  // this method will be fixed up.
   8561 
   8562  if (mOverflow.mType == OverflowStorageType::Large) {
   8563    // there is an overflow rect, and it's not stored as deltas but as
   8564    // a separately-allocated rect
   8565    return GetOverflowAreasProperty()->Overflow(aType);
   8566  }
   8567 
   8568  if (aType == OverflowType::Ink &&
   8569      mOverflow.mType != OverflowStorageType::None) {
   8570    return InkOverflowFromDeltas();
   8571  }
   8572 
   8573  return GetRectRelativeToSelf();
   8574 }
   8575 
   8576 OverflowAreas nsIFrame::GetOverflowAreas() const {
   8577  if (mOverflow.mType == OverflowStorageType::Large) {
   8578    // there is an overflow rect, and it's not stored as deltas but as
   8579    // a separately-allocated rect
   8580    return *GetOverflowAreasProperty();
   8581  }
   8582 
   8583  return OverflowAreas(InkOverflowFromDeltas(),
   8584                       nsRect(nsPoint(0, 0), GetSize()));
   8585 }
   8586 
   8587 OverflowAreas nsIFrame::GetOverflowAreasRelativeToSelf() const {
   8588  if (IsTransformed()) {
   8589    if (OverflowAreas* preTransformOverflows =
   8590            GetProperty(PreTransformOverflowAreasProperty())) {
   8591      return *preTransformOverflows;
   8592    }
   8593  }
   8594  return GetOverflowAreas();
   8595 }
   8596 
   8597 OverflowAreas nsIFrame::GetOverflowAreasRelativeToParent() const {
   8598  return GetOverflowAreas() + GetPosition();
   8599 }
   8600 
   8601 OverflowAreas nsIFrame::GetActualAndNormalOverflowAreasRelativeToParent()
   8602    const {
   8603  const bool hasAnchorPosReference = HasAnchorPosReference();
   8604  if (MOZ_LIKELY(!IsRelativelyOrStickyPositioned() && !hasAnchorPosReference)) {
   8605    return GetOverflowAreasRelativeToParent();
   8606  }
   8607 
   8608  const OverflowAreas overflows = GetOverflowAreas();
   8609  OverflowAreas actualAndNormalOverflows = overflows + GetNormalPosition();
   8610  if (IsRelativelyPositioned() || hasAnchorPosReference) {
   8611    actualAndNormalOverflows.UnionWith(overflows + GetPosition());
   8612  } else {
   8613    // For sticky positioned elements, we only use the normal position for the
   8614    // scrollable overflow. This avoids circular dependencies between sticky
   8615    // positioned elements and their scroll container. (The scroll position and
   8616    // the scroll container's size impact the sticky position, so we don't want
   8617    // the sticky position to impact them.)
   8618    MOZ_ASSERT(IsStickyPositioned());
   8619    actualAndNormalOverflows.UnionWith(
   8620        OverflowAreas(overflows.InkOverflow() + GetPosition(), nsRect()));
   8621  }
   8622  return actualAndNormalOverflows;
   8623 }
   8624 
   8625 nsRect nsIFrame::ScrollableOverflowRectRelativeToParent() const {
   8626  return ScrollableOverflowRect() + GetPosition();
   8627 }
   8628 
   8629 nsRect nsIFrame::InkOverflowRectRelativeToParent() const {
   8630  return InkOverflowRect() + GetPosition();
   8631 }
   8632 
   8633 nsRect nsIFrame::ScrollableOverflowRectRelativeToSelf() const {
   8634  if (IsTransformed()) {
   8635    if (OverflowAreas* preTransformOverflows =
   8636            GetProperty(PreTransformOverflowAreasProperty())) {
   8637      return preTransformOverflows->ScrollableOverflow();
   8638    }
   8639  }
   8640  return ScrollableOverflowRect();
   8641 }
   8642 
   8643 nsRect nsIFrame::InkOverflowRectRelativeToSelf() const {
   8644  if (IsTransformed()) {
   8645    if (OverflowAreas* preTransformOverflows =
   8646            GetProperty(PreTransformOverflowAreasProperty())) {
   8647      return preTransformOverflows->InkOverflow();
   8648    }
   8649  }
   8650  return InkOverflowRect();
   8651 }
   8652 
   8653 nsRect nsIFrame::PreEffectsInkOverflowRect() const {
   8654  nsRect* r = GetProperty(nsIFrame::PreEffectsBBoxProperty());
   8655  return r ? *r : InkOverflowRectRelativeToSelf();
   8656 }
   8657 
   8658 bool nsIFrame::UpdateOverflow() {
   8659  MOZ_ASSERT(FrameMaintainsOverflow(),
   8660             "Non-display SVG do not maintain ink overflow rects");
   8661 
   8662  nsRect rect(nsPoint(0, 0), GetSize());
   8663  OverflowAreas overflowAreas(rect, rect);
   8664 
   8665  if (!ComputeCustomOverflow(overflowAreas)) {
   8666    // If updating overflow wasn't supported by this frame, then it should
   8667    // have scheduled any necessary reflows. We can return false to say nothing
   8668    // changed, and wait for reflow to correct it.
   8669    return false;
   8670  }
   8671 
   8672  UnionChildOverflow(overflowAreas);
   8673 
   8674  if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
   8675    return true;
   8676  }
   8677 
   8678  // Frames that combine their 3d transform with their ancestors
   8679  // only compute a pre-transform overflow rect, and then contribute
   8680  // to the normal overflow rect of the preserve-3d root. Always return
   8681  // true here so that we propagate changes up to the root for final
   8682  // calculation.
   8683  return Combines3DTransformWithAncestors();
   8684 }
   8685 
   8686 /* virtual */
   8687 bool nsIFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
   8688  return true;
   8689 }
   8690 
   8691 bool nsIFrame::DoesClipChildrenInBothAxes() const {
   8692  if (IsScrollContainerOrSubclass()) {
   8693    return true;
   8694  }
   8695  const nsStyleDisplay* display = StyleDisplay();
   8696  if (display->IsContainPaint() && SupportsContainLayoutAndPaint()) {
   8697    return true;
   8698  }
   8699  return display->mOverflowX == StyleOverflow::Clip &&
   8700         display->mOverflowY == StyleOverflow::Clip;
   8701 }
   8702 
   8703 /* virtual */
   8704 void nsIFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
   8705                                  bool aAsIfScrolled) {
   8706  if (aAsIfScrolled || !DoesClipChildrenInBothAxes()) {
   8707    nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas);
   8708  }
   8709 }
   8710 
   8711 // Return true if this form control element's preferred size property (but not
   8712 // percentage max size property) contains a percentage value that should be
   8713 // resolved against zero when calculating its min-content contribution in the
   8714 // corresponding axis.
   8715 //
   8716 // For proper replaced elements, the percentage value in both their max size
   8717 // property or preferred size property should be resolved against zero. This is
   8718 // handled in IsPercentageResolvedAgainstZero().
   8719 inline static bool FormControlShrinksForPercentSize(const nsIFrame* aFrame) {
   8720  if (!aFrame->IsReplaced()) {
   8721    // Quick test to reject most frames.
   8722    return false;
   8723  }
   8724 
   8725  switch (aFrame->Type()) {
   8726    case LayoutFrameType::Progress:
   8727    case LayoutFrameType::Range:
   8728    case LayoutFrameType::TextInput:
   8729    case LayoutFrameType::ColorControl:
   8730    case LayoutFrameType::ComboboxControl:
   8731    case LayoutFrameType::ListControl:
   8732    case LayoutFrameType::CheckboxRadio:
   8733    case LayoutFrameType::FileControl:
   8734    case LayoutFrameType::ImageControl:
   8735      return true;
   8736    default:
   8737      // True buttons (<button>, backed by block/grid/flex frame) and most
   8738      // button-flavored <inputs> (those backed by InputButtonControlFrame)
   8739      // don't have this shrinking behavior. But color-inputs and comboboxes do;
   8740      // and both of those derive from ButtonControlFrame. So: we can't easily
   8741      // use do_QueryFrame to differentiate the buttons-that-do vs. the
   8742      // buttons-that-don't. So we explicitly list the buttons-that-do by
   8743      // LayoutFrameType above, and the others fall into this catch-all.
   8744      return false;
   8745  }
   8746 }
   8747 
   8748 bool nsIFrame::IsPercentageResolvedAgainstZero(
   8749    const StyleSize& aStyleSize, const StyleMaxSize& aStyleMaxSize) const {
   8750  const bool sizeHasPercent = aStyleSize.HasPercent();
   8751  return ((sizeHasPercent || aStyleMaxSize.HasPercent()) &&
   8752          HasReplacedSizing()) ||
   8753         (sizeHasPercent && FormControlShrinksForPercentSize(this));
   8754 }
   8755 
   8756 // Summary of the Cyclic-Percentage Intrinsic Size Contribution Rules:
   8757 //
   8758 // Element Type         |       Replaced           |        Non-replaced
   8759 // Contribution Type    | min-content  max-content | min-content  max-content
   8760 // ---------------------------------------------------------------------------
   8761 // min size             | zero         zero        | zero         zero
   8762 // max & preferred size | zero         initial     | initial      initial
   8763 //
   8764 // https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution
   8765 bool nsIFrame::IsPercentageResolvedAgainstZero(const LengthPercentage& aSize,
   8766                                               SizeProperty aProperty) const {
   8767  // Early return to avoid calling the virtual function, IsFrameOfType().
   8768  if (aProperty == SizeProperty::MinSize) {
   8769    return true;
   8770  }
   8771 
   8772  const bool hasPercentOnReplaced = aSize.HasPercent() && HasReplacedSizing();
   8773  if (aProperty == SizeProperty::MaxSize) {
   8774    return hasPercentOnReplaced;
   8775  }
   8776 
   8777  MOZ_ASSERT(aProperty == SizeProperty::Size);
   8778  return hasPercentOnReplaced ||
   8779         (aSize.HasPercent() && FormControlShrinksForPercentSize(this));
   8780 }
   8781 
   8782 bool nsIFrame::IsBlockWrapper() const {
   8783  auto pseudoType = Style()->GetPseudoType();
   8784  return pseudoType == PseudoStyleType::mozBlockInsideInlineWrapper ||
   8785         pseudoType == PseudoStyleType::cellContent ||
   8786         pseudoType == PseudoStyleType::columnSpanWrapper;
   8787 }
   8788 
   8789 bool nsIFrame::IsBlockFrameOrSubclass() const {
   8790  const nsBlockFrame* thisAsBlock = do_QueryFrame(this);
   8791  return !!thisAsBlock;
   8792 }
   8793 
   8794 bool nsIFrame::IsImageFrameOrSubclass() const {
   8795  const nsImageFrame* asImage = do_QueryFrame(this);
   8796  return !!asImage;
   8797 }
   8798 
   8799 bool nsIFrame::IsScrollContainerOrSubclass() const {
   8800  const bool result = IsScrollContainerFrame() || IsListControlFrame();
   8801  MOZ_ASSERT(result == !!QueryFrame(ScrollContainerFrame::kFrameIID));
   8802  return result;
   8803 }
   8804 
   8805 bool nsIFrame::IsSubgrid() const {
   8806  return IsGridContainerFrame() &&
   8807         static_cast<const nsGridContainerFrame*>(this)->IsSubgrid();
   8808 }
   8809 
   8810 static nsIFrame* GetNearestBlockContainer(nsIFrame* frame) {
   8811  while (!frame->IsBlockContainer()) {
   8812    frame = frame->GetParent();
   8813    NS_ASSERTION(
   8814        frame,
   8815        "How come we got to the root frame without seeing a containing block?");
   8816  }
   8817  return frame;
   8818 }
   8819 
   8820 bool nsIFrame::IsBlockContainer() const {
   8821  // The block wrappers we use to wrap blocks inside inlines aren't
   8822  // described in the CSS spec.  We need to make them not be containing
   8823  // blocks.
   8824  // Since the parent of such a block is either a normal block or
   8825  // another such pseudo, this shouldn't cause anything bad to happen.
   8826  // Also the anonymous blocks inside table cells are not containing blocks.
   8827  //
   8828  // If we ever start skipping table row groups from being containing blocks,
   8829  // you need to remove the StickyScrollContainer hack referencing bug 1421660.
   8830  // Table rows are not containing blocks either
   8831  return !IsLineParticipant() && !IsBlockWrapper() && !IsTableRowFrame();
   8832 }
   8833 
   8834 nsIFrame* nsIFrame::GetContainingBlock(
   8835    uint32_t aFlags, const nsStyleDisplay* aStyleDisplay) const {
   8836  MOZ_ASSERT(aStyleDisplay == StyleDisplay());
   8837 
   8838  // Keep this in sync with MightBeContainingBlockFor in ReflowInput.cpp.
   8839 
   8840  if (!GetParent()) {
   8841    return nullptr;
   8842  }
   8843  // MathML frames might have absolute positioning style, but they would
   8844  // still be in-flow.  So we have to check to make sure that the frame
   8845  // is really out-of-flow too.
   8846  nsIFrame* f;
   8847  if (IsAbsolutelyPositioned(aStyleDisplay)) {
   8848    f = GetParent();  // the parent is always the containing block
   8849  } else {
   8850    f = GetNearestBlockContainer(GetParent());
   8851  }
   8852 
   8853  if (aFlags & SKIP_SCROLLED_FRAME && f &&
   8854      f->Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
   8855    f = f->GetParent();
   8856  }
   8857  return f;
   8858 }
   8859 
   8860 #ifdef DEBUG_FRAME_DUMP
   8861 
   8862 Maybe<uint32_t> nsIFrame::ContentIndexInContainer(const nsIFrame* aFrame) {
   8863  if (nsIContent* content = aFrame->GetContent()) {
   8864    return content->ComputeIndexInParentContent();
   8865  }
   8866  return Nothing();
   8867 }
   8868 
   8869 nsAutoCString nsIFrame::ListTag(bool aListOnlyDeterministic) const {
   8870  nsAutoString tmp;
   8871  GetFrameName(tmp);
   8872 
   8873  nsAutoCString tag;
   8874  tag += NS_ConvertUTF16toUTF8(tmp);
   8875  ListPtr(tag, aListOnlyDeterministic, this, "@");
   8876  return tag;
   8877 }
   8878 
   8879 std::string nsIFrame::ConvertToString(const LogicalRect& aRect,
   8880                                      const WritingMode aWM, ListFlags aFlags) {
   8881  if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
   8882    // Abuse CSSRect to store all LogicalRect's dimensions in CSS pixels.
   8883    return ToString(mozilla::CSSRect(CSSPixel::FromAppUnits(aRect.IStart(aWM)),
   8884                                     CSSPixel::FromAppUnits(aRect.BStart(aWM)),
   8885                                     CSSPixel::FromAppUnits(aRect.ISize(aWM)),
   8886                                     CSSPixel::FromAppUnits(aRect.BSize(aWM))));
   8887  }
   8888  return ToString(aRect);
   8889 }
   8890 
   8891 std::string nsIFrame::ConvertToString(const LogicalSize& aSize,
   8892                                      const WritingMode aWM, ListFlags aFlags) {
   8893  if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
   8894    // Abuse CSSSize to store all LogicalSize's dimensions in CSS pixels.
   8895    return ToString(CSSSize(CSSPixel::FromAppUnits(aSize.ISize(aWM)),
   8896                            CSSPixel::FromAppUnits(aSize.BSize(aWM))));
   8897  }
   8898  return ToString(aSize);
   8899 }
   8900 
   8901 // Debugging
   8902 void nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix,
   8903                           ListFlags aFlags) const {
   8904  aTo += aPrefix;
   8905  const bool onlyDeterministic =
   8906      aFlags.contains(ListFlag::OnlyListDeterministicInfo);
   8907  aTo += ListTag(onlyDeterministic);
   8908  if (!onlyDeterministic) {
   8909    if (GetParent()) {
   8910      aTo += nsPrintfCString(" parent=%p", static_cast<void*>(GetParent()));
   8911    }
   8912    if (GetNextSibling()) {
   8913      aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling()));
   8914    }
   8915  }
   8916  if (GetPrevContinuation()) {
   8917    bool fluid = GetPrevInFlow() == GetPrevContinuation();
   8918    aTo += nsPrintfCString(" prev-%s", fluid ? "in-flow" : "continuation");
   8919    ListPtr(aTo, aFlags, GetPrevContinuation());
   8920  }
   8921  if (GetNextContinuation()) {
   8922    bool fluid = GetNextInFlow() == GetNextContinuation();
   8923    aTo += nsPrintfCString(" next-%s", fluid ? "in-flow" : "continuation");
   8924    ListPtr(aTo, aFlags, GetNextContinuation());
   8925  }
   8926  if (const nsAtom* const autoPageValue =
   8927          GetProperty(AutoPageValueProperty())) {
   8928    aTo += " AutoPage=";
   8929    aTo += nsAtomCString(autoPageValue);
   8930  }
   8931  if (const nsIFrame::PageValues* const pageValues =
   8932          GetProperty(PageValuesProperty())) {
   8933    aTo += " PageValues={";
   8934    if (pageValues->mStartPageValue) {
   8935      aTo += nsAtomCString(pageValues->mStartPageValue);
   8936    } else {
   8937      aTo += "<null>";
   8938    }
   8939    aTo += ", ";
   8940    if (pageValues->mEndPageValue) {
   8941      aTo += nsAtomCString(pageValues->mEndPageValue);
   8942    } else {
   8943      aTo += "<null>";
   8944    }
   8945    aTo += "}";
   8946  }
   8947  void* IBsibling = GetProperty(IBSplitSibling());
   8948  if (IBsibling) {
   8949    aTo += nsPrintfCString(" IBSplitSibling");
   8950    ListPtr(aTo, aFlags, IBsibling);
   8951  }
   8952  void* IBprevsibling = GetProperty(IBSplitPrevSibling());
   8953  if (IBprevsibling) {
   8954    aTo += nsPrintfCString(" IBSplitPrevSibling");
   8955    ListPtr(aTo, aFlags, IBprevsibling);
   8956  }
   8957  if (nsLayoutUtils::FontSizeInflationEnabled(PresContext())) {
   8958    if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
   8959      aTo += nsPrintfCString(" FFR");
   8960      if (nsFontInflationData* data =
   8961              nsFontInflationData::FindFontInflationDataFor(this)) {
   8962        aTo += nsPrintfCString(
   8963            ",enabled=%s,UIS=%s", data->InflationEnabled() ? "yes" : "no",
   8964            ConvertToString(data->UsableISize(), aFlags).c_str());
   8965      }
   8966    }
   8967    if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
   8968      aTo += nsPrintfCString(" FIC");
   8969    }
   8970    aTo += nsPrintfCString(" FI=%f", nsLayoutUtils::FontSizeInflationFor(this));
   8971  }
   8972  aTo += nsPrintfCString(" %s", ConvertToString(mRect, aFlags).c_str());
   8973 
   8974  mozilla::WritingMode wm = GetWritingMode();
   8975  if (wm.IsVertical() || wm.IsBidiRTL()) {
   8976    aTo +=
   8977        nsPrintfCString(" wm=%s logical-size=(%s)", ToString(wm).c_str(),
   8978                        ConvertToString(GetLogicalSize(), wm, aFlags).c_str());
   8979  }
   8980 
   8981  nsIFrame* parent = GetParent();
   8982  if (parent) {
   8983    WritingMode pWM = parent->GetWritingMode();
   8984    if (pWM.IsVertical() || pWM.IsBidiRTL()) {
   8985      nsSize containerSize = parent->mRect.Size();
   8986      LogicalRect lr(pWM, mRect, containerSize);
   8987      aTo += nsPrintfCString(" parent-wm=%s cs=(%s) logical-rect=%s",
   8988                             ToString(pWM).c_str(),
   8989                             ConvertToString(containerSize, aFlags).c_str(),
   8990                             ConvertToString(lr, pWM, aFlags).c_str());
   8991    }
   8992  }
   8993  nsIFrame* f = const_cast<nsIFrame*>(this);
   8994  if (f->HasOverflowAreas()) {
   8995    nsRect io = f->InkOverflowRect();
   8996    if (!io.IsEqualEdges(mRect)) {
   8997      aTo += nsPrintfCString(" ink-overflow=%s",
   8998                             ConvertToString(io, aFlags).c_str());
   8999    }
   9000    nsRect so = f->ScrollableOverflowRect();
   9001    if (!so.IsEqualEdges(mRect)) {
   9002      aTo += nsPrintfCString(" scr-overflow=%s",
   9003                             ConvertToString(so, aFlags).c_str());
   9004    }
   9005  }
   9006  if (OverflowAreas* preTransformOverflows =
   9007          f->GetProperty(PreTransformOverflowAreasProperty())) {
   9008    nsRect io = preTransformOverflows->InkOverflow();
   9009    if (!io.IsEqualEdges(mRect) &&
   9010        (!f->HasOverflowAreas() || !io.IsEqualEdges(f->InkOverflowRect()))) {
   9011      aTo += nsPrintfCString(" pre-transform-ink-overflow=%s",
   9012                             ConvertToString(io, aFlags).c_str());
   9013    }
   9014    nsRect so = preTransformOverflows->ScrollableOverflow();
   9015    if (!so.IsEqualEdges(mRect) &&
   9016        (!f->HasOverflowAreas() ||
   9017         !so.IsEqualEdges(f->ScrollableOverflowRect()))) {
   9018      aTo += nsPrintfCString(" pre-transform-scr-overflow=%s",
   9019                             ConvertToString(so, aFlags).c_str());
   9020    }
   9021  }
   9022  bool hasNormalPosition;
   9023  nsPoint normalPosition = GetNormalPosition(&hasNormalPosition);
   9024  if (hasNormalPosition) {
   9025    aTo += nsPrintfCString(" normal-position=%s",
   9026                           ConvertToString(normalPosition, aFlags).c_str());
   9027  }
   9028  if (HasProperty(BidiDataProperty())) {
   9029    FrameBidiData bidi = GetBidiData();
   9030    aTo += nsPrintfCString(" bidi(%d,%d,%d)", bidi.baseLevel.Value(),
   9031                           bidi.embeddingLevel.Value(),
   9032                           bidi.precedingControl.Value());
   9033  }
   9034  if (IsTransformed()) {
   9035    aTo += nsPrintfCString(" transformed");
   9036  }
   9037  if (ChildrenHavePerspective()) {
   9038    aTo += nsPrintfCString(" perspective");
   9039  }
   9040  if (Extend3DContext()) {
   9041    aTo += nsPrintfCString(" extend-3d");
   9042  }
   9043  if (Combines3DTransformWithAncestors()) {
   9044    aTo += nsPrintfCString(" combines-3d-transform-with-ancestors");
   9045  }
   9046  if (mContent) {
   9047    if (!onlyDeterministic) {
   9048      aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent));
   9049    }
   9050    if (IsPrimaryFrame() && DisplayPortUtils::HasDisplayPort(mContent)) {
   9051      // Anon boxes and continuations point to the same content - Just print on
   9052      // primary frame.
   9053      aTo += "[displayport]"_ns;
   9054    }
   9055  }
   9056  if (!onlyDeterministic) {
   9057    aTo += nsPrintfCString("[cs=%p]", static_cast<void*>(mComputedStyle));
   9058  }
   9059  if (mComputedStyle) {
   9060    const auto pseudoType = mComputedStyle->GetPseudoType();
   9061    const auto pseudoTypeStr = ToString(pseudoType);
   9062    if (!pseudoTypeStr.empty()) {
   9063      aTo += nsPrintfCString("[%s]", pseudoTypeStr.c_str());
   9064    }
   9065  }
   9066 
   9067  auto contentVisibility = StyleDisplay()->ContentVisibility(*this);
   9068  if (contentVisibility != StyleContentVisibility::Visible) {
   9069    aTo += nsPrintfCString(" [content-visibility=");
   9070    if (contentVisibility == StyleContentVisibility::Auto) {
   9071      aTo += "auto, "_ns;
   9072    } else if (contentVisibility == StyleContentVisibility::Hidden) {
   9073      aTo += "hidden, "_ns;
   9074    }
   9075 
   9076    if (HidesContent()) {
   9077      aTo += "HidesContent=hidden"_ns;
   9078    } else {
   9079      aTo += "HidesContent=visibile"_ns;
   9080    }
   9081    aTo += "]";
   9082  }
   9083 
   9084  if (IsFrameModified()) {
   9085    aTo += nsPrintfCString(" modified");
   9086  }
   9087 
   9088  if (HasModifiedDescendants()) {
   9089    aTo += nsPrintfCString(" has-modified-descendants");
   9090  }
   9091 }
   9092 
   9093 void nsIFrame::List(FILE* out, const char* aPrefix, ListFlags aFlags) const {
   9094  nsCString str;
   9095  ListGeneric(str, aPrefix, aFlags);
   9096  fprintf_stderr(out, "%s\n", str.get());
   9097 }
   9098 
   9099 void nsIFrame::ListTextRuns(FILE* out) const {
   9100  nsTHashSet<const void*> seen;
   9101  ListTextRuns(out, seen);
   9102 }
   9103 
   9104 void nsIFrame::ListTextRuns(FILE* out, nsTHashSet<const void*>& aSeen) const {
   9105  for (const auto& childList : ChildLists()) {
   9106    for (const nsIFrame* kid : childList.mList) {
   9107      kid->ListTextRuns(out, aSeen);
   9108    }
   9109  }
   9110 }
   9111 
   9112 void nsIFrame::ListMatchedRules(FILE* out, const char* aPrefix) const {
   9113  AutoTArray<StyleMatchingDeclarationBlock, 8> decls;
   9114  Servo_ComputedValues_GetMatchingDeclarations(Style(), &decls);
   9115  for (const StyleMatchingDeclarationBlock& block : decls) {
   9116    nsAutoCString ruleText;
   9117    Servo_DeclarationBlock_GetCssText(block.block, &ruleText);
   9118    fprintf_stderr(out, "%s%s\n", aPrefix, ruleText.get());
   9119  }
   9120 }
   9121 
   9122 void nsIFrame::ListWithMatchedRules(FILE* out, const char* aPrefix) const {
   9123  fprintf_stderr(out, "%s%s\n", aPrefix, ListTag().get());
   9124 
   9125  nsCString rulePrefix;
   9126  rulePrefix += aPrefix;
   9127  rulePrefix += "    ";
   9128  ListMatchedRules(out, rulePrefix.get());
   9129 }
   9130 
   9131 nsresult nsIFrame::GetFrameName(nsAString& aResult) const {
   9132  return MakeFrameName(u"Frame"_ns, aResult);
   9133 }
   9134 
   9135 nsresult nsIFrame::MakeFrameName(const nsAString& aType,
   9136                                 nsAString& aResult) const {
   9137  aResult = aType;
   9138  if (mContent && !mContent->IsText()) {
   9139    nsAutoString buf;
   9140    mContent->NodeInfo()->NameAtom()->ToString(buf);
   9141    if (nsAtom* id = mContent->GetID()) {
   9142      buf.AppendLiteral(" id=");
   9143      buf.Append(nsDependentAtomString(id));
   9144    }
   9145    if (IsSubDocumentFrame()) {
   9146      nsAutoString src;
   9147      mContent->AsElement()->GetAttr(nsGkAtoms::src, src);
   9148      buf.AppendLiteral(" src=");
   9149      buf.Append(src);
   9150    }
   9151    aResult.Append('(');
   9152    aResult.Append(buf);
   9153    aResult.Append(')');
   9154  }
   9155  aResult.Append('(');
   9156  Maybe<uint32_t> index = ContentIndexInContainer(this);
   9157  if (index.isSome()) {
   9158    aResult.AppendInt(*index);
   9159  } else {
   9160    aResult.AppendInt(-1);
   9161  }
   9162  aResult.Append(')');
   9163  return NS_OK;
   9164 }
   9165 
   9166 void nsIFrame::DumpFrameTree() const { DumpFrameTree(false); }
   9167 
   9168 void nsIFrame::DumpFrameTree(bool aListOnlyDeterministic) const {
   9169  ListFlags flags;
   9170  if (aListOnlyDeterministic) {
   9171    flags += ListFlag::OnlyListDeterministicInfo;
   9172  }
   9173  PresShell()->GetRootFrame()->List(stderr, "", flags);
   9174 }
   9175 
   9176 void nsIFrame::DumpFrameTreeInCSSPixels() const {
   9177  DumpFrameTreeInCSSPixels(false);
   9178 }
   9179 
   9180 void nsIFrame::DumpFrameTreeInCSSPixels(bool aListOnlyDeterministic) const {
   9181  ListFlags flags{ListFlag::DisplayInCSSPixels};
   9182  if (aListOnlyDeterministic) {
   9183    flags += ListFlag::OnlyListDeterministicInfo;
   9184  }
   9185  PresShell()->GetRootFrame()->List(stderr, "", flags);
   9186 }
   9187 
   9188 void nsIFrame::DumpFrameTreeLimited() const { List(stderr); }
   9189 void nsIFrame::DumpFrameTreeLimitedInCSSPixels() const {
   9190  List(stderr, "", ListFlag::DisplayInCSSPixels);
   9191 }
   9192 
   9193 #endif
   9194 
   9195 bool nsIFrame::IsVisibleForPainting() const {
   9196  return StyleVisibility()->IsVisible();
   9197 }
   9198 
   9199 bool nsIFrame::IsVisibleOrCollapsedForPainting() const {
   9200  return StyleVisibility()->IsVisibleOrCollapsed();
   9201 }
   9202 
   9203 /* virtual */
   9204 bool nsIFrame::IsEmpty() {
   9205  return IsHiddenByContentVisibilityOfInFlowParentForLayout();
   9206 }
   9207 
   9208 bool nsIFrame::CachedIsEmpty() {
   9209  NS_ASSERTION(!HasAnyStateBits(NS_FRAME_IS_DIRTY) ||
   9210                   IsHiddenByContentVisibilityOfInFlowParentForLayout(),
   9211               "Must only be called on reflowed lines or those hidden by "
   9212               "content-visibility.");
   9213  return IsEmpty();
   9214 }
   9215 
   9216 /* virtual */
   9217 bool nsIFrame::IsSelfEmpty() {
   9218  return IsHiddenByContentVisibilityOfInFlowParentForLayout();
   9219 }
   9220 
   9221 nsISelectionController* nsIFrame::GetSelectionController() const {
   9222  if (nsTextControlFrame* const tcf = GetContainingTextControlFrame()) {
   9223    return tcf->ControlElement()->GetSelectionController();
   9224  }
   9225  return static_cast<nsISelectionController*>(PresShell());
   9226 }
   9227 
   9228 int16_t nsIFrame::GetDisplaySelection() const {
   9229  nsISelectionController* const selCon = GetSelectionController();
   9230  if (MOZ_UNLIKELY(!selCon)) {
   9231    return nsISelectionController::SELECTION_OFF;
   9232  }
   9233  int16_t display = nsISelectionController::SELECTION_OFF;
   9234  selCon->GetDisplaySelection(&display);
   9235  return display;
   9236 }
   9237 
   9238 already_AddRefed<nsFrameSelection> nsIFrame::GetFrameSelection() {
   9239  RefPtr<nsFrameSelection> fs =
   9240      const_cast<nsFrameSelection*>(GetConstFrameSelection());
   9241  return fs.forget();
   9242 }
   9243 
   9244 const nsFrameSelection* nsIFrame::GetConstFrameSelection() const {
   9245  if (nsTextControlFrame* tcf = GetContainingTextControlFrame()) {
   9246    return tcf->GetOwnedFrameSelection();
   9247  }
   9248  return PresShell()->ConstFrameSelection();
   9249 }
   9250 
   9251 bool nsIFrame::IsFrameSelected() const {
   9252  NS_ASSERTION(!GetContent() || GetContent()->IsMaybeSelected(),
   9253               "use the public IsSelected() instead");
   9254  if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   9255    if (const ShadowRoot* shadowRoot =
   9256            GetContent()->GetShadowRootForSelection()) {
   9257      return shadowRoot->IsSelected(0, shadowRoot->GetChildCount());
   9258    }
   9259  }
   9260  return GetContent()->IsSelected(0, GetContent()->GetChildCount());
   9261 }
   9262 
   9263 nsresult nsIFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) {
   9264  MOZ_ASSERT(outPoint != nullptr, "Null parameter");
   9265  nsRect contentRect = GetContentRectRelativeToSelf();
   9266  nsPoint pt = contentRect.TopLeft();
   9267  if (mContent) {
   9268    nsIContent* newContent = mContent->GetParent();
   9269    if (newContent) {
   9270      const int32_t newOffset = newContent->ComputeIndexOf_Deprecated(mContent);
   9271 
   9272      // Find the direction of the frame from the EmbeddingLevelProperty,
   9273      // which is the resolved bidi level set in
   9274      // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
   9275      // If the embedding level isn't set, just use the CSS direction
   9276      // property.
   9277      bool hasBidiData;
   9278      FrameBidiData bidiData = GetProperty(BidiDataProperty(), &hasBidiData);
   9279      bool isRTL = hasBidiData
   9280                       ? bidiData.embeddingLevel.IsRTL()
   9281                       : StyleVisibility()->mDirection == StyleDirection::Rtl;
   9282      if ((!isRTL && inOffset > newOffset) ||
   9283          (isRTL && inOffset <= newOffset)) {
   9284        pt = contentRect.TopRight();
   9285      }
   9286    }
   9287  }
   9288  *outPoint = pt;
   9289  return NS_OK;
   9290 }
   9291 
   9292 nsresult nsIFrame::GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
   9293                                            nsTArray<nsRect>& aOutRect) {
   9294  /* no text */
   9295  return NS_ERROR_FAILURE;
   9296 }
   9297 
   9298 nsresult nsIFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
   9299                                                 bool inHint,
   9300                                                 int32_t* outFrameContentOffset,
   9301                                                 nsIFrame** outChildFrame) {
   9302  MOZ_ASSERT(outChildFrame && outFrameContentOffset, "Null parameter");
   9303  *outFrameContentOffset = (int32_t)inHint;
   9304  // the best frame to reflect any given offset would be a visible frame if
   9305  // possible i.e. we are looking for a valid frame to place the blinking caret
   9306  nsRect rect = GetRect();
   9307  if (!rect.width || !rect.height) {
   9308    // if we have a 0 width or height then lets look for another frame that
   9309    // possibly has the same content.  If we have no frames in flow then just
   9310    // let us return 'this' frame
   9311    nsIFrame* nextFlow = GetNextInFlow();
   9312    if (nextFlow) {
   9313      return nextFlow->GetChildFrameContainingOffset(
   9314          inContentOffset, inHint, outFrameContentOffset, outChildFrame);
   9315    }
   9316  }
   9317  *outChildFrame = this;
   9318  return NS_OK;
   9319 }
   9320 
   9321 //
   9322 // What I've pieced together about this routine:
   9323 // Starting with a block frame (from which a line frame can be gotten)
   9324 // and a line number, drill down and get the first/last selectable
   9325 // frame on that line, depending on aPos->mDirection.
   9326 // aOutSideLimit != 0 means ignore aLineStart, instead work from
   9327 // the end (if > 0) or beginning (if < 0).
   9328 //
   9329 static nsresult GetNextPrevLineFromBlockFrame(PeekOffsetStruct* aPos,
   9330                                              nsIFrame* aBlockFrame,
   9331                                              int32_t aLineStart,
   9332                                              int8_t aOutSideLimit) {
   9333  MOZ_ASSERT(aPos);
   9334  MOZ_ASSERT(aBlockFrame);
   9335 
   9336  nsPresContext* pc = aBlockFrame->PresContext();
   9337 
   9338  // magic numbers: aLineStart will be -1 for end of block, 0 will be start of
   9339  // block.
   9340 
   9341  aPos->mResultFrame = nullptr;
   9342  aPos->mResultContent = nullptr;
   9343  aPos->mAttach = aPos->mDirection == eDirNext ? CaretAssociationHint::After
   9344                                               : CaretAssociationHint::Before;
   9345 
   9346  AutoAssertNoDomMutations guard;
   9347  nsILineIterator* it = aBlockFrame->GetLineIterator();
   9348  if (!it) {
   9349    return NS_ERROR_FAILURE;
   9350  }
   9351  int32_t searchingLine = aLineStart;
   9352  int32_t countLines = it->GetNumLines();
   9353  if (aOutSideLimit > 0) {  // start at end
   9354    searchingLine = countLines;
   9355  } else if (aOutSideLimit < 0) {  // start at beginning
   9356    searchingLine = -1;            //"next" will be 0
   9357  } else if ((aPos->mDirection == eDirPrevious && searchingLine == 0) ||
   9358             (aPos->mDirection == eDirNext &&
   9359              searchingLine >= (countLines - 1))) {
   9360    // Not found.
   9361    return NS_ERROR_FAILURE;
   9362  }
   9363  nsIFrame* resultFrame = nullptr;
   9364  nsIFrame* farStoppingFrame = nullptr;  // we keep searching until we find a
   9365                                         // "this" frame then we go to next line
   9366  nsIFrame* nearStoppingFrame = nullptr;  // if we are backing up from edge,
   9367                                          // stop here
   9368  bool isBeforeFirstFrame, isAfterLastFrame;
   9369  bool found = false;
   9370 
   9371  const bool forceInEditableRegion =
   9372      aPos->mOptions.contains(PeekOffsetOption::ForceEditableRegion);
   9373  while (!found) {
   9374    if (aPos->mDirection == eDirPrevious) {
   9375      searchingLine--;
   9376    } else {
   9377      searchingLine++;
   9378    }
   9379    if ((aPos->mDirection == eDirPrevious && searchingLine < 0) ||
   9380        (aPos->mDirection == eDirNext && searchingLine >= countLines)) {
   9381      // we need to jump to new block frame.
   9382      return NS_ERROR_FAILURE;
   9383    }
   9384    {
   9385      auto line = it->GetLine(searchingLine).unwrap();
   9386      if (!line.mNumFramesOnLine) {
   9387        continue;
   9388      }
   9389      nsIFrame* firstFrame = nullptr;
   9390      nsIFrame* lastFrame = nullptr;
   9391      nsIFrame* frame = line.mFirstFrameOnLine;
   9392      int32_t i = line.mNumFramesOnLine;
   9393      do {
   9394        // If the caller wants a frame for a inclusive ancestor of the ancestor
   9395        // limiter, ignore frames for outside the limiter.
   9396        if (aPos->FrameContentIsInAncestorLimiter(frame)) {
   9397          if (!firstFrame) {
   9398            firstFrame = frame;
   9399          }
   9400          lastFrame = frame;
   9401        }
   9402        if (i == 1) {
   9403          break;
   9404        }
   9405        frame = frame->GetNextSibling();
   9406        if (!frame) {
   9407          NS_ERROR("GetLine promised more frames than could be found");
   9408          return NS_ERROR_FAILURE;
   9409        }
   9410      } while (--i);
   9411      if (!lastFrame) {
   9412        // If we're looking for an editable content frame, but all frames in the
   9413        // line are not in the specified editing host, return error because we
   9414        // must reach the editing host boundary.
   9415        return NS_ERROR_FAILURE;
   9416      }
   9417      // Don't enter into native anonymous subtrees.
   9418      if (!lastFrame->ContentIsRootOfNativeAnonymousSubtree()) {
   9419        nsIFrame::GetLastLeaf(&lastFrame);
   9420      }
   9421 
   9422      if (aPos->mDirection == eDirNext) {
   9423        nearStoppingFrame = firstFrame;
   9424        farStoppingFrame = lastFrame;
   9425      } else {
   9426        nearStoppingFrame = lastFrame;
   9427        farStoppingFrame = firstFrame;
   9428      }
   9429    }
   9430    nsPoint offset = aBlockFrame->GetOffsetToRootFrame();
   9431    nsPoint newDesiredPos =
   9432        aPos->mDesiredCaretPos -
   9433        offset;  // get desired position into blockframe coords
   9434    // TODO: nsILineIterator::FindFrameAt should take optional editing host
   9435    // parameter and if it's set, it should return the nearest editable frame
   9436    // for the editing host when the frame at the desired position is not
   9437    // editable.
   9438    nsresult rv = it->FindFrameAt(searchingLine, newDesiredPos, &resultFrame,
   9439                                  &isBeforeFirstFrame, &isAfterLastFrame);
   9440    if (NS_FAILED(rv)) {
   9441      continue;
   9442    }
   9443 
   9444    if (resultFrame) {
   9445      // If ancestor limiter is specified and we reached outside content of it,
   9446      // return error because we reached its element boundary.
   9447      if (!aPos->FrameContentIsInAncestorLimiter(resultFrame)) {
   9448        return NS_ERROR_FAILURE;
   9449      }
   9450      // Check to see if this is ANOTHER blockframe inside the other one that
   9451      // we should look inside.
   9452      if (resultFrame->CanProvideLineIterator() &&
   9453          IsRelevantBlockFrame(resultFrame)) {
   9454        aPos->mResultFrame = resultFrame;
   9455        return NS_OK;
   9456      }
   9457      // resultFrame is not a block frame
   9458      Maybe<nsFrameIterator> frameIterator;
   9459      frameIterator.emplace(
   9460          pc, resultFrame, nsFrameIterator::Type::PostOrder,
   9461          false,  // aVisual
   9462          aPos->mOptions.contains(PeekOffsetOption::StopAtScroller),
   9463          false,  // aFollowOOFs
   9464          false   // aSkipPopupChecks
   9465      );
   9466 
   9467      auto FoundValidFrame = [forceInEditableRegion, aPos](
   9468                                 const nsIFrame::ContentOffsets& aOffsets,
   9469                                 const nsIFrame* aFrame) {
   9470        if (!aOffsets.content) {
   9471          return false;
   9472        }
   9473        if (!aFrame->IsSelectable()) {
   9474          return false;
   9475        }
   9476        if (aPos->mAncestorLimiter &&
   9477            !aOffsets.content->IsInclusiveDescendantOf(
   9478                aPos->mAncestorLimiter)) {
   9479          return false;
   9480        }
   9481        if (forceInEditableRegion && !aOffsets.content->IsEditable()) {
   9482          return false;
   9483        }
   9484        return true;
   9485      };
   9486 
   9487      nsIFrame* storeOldResultFrame = resultFrame;
   9488      while (!found) {
   9489        nsPoint point;
   9490        nsRect tempRect = resultFrame->GetRect();
   9491        nsPoint offset = resultFrame->GetOffsetToRootFrame();
   9492        if (resultFrame->GetWritingMode().IsVertical()) {
   9493          point.y = aPos->mDesiredCaretPos.y;
   9494          point.x = tempRect.width + offset.x;
   9495        } else {
   9496          point.y = tempRect.height + offset.y;
   9497          point.x = aPos->mDesiredCaretPos.x;
   9498        }
   9499 
   9500        if (!resultFrame->IsViewportFrame()) {
   9501          nsPoint offset = resultFrame->GetOffsetToRootFrame();
   9502          nsIFrame::ContentOffsets offsets =
   9503              resultFrame->GetContentOffsetsFromPoint(
   9504                  point - offset, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
   9505          aPos->mResultContent = offsets.content;
   9506          aPos->mContentOffset = offsets.offset;
   9507          aPos->mAttach = offsets.associate;
   9508          if (FoundValidFrame(offsets, resultFrame)) {
   9509            found = true;
   9510            break;
   9511          }
   9512        }
   9513 
   9514        if (aPos->mDirection == eDirPrevious &&
   9515            resultFrame == farStoppingFrame) {
   9516          break;
   9517        }
   9518        if (aPos->mDirection == eDirNext && resultFrame == nearStoppingFrame) {
   9519          break;
   9520        }
   9521        // always try previous on THAT line if that fails go the other way
   9522        resultFrame = frameIterator->Traverse(/* aForward = */ false);
   9523        if (!resultFrame) {
   9524          return NS_ERROR_FAILURE;
   9525        }
   9526      }
   9527 
   9528      if (!found) {
   9529        resultFrame = storeOldResultFrame;
   9530        frameIterator.reset();
   9531        frameIterator.emplace(
   9532            pc, resultFrame, nsFrameIterator::Type::Leaf,
   9533            false,  // aVisual
   9534            aPos->mOptions.contains(PeekOffsetOption::StopAtScroller),
   9535            false,  // aFollowOOFs
   9536            false   // aSkipPopupChecks
   9537        );
   9538        MOZ_ASSERT(frameIterator);
   9539      }
   9540      while (!found) {
   9541        nsPoint point = aPos->mDesiredCaretPos;
   9542        nsPoint offset = resultFrame->GetOffsetToRootFrame();
   9543        nsIFrame::ContentOffsets offsets =
   9544            resultFrame->GetContentOffsetsFromPoint(
   9545                point - offset, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
   9546        aPos->mResultContent = offsets.content;
   9547        aPos->mContentOffset = offsets.offset;
   9548        aPos->mAttach = offsets.associate;
   9549        if (FoundValidFrame(offsets, resultFrame)) {
   9550          found = true;
   9551          aPos->mAttach = resultFrame == farStoppingFrame
   9552                              ? CaretAssociationHint::Before
   9553                              : CaretAssociationHint::After;
   9554          break;
   9555        }
   9556        if (aPos->mDirection == eDirPrevious &&
   9557            resultFrame == nearStoppingFrame) {
   9558          break;
   9559        }
   9560        if (aPos->mDirection == eDirNext && resultFrame == farStoppingFrame) {
   9561          break;
   9562        }
   9563        // previous didnt work now we try "next"
   9564        nsIFrame* tempFrame = frameIterator->Traverse(/* aForward = */ true);
   9565        if (!tempFrame) {
   9566          break;
   9567        }
   9568        resultFrame = tempFrame;
   9569      }
   9570      aPos->mResultFrame = resultFrame;
   9571    } else {
   9572      // we need to jump to new block frame.
   9573      aPos->mAmount = eSelectLine;
   9574      aPos->mStartOffset = 0;
   9575      aPos->mAttach = aPos->mDirection == eDirNext
   9576                          ? CaretAssociationHint::Before
   9577                          : CaretAssociationHint::After;
   9578      if (aPos->mDirection == eDirPrevious) {
   9579        aPos->mStartOffset = -1;  // start from end
   9580      }
   9581      return aBlockFrame->PeekOffset(aPos);
   9582    }
   9583  }
   9584  return NS_OK;
   9585 }
   9586 
   9587 nsIFrame::CaretPosition nsIFrame::GetExtremeCaretPosition(bool aStart) {
   9588  CaretPosition result;
   9589 
   9590  FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, 0);
   9591  FrameContentRange range = GetRangeForFrame(targetFrame.frame);
   9592  result.mResultContent = range.content;
   9593  result.mContentOffset = aStart ? range.start : range.end;
   9594  return result;
   9595 }
   9596 
   9597 // If this is a preformatted text frame, see if it ends with a newline
   9598 static nsContentAndOffset FindLineBreakInText(nsIFrame* aFrame,
   9599                                              nsDirection aDirection) {
   9600  nsContentAndOffset result;
   9601 
   9602  if (aFrame->IsGeneratedContentFrame() ||
   9603      !aFrame->HasSignificantTerminalNewline()) {
   9604    return result;
   9605  }
   9606 
   9607  int32_t endOffset = aFrame->GetOffsets().second;
   9608  result.mContent = aFrame->GetContent();
   9609  result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
   9610  return result;
   9611 }
   9612 
   9613 // Find the first (or last) descendant of the given frame
   9614 // which is either a block-level frame or a BRFrame, or some other kind of break
   9615 // which stops the line.
   9616 static nsContentAndOffset FindLineBreakingFrame(nsIFrame* aFrame,
   9617                                                nsDirection aDirection) {
   9618  nsContentAndOffset result;
   9619 
   9620  if (aFrame->IsGeneratedContentFrame()) {
   9621    return result;
   9622  }
   9623 
   9624  // Treat form controls and other replaced inline level elements as inline
   9625  // leaves.
   9626  if (aFrame->IsReplaced() && aFrame->IsInlineOutside() &&
   9627      !aFrame->IsBrFrame() && !aFrame->IsTextFrame()) {
   9628    return result;
   9629  }
   9630 
   9631  // Check the frame itself
   9632  // Fall through block-in-inline split frames because their mContent is
   9633  // the content of the inline frames they were created from. The
   9634  // first/last child of such frames is the real block frame we're
   9635  // looking for.
   9636  if ((IsRelevantBlockFrame(aFrame) &&
   9637       !aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) ||
   9638      aFrame->IsBrFrame()) {
   9639    nsIContent* content = aFrame->GetContent();
   9640    result.mContent = content->GetParent();
   9641    // In some cases (bug 310589, bug 370174) we end up here with a null
   9642    // content. This probably shouldn't ever happen, but since it sometimes
   9643    // does, we want to avoid crashing here.
   9644    NS_ASSERTION(result.mContent, "Unexpected orphan content");
   9645    if (result.mContent) {
   9646      result.mOffset = result.mContent->ComputeIndexOf_Deprecated(content) +
   9647                       (aDirection == eDirPrevious ? 1 : 0);
   9648    }
   9649    return result;
   9650  }
   9651 
   9652  result = FindLineBreakInText(aFrame, aDirection);
   9653  if (result.mContent) {
   9654    return result;
   9655  }
   9656 
   9657  // Iterate over children and call ourselves recursively
   9658  if (aDirection == eDirPrevious) {
   9659    nsIFrame* child = aFrame->PrincipalChildList().LastChild();
   9660    while (child && !result.mContent) {
   9661      result = FindLineBreakingFrame(child, aDirection);
   9662      child = child->GetPrevSibling();
   9663    }
   9664  } else {  // eDirNext
   9665    nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
   9666    while (child && !result.mContent) {
   9667      result = FindLineBreakingFrame(child, aDirection);
   9668      child = child->GetNextSibling();
   9669    }
   9670  }
   9671  return result;
   9672 }
   9673 
   9674 nsresult nsIFrame::PeekOffsetForParagraph(PeekOffsetStruct* aPos) {
   9675  nsIFrame* frame = this;
   9676  nsContentAndOffset blockFrameOrBR;
   9677  blockFrameOrBR.mContent = nullptr;
   9678  bool reachedLimit = IsRelevantBlockFrame(frame) || IsEditingHost(frame);
   9679 
   9680  auto traverse = [&aPos](nsIFrame* current) {
   9681    return aPos->mDirection == eDirPrevious ? current->GetPrevSibling()
   9682                                            : current->GetNextSibling();
   9683  };
   9684 
   9685  // Go through containing frames until reaching a block frame.
   9686  // In each step, search the previous (or next) siblings for the closest
   9687  // "stop frame" (a block frame or a BRFrame).
   9688  // If found, set it to be the selection boundary and abort.
   9689  while (!reachedLimit) {
   9690    nsIFrame* parent = frame->GetParent();
   9691    // Treat a frame associated with the root content as if it were a block
   9692    // frame.
   9693    if (!frame->mContent || !frame->mContent->GetParent()) {
   9694      reachedLimit = true;
   9695      break;
   9696    }
   9697 
   9698    if (aPos->mDirection == eDirNext) {
   9699      // Try to find our own line-break before looking at our siblings.
   9700      blockFrameOrBR = FindLineBreakInText(frame, eDirNext);
   9701    }
   9702 
   9703    nsIFrame* sibling = traverse(frame);
   9704    while (sibling && !blockFrameOrBR.mContent) {
   9705      blockFrameOrBR = FindLineBreakingFrame(sibling, aPos->mDirection);
   9706      sibling = traverse(sibling);
   9707    }
   9708    if (blockFrameOrBR.mContent) {
   9709      aPos->mResultContent = blockFrameOrBR.mContent;
   9710      aPos->mContentOffset = blockFrameOrBR.mOffset;
   9711      break;
   9712    }
   9713    frame = parent;
   9714    reachedLimit =
   9715        frame && (IsRelevantBlockFrame(frame) || IsEditingHost(frame));
   9716  }
   9717 
   9718  if (reachedLimit) {  // no "stop frame" found
   9719    aPos->mResultContent = frame->GetContent();
   9720    if (aPos->mResultContent) {
   9721      if (ShadowRoot* shadowRoot =
   9722              aPos->mResultContent->GetShadowRootForSelection()) {
   9723        // Even if there's no children for this node,
   9724        // the elements inside the shadow root is still
   9725        // selectable
   9726        aPos->mResultContent = shadowRoot;
   9727      }
   9728    }
   9729    if (aPos->mDirection == eDirPrevious) {
   9730      aPos->mContentOffset = 0;
   9731    } else if (aPos->mResultContent) {
   9732      aPos->mContentOffset = aPos->mResultContent->GetChildCount();
   9733    }
   9734  }
   9735  return NS_OK;
   9736 }
   9737 
   9738 // Determine movement direction relative to frame
   9739 static bool IsMovingInFrameDirection(const nsIFrame* frame,
   9740                                     nsDirection aDirection, bool aVisual) {
   9741  bool isReverseDirection =
   9742      aVisual && nsBidiPresUtils::IsReversedDirectionFrame(frame);
   9743  return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
   9744 }
   9745 
   9746 // Determines "are we looking for a boundary between whitespace and
   9747 // non-whitespace (in the direction we're moving in)". It is true when moving
   9748 // forward and looking for a beginning of a word, or when moving backwards and
   9749 // looking for an end of a word.
   9750 static bool ShouldWordSelectionEatSpace(const PeekOffsetStruct& aPos) {
   9751  if (aPos.mWordMovementType != eDefaultBehavior) {
   9752    // aPos->mWordMovementType possible values:
   9753    //       eEndWord: eat the space if we're moving backwards
   9754    //       eStartWord: eat the space if we're moving forwards
   9755    return (aPos.mWordMovementType == eEndWord) ==
   9756           (aPos.mDirection == eDirPrevious);
   9757  }
   9758  // Use the hidden preference which is based on operating system
   9759  // behavior. This pref only affects whether moving forward by word
   9760  // should go to the end of this word or start of the next word. When
   9761  // going backwards, the start of the word is always used, on every
   9762  // operating system.
   9763  return aPos.mDirection == eDirNext &&
   9764         StaticPrefs::layout_word_select_eat_space_to_next_word();
   9765 }
   9766 
   9767 enum class OffsetIsAtLineEdge : bool { No, Yes };
   9768 
   9769 static void SetPeekResultFromFrame(PeekOffsetStruct& aPos, nsIFrame* aFrame,
   9770                                   int32_t aOffset,
   9771                                   OffsetIsAtLineEdge aAtLineEdge) {
   9772  FrameContentRange range = GetRangeForFrame(aFrame);
   9773  aPos.mResultFrame = aFrame;
   9774  aPos.mResultContent = range.content;
   9775  // Output offset is relative to content, not frame
   9776  aPos.mContentOffset =
   9777      aOffset < 0 ? range.end + aOffset + 1 : range.start + aOffset;
   9778  // Ensure we don't go past the range. This is important if aFrame is empty.
   9779  aPos.mContentOffset = std::clamp(aPos.mContentOffset, range.start, range.end);
   9780  if (aAtLineEdge == OffsetIsAtLineEdge::Yes) {
   9781    aPos.mAttach = aPos.mContentOffset == range.start
   9782                       ? CaretAssociationHint::After
   9783                       : CaretAssociationHint::Before;
   9784  }
   9785 }
   9786 
   9787 void nsIFrame::SelectablePeekReport::TransferTo(PeekOffsetStruct& aPos) const {
   9788  return SetPeekResultFromFrame(aPos, mFrame, mOffset, OffsetIsAtLineEdge::No);
   9789 }
   9790 
   9791 nsIFrame::SelectablePeekReport::SelectablePeekReport(
   9792    const mozilla::GenericErrorResult<nsresult>&& aErr) {
   9793  MOZ_ASSERT(NS_FAILED(aErr.operator nsresult()));
   9794  // Return an empty report
   9795 }
   9796 
   9797 nsresult nsIFrame::PeekOffsetForCharacter(PeekOffsetStruct* aPos,
   9798                                          int32_t aOffset) {
   9799  SelectablePeekReport current{this, aOffset};
   9800 
   9801  nsIFrame::FrameSearchResult peekSearchState = CONTINUE;
   9802 
   9803  const bool forceEditableRegion =
   9804      aPos->mOptions.contains(PeekOffsetOption::ForceEditableRegion);
   9805 
   9806  while (peekSearchState != FOUND) {
   9807    const bool movingInFrameDirection = IsMovingInFrameDirection(
   9808        current.mFrame, aPos->mDirection,
   9809        aPos->mOptions.contains(PeekOffsetOption::Visual));
   9810 
   9811    if (current.mJumpedLine) {
   9812      // If we jumped lines, it's as if we found a character, but we still need
   9813      // to eat non-renderable content on the new line.
   9814      peekSearchState = current.PeekOffsetNoAmount(movingInFrameDirection);
   9815    } else {
   9816      PeekOffsetCharacterOptions options;
   9817      options.mRespectClusters = aPos->mAmount == eSelectCluster;
   9818      peekSearchState =
   9819          current.PeekOffsetCharacter(movingInFrameDirection, options);
   9820      if (peekSearchState == FOUND && forceEditableRegion &&
   9821          !current.mFrame->ContentIsEditable()) {
   9822        // Treat non-editable content as unselectable.  Note that we may need to
   9823        // set mJumpedLine propery even if it's not editable.  Therefore, we
   9824        // cannot skip the above call.
   9825        peekSearchState = CONTINUE_UNSELECTABLE;
   9826      }
   9827    }
   9828 
   9829    current.mMovedOverNonSelectableText |=
   9830        peekSearchState == CONTINUE_UNSELECTABLE;
   9831 
   9832    if (peekSearchState != FOUND) {
   9833      SelectablePeekReport next = current.mFrame->GetFrameFromDirection(*aPos);
   9834      if (next.Failed()) {
   9835        return NS_ERROR_FAILURE;
   9836      }
   9837      next.mJumpedLine |= current.mJumpedLine;
   9838      next.mMovedOverNonSelectableText |= current.mMovedOverNonSelectableText;
   9839      next.mHasSelectableFrame |= current.mHasSelectableFrame;
   9840      current = next;
   9841    }
   9842 
   9843    // Found frame, but because we moved over non selectable text we want
   9844    // the offset to be at the frame edge. Note that if we are extending the
   9845    // selection, this doesn't matter.
   9846    if (peekSearchState == FOUND && current.mMovedOverNonSelectableText &&
   9847        (!aPos->mOptions.contains(PeekOffsetOption::Extend) ||
   9848         current.mHasSelectableFrame)) {
   9849      auto [start, end] = current.mFrame->GetOffsets();
   9850      current.mOffset = aPos->mDirection == eDirNext ? 0 : end - start;
   9851    }
   9852  }
   9853 
   9854  // Set outputs
   9855  current.TransferTo(*aPos);
   9856  // If we're dealing with a text frame and moving backward positions us at
   9857  // the end of that line, decrease the offset by one to make sure that
   9858  // we're placed before the linefeed character on the previous line.
   9859  if (current.mOffset < 0 && current.mJumpedLine &&
   9860      aPos->mDirection == eDirPrevious &&
   9861      current.mFrame->HasSignificantTerminalNewline() &&
   9862      !current.mIgnoredBrFrame) {
   9863    --aPos->mContentOffset;
   9864  }
   9865  return NS_OK;
   9866 }
   9867 
   9868 nsresult nsIFrame::PeekOffsetForWord(PeekOffsetStruct* aPos, int32_t aOffset) {
   9869  SelectablePeekReport current{this, aOffset};
   9870  bool shouldStopAtHardBreak =
   9871      aPos->mWordMovementType == eDefaultBehavior &&
   9872      StaticPrefs::layout_word_select_eat_space_to_next_word();
   9873  bool wordSelectEatSpace = ShouldWordSelectionEatSpace(*aPos);
   9874 
   9875  PeekWordState state;
   9876  while (true) {
   9877    bool movingInFrameDirection = IsMovingInFrameDirection(
   9878        current.mFrame, aPos->mDirection,
   9879        aPos->mOptions.contains(PeekOffsetOption::Visual));
   9880 
   9881    FrameSearchResult searchResult = current.mFrame->PeekOffsetWord(
   9882        movingInFrameDirection, wordSelectEatSpace,
   9883        aPos->mOptions.contains(PeekOffsetOption::IsKeyboardSelect),
   9884        &current.mOffset, &state,
   9885        !aPos->mOptions.contains(PeekOffsetOption::PreserveSpaces));
   9886    if (searchResult == FOUND) {
   9887      break;
   9888    }
   9889 
   9890    SelectablePeekReport next = [&]() {
   9891      PeekOffsetOptions options = aPos->mOptions;
   9892      if (state.mSawInlineCharacter) {
   9893        // If we've already found a character, we don't want to stop at
   9894        // placeholder frame boundary if there is in the word.
   9895        options += PeekOffsetOption::StopAtPlaceholder;
   9896      }
   9897      return current.mFrame->GetFrameFromDirection(aPos->mDirection, options,
   9898                                                   aPos->mAncestorLimiter);
   9899    }();
   9900    if (next.Failed()) {
   9901      // If we've crossed the line boundary, check to make sure that we
   9902      // have not consumed a trailing newline as whitespace if it's
   9903      // significant.
   9904      if (next.mJumpedLine && wordSelectEatSpace &&
   9905          current.mFrame->HasSignificantTerminalNewline() &&
   9906          current.mFrame->StyleText()->mWhiteSpaceCollapse !=
   9907              StyleWhiteSpaceCollapse::PreserveBreaks) {
   9908        current.mOffset -= 1;
   9909      }
   9910      break;
   9911    }
   9912 
   9913    if ((next.mJumpedLine || next.mFoundPlaceholder) && !wordSelectEatSpace &&
   9914        state.mSawBeforeType) {
   9915      // We can't jump lines if we're looking for whitespace following
   9916      // non-whitespace, and we already encountered non-whitespace.
   9917      break;
   9918    }
   9919 
   9920    if (shouldStopAtHardBreak && next.mJumpedHardBreak) {
   9921      /**
   9922       * Prev, always: Jump and stop right there
   9923       * Next, saw inline: just stop
   9924       * Next, no inline: Jump and consume whitespaces
   9925       */
   9926      if (aPos->mDirection == eDirPrevious) {
   9927        // Try moving to the previous line if exists
   9928        current.TransferTo(*aPos);
   9929        current.mFrame->PeekOffsetForCharacter(aPos, current.mOffset);
   9930        return NS_OK;
   9931      }
   9932      if (state.mSawInlineCharacter || current.mJumpedHardBreak) {
   9933        if (current.mFrame->HasSignificantTerminalNewline()) {
   9934          current.mOffset -= 1;
   9935        }
   9936        current.TransferTo(*aPos);
   9937        return NS_OK;
   9938      }
   9939      // Mark the state as whitespace and continue
   9940      state.Update(false, true);
   9941    }
   9942 
   9943    if (next.mJumpedLine) {
   9944      state.mContext.Truncate();
   9945    }
   9946    current = next;
   9947    // Jumping a line is equivalent to encountering whitespace
   9948    // This affects only when it already met an actual character
   9949    if (wordSelectEatSpace && next.mJumpedLine) {
   9950      state.SetSawBeforeType();
   9951    }
   9952  }
   9953 
   9954  // Set outputs
   9955  current.TransferTo(*aPos);
   9956  return NS_OK;
   9957 }
   9958 
   9959 static nsIFrame* GetFirstSelectableDescendantWithLineIterator(
   9960    const PeekOffsetStruct& aPeekOffsetStruct, nsIFrame* aParentFrame) {
   9961  const bool forceEditableRegion = aPeekOffsetStruct.mOptions.contains(
   9962      PeekOffsetOption::ForceEditableRegion);
   9963  auto FoundValidFrame = [aPeekOffsetStruct,
   9964                          forceEditableRegion](const nsIFrame* aFrame) {
   9965    if (!aFrame->IsSelectable()) {
   9966      return false;
   9967    }
   9968    if (!aPeekOffsetStruct.FrameContentIsInAncestorLimiter(aFrame)) {
   9969      return false;
   9970    }
   9971    if (forceEditableRegion && !aFrame->ContentIsEditable()) {
   9972      return false;
   9973    }
   9974    return true;
   9975  };
   9976 
   9977  for (nsIFrame* child : aParentFrame->PrincipalChildList()) {
   9978    // some children may not be selectable, e.g. :before / :after pseudoelements
   9979    // content with user-select: none, or contenteditable="false"
   9980    // we need to skip them
   9981    if (child->CanProvideLineIterator() && FoundValidFrame(child)) {
   9982      return child;
   9983    }
   9984    if (nsIFrame* nested = GetFirstSelectableDescendantWithLineIterator(
   9985            aPeekOffsetStruct, child)) {
   9986      return nested;
   9987    }
   9988  }
   9989  return nullptr;
   9990 }
   9991 
   9992 nsresult nsIFrame::PeekOffsetForLine(PeekOffsetStruct* aPos) {
   9993  nsIFrame* blockFrame = this;
   9994  nsresult result = NS_ERROR_FAILURE;
   9995 
   9996  // outer loop
   9997  // moving to a next block when no more blocks are available in a subtree
   9998  AutoAssertNoDomMutations guard;
   9999  while (NS_FAILED(result)) {
  10000    auto [newBlock, lineFrame] = blockFrame->GetContainingBlockForLine(
  10001        aPos->mOptions.contains(PeekOffsetOption::StopAtScroller));
  10002    if (!newBlock) {
  10003      return NS_ERROR_FAILURE;
  10004    }
  10005    // FYI: If the editing host is an inline element, the block frame content
  10006    // may be either not editable or editable but belonging to different editing
  10007    // host.
  10008    blockFrame = newBlock;
  10009    nsILineIterator* iter = blockFrame->GetLineIterator();
  10010    int32_t thisLine = iter->FindLineContaining(lineFrame);
  10011    if (NS_WARN_IF(thisLine < 0)) {
  10012      return NS_ERROR_FAILURE;
  10013    }
  10014 
  10015    int8_t edgeCase = 0;  // no edge case. This should look at thisLine
  10016 
  10017    // this part will find a frame or a block frame. If it's a block frame
  10018    // it will "drill down" to find a viable frame or it will return an
  10019    // error.
  10020    nsIFrame* lastFrame = this;
  10021 
  10022    // inner loop - crawling the frames within a specific block subtree
  10023    while (true) {
  10024      result =
  10025          GetNextPrevLineFromBlockFrame(aPos, blockFrame, thisLine, edgeCase);
  10026      // we came back to same spot! keep going
  10027      if (NS_SUCCEEDED(result) &&
  10028          (!aPos->mResultFrame || aPos->mResultFrame == lastFrame)) {
  10029        aPos->mResultFrame = nullptr;
  10030        lastFrame = nullptr;
  10031        if (aPos->mDirection == eDirPrevious) {
  10032          thisLine--;
  10033        } else {
  10034          thisLine++;
  10035        }
  10036        continue;
  10037      }
  10038 
  10039      if (NS_FAILED(result)) {
  10040        break;
  10041      }
  10042 
  10043      lastFrame = aPos->mResultFrame;  // set last frame
  10044      /* SPECIAL CHECK FOR NAVIGATION INTO TABLES
  10045       * when we hit a frame which doesn't have line iterator, we need to
  10046       * drill down and find a child with the line iterator to prevent the
  10047       * crawling process to prematurely finish. Note that this is only sound if
  10048       * we're guaranteed to not have multiple children implementing
  10049       * LineIterator.
  10050       *
  10051       * So far known cases are:
  10052       * 1) table wrapper (drill down into table row group)
  10053       * 2) table cell (drill down into its only anon child)
  10054       */
  10055      const bool shouldDrillIntoChildren =
  10056          aPos->mResultFrame->IsTableWrapperFrame() ||
  10057          aPos->mResultFrame->IsTableCellFrame();
  10058 
  10059      if (shouldDrillIntoChildren) {
  10060        nsIFrame* child = GetFirstSelectableDescendantWithLineIterator(
  10061            *aPos, aPos->mResultFrame);
  10062        if (child) {
  10063          aPos->mResultFrame = child;
  10064        }
  10065      }
  10066 
  10067      if (!aPos->mResultFrame->CanProvideLineIterator()) {
  10068        // no more selectable content at this level
  10069        break;
  10070      }
  10071 
  10072      if (aPos->mResultFrame == blockFrame) {
  10073        // Make sure block element is not the same as the one we had before.
  10074        break;
  10075      }
  10076 
  10077      // we've struck another block element with selectable content!
  10078      if (aPos->mDirection == eDirPrevious) {
  10079        edgeCase = 1;  // far edge, search from end backwards
  10080      } else {
  10081        edgeCase = -1;  // near edge search from beginning onwards
  10082      }
  10083      thisLine = 0;  // this line means nothing now.
  10084      // everything else means something so keep looking "inside" the
  10085      // block
  10086      blockFrame = aPos->mResultFrame;
  10087    }
  10088  }
  10089  return result;
  10090 }
  10091 
  10092 nsresult nsIFrame::PeekOffsetForLineEdge(PeekOffsetStruct* aPos) {
  10093  // Adjusted so that the caret can't get confused when content changes
  10094  nsIFrame* frame = AdjustFrameForSelectionStyles(this);
  10095  // FIXME: Use PeekOffsetStruct::mAncestorLimiter instead.
  10096  Element* editingHost = frame->GetContent()->GetEditingHost();
  10097 
  10098  auto [blockFrame, lineFrame] = frame->GetContainingBlockForLine(
  10099      aPos->mOptions.contains(PeekOffsetOption::StopAtScroller));
  10100  if (!blockFrame) {
  10101    return NS_ERROR_FAILURE;
  10102  }
  10103  AutoAssertNoDomMutations guard;
  10104  nsILineIterator* it = blockFrame->GetLineIterator();
  10105  int32_t thisLine = it->FindLineContaining(lineFrame);
  10106  if (thisLine < 0) {
  10107    return NS_ERROR_FAILURE;
  10108  }
  10109 
  10110  nsIFrame* baseFrame = nullptr;
  10111  bool endOfLine = eSelectEndLine == aPos->mAmount;
  10112 
  10113  if (aPos->mOptions.contains(PeekOffsetOption::Visual) &&
  10114      PresContext()->BidiEnabled()) {
  10115    nsIFrame* firstFrame;
  10116    bool isReordered;
  10117    nsIFrame* lastFrame;
  10118    MOZ_TRY(
  10119        it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame));
  10120    baseFrame = endOfLine ? lastFrame : firstFrame;
  10121  } else {
  10122    auto line = it->GetLine(thisLine).unwrap();
  10123 
  10124    nsIFrame* frame = line.mFirstFrameOnLine;
  10125    bool lastFrameWasEditable = false;
  10126    for (int32_t count = line.mNumFramesOnLine; count;
  10127         --count, frame = frame->GetNextSibling()) {
  10128      if (frame->IsGeneratedContentFrame()) {
  10129        continue;
  10130      }
  10131      // When jumping to the end of the line with the "end" key,
  10132      // try to skip over brFrames
  10133      if (endOfLine && line.mNumFramesOnLine > 1 && frame->IsBrFrame() &&
  10134          lastFrameWasEditable == frame->GetContent()->IsEditable()) {
  10135        continue;
  10136      }
  10137      lastFrameWasEditable =
  10138          frame->GetContent() && frame->GetContent()->IsEditable();
  10139      baseFrame = frame;
  10140      if (!endOfLine) {
  10141        break;
  10142      }
  10143    }
  10144  }
  10145  if (!baseFrame) {
  10146    return NS_ERROR_FAILURE;
  10147  }
  10148  // Make sure we are not leaving our inline editing host if exists
  10149  if (editingHost) {
  10150    if (nsIFrame* frame = editingHost->GetPrimaryFrame()) {
  10151      if (frame->IsInlineOutside() &&
  10152          !editingHost->Contains(baseFrame->GetContent())) {
  10153        baseFrame = frame;
  10154        if (endOfLine) {
  10155          baseFrame = baseFrame->LastContinuation();
  10156        }
  10157      }
  10158    }
  10159  }
  10160  FrameTarget targetFrame = DrillDownToSelectionFrame(
  10161      baseFrame, endOfLine, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
  10162  SetPeekResultFromFrame(*aPos, targetFrame.frame, endOfLine ? -1 : 0,
  10163                         OffsetIsAtLineEdge::Yes);
  10164  if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) {
  10165    // Do not position the caret after the terminating newline if we're
  10166    // trying to move to the end of line (see bug 596506)
  10167    --aPos->mContentOffset;
  10168  }
  10169  if (!aPos->mResultContent) {
  10170    return NS_ERROR_FAILURE;
  10171  }
  10172  return NS_OK;
  10173 }
  10174 
  10175 nsresult nsIFrame::PeekOffset(PeekOffsetStruct* aPos) {
  10176  MOZ_ASSERT(aPos);
  10177 
  10178  if (NS_WARN_IF(HasAnyStateBits(NS_FRAME_IS_DIRTY))) {
  10179    // FIXME(Bug 1654362): <caption> currently can remain dirty.
  10180    return NS_ERROR_UNEXPECTED;
  10181  }
  10182 
  10183  // Translate content offset to be relative to frame
  10184  int32_t offset = aPos->mStartOffset - GetRangeForFrame(this).start;
  10185 
  10186  switch (aPos->mAmount) {
  10187    case eSelectCharacter:
  10188    case eSelectCluster:
  10189      return PeekOffsetForCharacter(aPos, offset);
  10190    case eSelectWordNoSpace:
  10191      // eSelectWordNoSpace means that we should not be eating any whitespace
  10192      // when moving to the adjacent word.  This means that we should set aPos->
  10193      // mWordMovementType to eEndWord if we're moving forwards, and to
  10194      // eStartWord if we're moving backwards.
  10195      if (aPos->mDirection == eDirPrevious) {
  10196        aPos->mWordMovementType = eStartWord;
  10197      } else {
  10198        aPos->mWordMovementType = eEndWord;
  10199      }
  10200      // Intentionally fall through the eSelectWord case.
  10201      [[fallthrough]];
  10202    case eSelectWord:
  10203      return PeekOffsetForWord(aPos, offset);
  10204    case eSelectLine:
  10205      return PeekOffsetForLine(aPos);
  10206    case eSelectBeginLine:
  10207    case eSelectEndLine:
  10208      return PeekOffsetForLineEdge(aPos);
  10209    case eSelectParagraph:
  10210      return PeekOffsetForParagraph(aPos);
  10211    default: {
  10212      NS_ASSERTION(false, "Invalid amount");
  10213      return NS_ERROR_FAILURE;
  10214    }
  10215  }
  10216 }
  10217 
  10218 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetNoAmount(bool aForward,
  10219                                                         int32_t* aOffset) {
  10220  NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
  10221  // Sure, we can stop right here.
  10222  return FOUND;
  10223 }
  10224 
  10225 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetCharacter(
  10226    bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) {
  10227  NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
  10228  int32_t startOffset = *aOffset;
  10229  // A negative offset means "end of frame", which in our case means offset 1.
  10230  if (startOffset < 0) {
  10231    startOffset = 1;
  10232  }
  10233  if (aForward == (startOffset == 0)) {
  10234    // We're before the frame and moving forward, or after it and moving
  10235    // backwards: skip to the other side and we're done.
  10236    *aOffset = 1 - startOffset;
  10237    return FOUND;
  10238  }
  10239  return CONTINUE;
  10240 }
  10241 
  10242 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetWord(
  10243    bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
  10244    int32_t* aOffset, PeekWordState* aState, bool /*aTrimSpaces*/) {
  10245  NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
  10246  int32_t startOffset = *aOffset;
  10247  // This isn't text, so truncate the context
  10248  aState->mContext.Truncate();
  10249  if (startOffset < 0) {
  10250    startOffset = 1;
  10251  }
  10252  if (aForward == (startOffset == 0)) {
  10253    // We're before the frame and moving forward, or after it and moving
  10254    // backwards. If we're looking for non-whitespace, we found it (without
  10255    // skipping this frame).
  10256    if (!aState->mAtStart) {
  10257      if (aState->mLastCharWasPunctuation) {
  10258        // We're not punctuation, so this is a punctuation boundary.
  10259        if (BreakWordBetweenPunctuation(aState, aForward, false, false,
  10260                                        aIsKeyboardSelect)) {
  10261          return FOUND;
  10262        }
  10263      } else {
  10264        // This is not a punctuation boundary.
  10265        if (aWordSelectEatSpace && aState->mSawBeforeType) {
  10266          return FOUND;
  10267        }
  10268      }
  10269    }
  10270    // Otherwise skip to the other side and note that we encountered
  10271    // non-whitespace.
  10272    *aOffset = 1 - startOffset;
  10273    aState->Update(false,  // not punctuation
  10274                   false   // not whitespace
  10275    );
  10276    if (!aWordSelectEatSpace) {
  10277      aState->SetSawBeforeType();
  10278    }
  10279  }
  10280  return CONTINUE;
  10281 }
  10282 
  10283 // static
  10284 bool nsIFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
  10285                                           bool aForward, bool aPunctAfter,
  10286                                           bool aWhitespaceAfter,
  10287                                           bool aIsKeyboardSelect) {
  10288  NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation,
  10289               "Call this only at punctuation boundaries");
  10290  if (aState->mLastCharWasWhitespace) {
  10291    // We always stop between whitespace and punctuation
  10292    return true;
  10293  }
  10294  if (!StaticPrefs::layout_word_select_stop_at_punctuation()) {
  10295    // When this pref is false, we never stop at a punctuation boundary unless
  10296    // it's followed by whitespace (in the relevant direction).
  10297    return aWhitespaceAfter;
  10298  }
  10299  if (!aIsKeyboardSelect) {
  10300    // mouse caret movement (e.g. word selection) always stops at every
  10301    // punctuation boundary
  10302    return true;
  10303  }
  10304  bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
  10305  if (!afterPunct) {
  10306    // keyboard caret movement only stops after punctuation (in content order)
  10307    return false;
  10308  }
  10309  // Stop only if we've seen some non-punctuation since the last whitespace;
  10310  // don't stop after punctuation that follows whitespace.
  10311  return aState->mSeenNonPunctuationSinceWhitespace;
  10312 }
  10313 
  10314 std::pair<nsIFrame*, nsIFrame*> nsIFrame::GetContainingBlockForLine(
  10315    bool aLockScroll) const {
  10316  const nsIFrame* parentFrame = this;
  10317  const nsIFrame* frame;
  10318  while (parentFrame) {
  10319    frame = parentFrame;
  10320    if (frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
  10321      // if we are searching for a frame that is not in flow we will not find
  10322      // it. we must instead look for its placeholder
  10323      if (frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
  10324        // abspos continuations don't have placeholders, get the fif
  10325        frame = frame->FirstInFlow();
  10326      }
  10327      frame = frame->GetPlaceholderFrame();
  10328      if (!frame) {
  10329        return std::pair(nullptr, nullptr);
  10330      }
  10331    }
  10332    parentFrame = frame->GetParent();
  10333    if (parentFrame) {
  10334      if (aLockScroll && parentFrame->IsScrollContainerFrame()) {
  10335        return std::pair(nullptr, nullptr);
  10336      }
  10337      if (parentFrame->CanProvideLineIterator()) {
  10338        return std::pair(const_cast<nsIFrame*>(parentFrame),
  10339                         const_cast<nsIFrame*>(frame));
  10340      }
  10341    }
  10342  }
  10343  return std::pair(nullptr, nullptr);
  10344 }
  10345 
  10346 Result<bool, nsresult> nsIFrame::IsVisuallyAtLineEdge(
  10347    nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
  10348  auto line = aLineIterator->GetLine(aLine).unwrap();
  10349 
  10350  const bool lineIsRTL = aLineIterator->IsLineIteratorFlowRTL();
  10351 
  10352  nsIFrame *firstFrame = nullptr, *lastFrame = nullptr;
  10353  bool isReordered = false;
  10354  MOZ_TRY(aLineIterator->CheckLineOrder(aLine, &isReordered, &firstFrame,
  10355                                        &lastFrame));
  10356  if (!firstFrame || !lastFrame) {
  10357    return true;  // XXX: Why true?  We check whether `this` is at the edge...
  10358  }
  10359 
  10360  nsIFrame* leftmostFrame = lineIsRTL ? lastFrame : firstFrame;
  10361  nsIFrame* rightmostFrame = lineIsRTL ? firstFrame : lastFrame;
  10362  auto FrameIsRTL = [](nsIFrame* aFrame) {
  10363    return nsBidiPresUtils::FrameDirection(aFrame) ==
  10364           mozilla::intl::BidiDirection::RTL;
  10365  };
  10366  if (!lineIsRTL == (aDirection == eDirPrevious)) {
  10367    nsIFrame* maybeLeftmostFrame = leftmostFrame;
  10368    for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
  10369      if (maybeLeftmostFrame == this) {
  10370        return true;
  10371      }
  10372      // If left edge of the line starts with placeholder frames, we can ignore
  10373      // them and should keep checking the following frames.
  10374      if (!maybeLeftmostFrame->IsPlaceholderFrame()) {
  10375        if ((FrameIsRTL(maybeLeftmostFrame) == lineIsRTL) ==
  10376            (aDirection == eDirPrevious)) {
  10377          nsIFrame::GetFirstLeaf(&maybeLeftmostFrame);
  10378        } else {
  10379          nsIFrame::GetLastLeaf(&maybeLeftmostFrame);
  10380        }
  10381        return maybeLeftmostFrame == this;
  10382      }
  10383      maybeLeftmostFrame = nsBidiPresUtils::GetFrameToRightOf(
  10384          maybeLeftmostFrame, line.mFirstFrameOnLine, line.mNumFramesOnLine);
  10385      if (!maybeLeftmostFrame) {
  10386        return false;
  10387      }
  10388    }
  10389    return false;
  10390  }
  10391 
  10392  nsIFrame* maybeRightmostFrame = rightmostFrame;
  10393  for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
  10394    if (maybeRightmostFrame == this) {
  10395      return true;
  10396    }
  10397    // If the line ends with placehlder frames, we can ignore them and should
  10398    // keep checking the preceding frames.
  10399    if (!maybeRightmostFrame->IsPlaceholderFrame()) {
  10400      if ((FrameIsRTL(maybeRightmostFrame) == lineIsRTL) ==
  10401          (aDirection == eDirPrevious)) {
  10402        nsIFrame::GetFirstLeaf(&maybeRightmostFrame);
  10403      } else {
  10404        nsIFrame::GetLastLeaf(&maybeRightmostFrame);
  10405      }
  10406      return maybeRightmostFrame == this;
  10407    }
  10408    maybeRightmostFrame = nsBidiPresUtils::GetFrameToLeftOf(
  10409        maybeRightmostFrame, line.mFirstFrameOnLine, line.mNumFramesOnLine);
  10410    if (!maybeRightmostFrame) {
  10411      return false;
  10412    }
  10413  }
  10414  return false;
  10415 }
  10416 
  10417 Result<bool, nsresult> nsIFrame::IsLogicallyAtLineEdge(
  10418    nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
  10419  auto line = aLineIterator->GetLine(aLine).unwrap();
  10420  if (!line.mNumFramesOnLine) {
  10421    return false;
  10422  }
  10423  MOZ_ASSERT(line.mFirstFrameOnLine);
  10424 
  10425  if (aDirection == eDirPrevious) {
  10426    nsIFrame* maybeFirstFrame = line.mFirstFrameOnLine;
  10427    for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
  10428      if (maybeFirstFrame == this) {
  10429        return true;
  10430      }
  10431      // If the line starts with placeholder frames, we can ignore them and
  10432      // should keep checking the following frames.
  10433      if (!maybeFirstFrame->IsPlaceholderFrame()) {
  10434        nsIFrame::GetFirstLeaf(&maybeFirstFrame);
  10435        return maybeFirstFrame == this;
  10436      }
  10437      maybeFirstFrame = maybeFirstFrame->GetNextSibling();
  10438      if (!maybeFirstFrame) {
  10439        return false;
  10440      }
  10441    }
  10442    return false;
  10443  }
  10444 
  10445  // eDirNext
  10446  nsIFrame* maybeLastFrame = line.GetLastFrameOnLine();
  10447  for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
  10448    if (maybeLastFrame == this) {
  10449      return true;
  10450    }
  10451    // If the line ends with placehlder frames, we can ignore them and should
  10452    // keep checking the preceding frames.
  10453    if (!maybeLastFrame->IsPlaceholderFrame()) {
  10454      nsIFrame::GetLastLeaf(&maybeLastFrame);
  10455      return maybeLastFrame == this;
  10456    }
  10457    maybeLastFrame = maybeLastFrame->GetPrevSibling();
  10458  }
  10459  return false;
  10460 }
  10461 
  10462 nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
  10463    nsDirection aDirection, const PeekOffsetOptions& aOptions,
  10464    const Element* aAncestorLimiter) {
  10465  SelectablePeekReport result;
  10466 
  10467  nsPresContext* presContext = PresContext();
  10468  const bool needsVisualTraversal =
  10469      aOptions.contains(PeekOffsetOption::Visual) && presContext->BidiEnabled();
  10470  const bool followOofs =
  10471      !aOptions.contains(PeekOffsetOption::StopAtPlaceholder);
  10472  nsFrameIterator frameIterator(
  10473      presContext, this, nsFrameIterator::Type::Leaf, needsVisualTraversal,
  10474      aOptions.contains(PeekOffsetOption::StopAtScroller), followOofs,
  10475      false,  // aSkipPopupChecks
  10476      aAncestorLimiter);
  10477 
  10478  // Find the prev/next selectable frame
  10479  bool selectable = false;
  10480  nsIFrame* traversedFrame = this;
  10481  AutoAssertNoDomMutations guard;
  10482  const nsFrameSelection* frameSelection =
  10483      GetContent() ? GetContent()->GetFrameSelection() : nullptr;
  10484  while (!selectable) {
  10485    auto [blockFrame, lineFrame] = traversedFrame->GetContainingBlockForLine(
  10486        aOptions.contains(PeekOffsetOption::StopAtScroller));
  10487    if (!blockFrame) {
  10488      return result;
  10489    }
  10490 
  10491    nsILineIterator* it = blockFrame->GetLineIterator();
  10492    int32_t thisLine = it->FindLineContaining(lineFrame);
  10493    if (thisLine < 0) {
  10494      return result;
  10495    }
  10496 
  10497    bool atLineEdge = MOZ_TRY(
  10498        needsVisualTraversal
  10499            ? traversedFrame->IsVisuallyAtLineEdge(it, thisLine, aDirection)
  10500            : traversedFrame->IsLogicallyAtLineEdge(it, thisLine, aDirection));
  10501    if (atLineEdge) {
  10502      result.mJumpedLine = true;
  10503      if (!aOptions.contains(PeekOffsetOption::JumpLines)) {
  10504        return result;  // we are done. cannot jump lines
  10505      }
  10506      int32_t lineToCheckWrap =
  10507          aDirection == eDirPrevious ? thisLine - 1 : thisLine;
  10508      if (lineToCheckWrap < 0 ||
  10509          !it->GetLine(lineToCheckWrap).unwrap().mIsWrapped) {
  10510        result.mJumpedHardBreak = true;
  10511      }
  10512    }
  10513 
  10514    traversedFrame = frameIterator.Traverse(aDirection == eDirNext);
  10515    if (!traversedFrame) {
  10516      return result;
  10517    }
  10518 
  10519    if (aOptions.contains(PeekOffsetOption::StopAtPlaceholder) &&
  10520        traversedFrame->IsPlaceholderFrame()) {
  10521      // XXX If the placeholder frame does not have meaningful content, the user
  10522      // may want to select as a word around the out-of-flow cotent.  However,
  10523      // non-text frame resets context in nsIFrame::PeekOffsetWord(). Therefore,
  10524      // next text frame considers the new word starts from its edge. So, it's
  10525      // not enough to implement such behavior with adding a check here whether
  10526      // the real frame may change the word with its contents if it were not
  10527      // out-of-flow.
  10528      result.mFoundPlaceholder = true;
  10529      return result;
  10530    }
  10531 
  10532    auto IsSelectableFrame = [aAncestorLimiter, aOptions,
  10533                              frameSelection](const nsIFrame* aFrame) {
  10534      if (!aFrame->IsSelectable() || MOZ_UNLIKELY(!aFrame->GetContent())) {
  10535        return false;
  10536      }
  10537      // If the found frame content is managed by different nsFrameSelection, we
  10538      // cannot select the frame content with current selection.
  10539      if (frameSelection != aFrame->GetContent()->GetFrameSelection()) {
  10540        return false;
  10541      }
  10542      if (MOZ_UNLIKELY(aAncestorLimiter &&
  10543                       !aAncestorLimiter->GetPrimaryFrame()) &&
  10544          !aFrame->GetContent()->IsInclusiveFlatTreeDescendantOf(
  10545              aAncestorLimiter)) {
  10546        return false;
  10547      }
  10548      return !aOptions.contains(PeekOffsetOption::ForceEditableRegion) ||
  10549             aFrame->GetContent()->IsEditable();
  10550    };
  10551 
  10552    // Skip br frames, but only if we can select something before hitting the
  10553    // end of the line or a non-selectable region.
  10554    if (atLineEdge && aDirection == eDirPrevious &&
  10555        traversedFrame->IsBrFrame()) {
  10556      for (nsIFrame* current = traversedFrame->GetPrevSibling(); current;
  10557           current = current->GetPrevSibling()) {
  10558        if (!current->IsBlockOutside() && IsSelectableFrame(current)) {
  10559          if (!current->IsBrFrame()) {
  10560            result.mIgnoredBrFrame = true;
  10561          }
  10562          break;
  10563        }
  10564      }
  10565      if (result.mIgnoredBrFrame) {
  10566        continue;
  10567      }
  10568    }
  10569 
  10570    selectable = IsSelectableFrame(traversedFrame);
  10571    if (MOZ_UNLIKELY(!frameSelection) && selectable &&
  10572        MOZ_LIKELY(traversedFrame->GetContent())) {
  10573      frameSelection = traversedFrame->GetContent()->GetFrameSelection();
  10574    }
  10575    if (!selectable) {
  10576      if (traversedFrame->IsSelectable()) {
  10577        result.mHasSelectableFrame = true;
  10578      }
  10579      result.mMovedOverNonSelectableText = true;
  10580    }
  10581  }  // while (!selectable)
  10582 
  10583  result.mOffset = (aDirection == eDirNext) ? 0 : -1;
  10584 
  10585  if (aOptions.contains(PeekOffsetOption::Visual) &&
  10586      nsBidiPresUtils::IsReversedDirectionFrame(traversedFrame)) {
  10587    // The new frame is reverse-direction, go to the other end
  10588    result.mOffset = -1 - result.mOffset;
  10589  }
  10590  result.mFrame = traversedFrame;
  10591  return result;
  10592 }
  10593 
  10594 nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
  10595    const PeekOffsetStruct& aPos) {
  10596  return GetFrameFromDirection(aPos.mDirection, aPos.mOptions,
  10597                               aPos.mAncestorLimiter);
  10598 }
  10599 
  10600 /* virtual */
  10601 void nsIFrame::ChildIsDirty(nsIFrame* aChild) {
  10602  MOZ_ASSERT_UNREACHABLE(
  10603      "should never be called on a frame that doesn't "
  10604      "inherit from nsContainerFrame");
  10605 }
  10606 
  10607 #ifdef ACCESSIBILITY
  10608 a11y::AccType nsIFrame::AccessibleType() {
  10609  if (IsTableCaption() && !GetRect().IsEmpty()) {
  10610    return a11y::eHTMLCaptionType;
  10611  }
  10612  return a11y::eNoType;
  10613 }
  10614 #endif
  10615 
  10616 bool nsIFrame::ClearOverflowRects() {
  10617  if (mOverflow.mType == OverflowStorageType::None) {
  10618    return false;
  10619  }
  10620  if (mOverflow.mType == OverflowStorageType::Large) {
  10621    RemoveProperty(OverflowAreasProperty());
  10622  }
  10623  mOverflow.mType = OverflowStorageType::None;
  10624  return true;
  10625 }
  10626 
  10627 bool nsIFrame::SetOverflowAreas(const OverflowAreas& aOverflowAreas) {
  10628  if (mOverflow.mType == OverflowStorageType::Large) {
  10629    OverflowAreas* overflow = GetOverflowAreasProperty();
  10630    bool changed = *overflow != aOverflowAreas;
  10631    *overflow = aOverflowAreas;
  10632 
  10633    // Don't bother with converting to the deltas form if we already
  10634    // have a property.
  10635    return changed;
  10636  }
  10637 
  10638  const nsRect& vis = aOverflowAreas.InkOverflow();
  10639  uint32_t l = -vis.x,                 // left edge: positive delta is leftwards
  10640      t = -vis.y,                      // top: positive is upwards
  10641      r = vis.XMost() - mRect.width,   // right: positive is rightwards
  10642      b = vis.YMost() - mRect.height;  // bottom: positive is downwards
  10643  if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(
  10644          nsRect(nsPoint(0, 0), GetSize())) &&
  10645      l <= InkOverflowDeltas::kMax && t <= InkOverflowDeltas::kMax &&
  10646      r <= InkOverflowDeltas::kMax && b <= InkOverflowDeltas::kMax &&
  10647      // we have to check these against zero because we *never* want to
  10648      // set a frame as having no overflow in this function.  This is
  10649      // because FinishAndStoreOverflow calls this function prior to
  10650      // SetRect based on whether the overflow areas match aNewSize.
  10651      // In the case where the overflow areas exactly match mRect but
  10652      // do not match aNewSize, we need to store overflow in a property
  10653      // so that our eventual SetRect/SetSize will know that it has to
  10654      // reset our overflow areas.
  10655      (l | t | r | b) != 0) {
  10656    InkOverflowDeltas oldDeltas = mOverflow.mInkOverflowDeltas;
  10657    // It's a "small" overflow area so we store the deltas for each edge
  10658    // directly in the frame, rather than allocating a separate rect.
  10659    // If they're all zero, that's fine; we're setting things to
  10660    // no-overflow.
  10661    mOverflow.mInkOverflowDeltas.mLeft = l;
  10662    mOverflow.mInkOverflowDeltas.mTop = t;
  10663    mOverflow.mInkOverflowDeltas.mRight = r;
  10664    mOverflow.mInkOverflowDeltas.mBottom = b;
  10665    // There was no scrollable overflow before, and there isn't now.
  10666    return oldDeltas != mOverflow.mInkOverflowDeltas;
  10667  } else {
  10668    bool changed =
  10669        !aOverflowAreas.ScrollableOverflow().IsEqualEdges(
  10670            nsRect(nsPoint(0, 0), GetSize())) ||
  10671        !aOverflowAreas.InkOverflow().IsEqualEdges(InkOverflowFromDeltas());
  10672 
  10673    // it's a large overflow area that we need to store as a property
  10674    mOverflow.mType = OverflowStorageType::Large;
  10675    AddProperty(OverflowAreasProperty(), new OverflowAreas(aOverflowAreas));
  10676    return changed;
  10677  }
  10678 }
  10679 
  10680 enum class ApplyTransform : bool { No, Yes };
  10681 
  10682 /**
  10683 * Compute the outline inner rect (so without outline-width and outline-offset)
  10684 * of aFrame, maybe iterating over its descendants, in aFrame's coordinate space
  10685 * or its post-transform coordinate space (depending on aApplyTransform).
  10686 */
  10687 static nsRect ComputeOutlineInnerRect(
  10688    nsIFrame* aFrame, ApplyTransform aApplyTransform, bool& aOutValid,
  10689    const nsSize* aSizeOverride = nullptr,
  10690    const OverflowAreas* aOverflowOverride = nullptr) {
  10691  const nsRect bounds(nsPoint(0, 0),
  10692                      aSizeOverride ? *aSizeOverride : aFrame->GetSize());
  10693 
  10694  // The SVG container frames besides SVGTextFrame do not maintain
  10695  // an accurate mRect. It will make the outline be larger than
  10696  // we expect, we need to make them narrow to their children's outline.
  10697  // aOutValid is set to false if the returned nsRect is not valid
  10698  // and should not be included in the outline rectangle.
  10699  aOutValid = !aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||
  10700              !aFrame->IsSVGContainerFrame() || aFrame->IsSVGTextFrame();
  10701 
  10702  nsRect u;
  10703 
  10704  if (!aFrame->FrameMaintainsOverflow()) {
  10705    return u;
  10706  }
  10707 
  10708  // Start from our border-box, transformed.  See comment below about
  10709  // transform of children.
  10710  bool doTransform =
  10711      aApplyTransform == ApplyTransform::Yes && aFrame->IsTransformed();
  10712  TransformReferenceBox boundsRefBox(nullptr, bounds);
  10713  if (doTransform) {
  10714    u = nsDisplayTransform::TransformRect(bounds, aFrame, boundsRefBox);
  10715  } else {
  10716    u = bounds;
  10717  }
  10718 
  10719  if (aOutValid && !StaticPrefs::layout_outline_include_overflow()) {
  10720    return u;
  10721  }
  10722 
  10723  // Only iterate through the children if the overflow areas suggest
  10724  // that we might need to, and if the frame doesn't clip its overflow
  10725  // anyway.
  10726  if (aOverflowOverride) {
  10727    if (!doTransform && bounds.IsEqualEdges(aOverflowOverride->InkOverflow()) &&
  10728        bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
  10729      return u;
  10730    }
  10731  } else {
  10732    if (!doTransform && bounds.IsEqualEdges(aFrame->InkOverflowRect()) &&
  10733        bounds.IsEqualEdges(aFrame->ScrollableOverflowRect())) {
  10734      return u;
  10735    }
  10736  }
  10737  const nsStyleDisplay* disp = aFrame->StyleDisplay();
  10738  LayoutFrameType fType = aFrame->Type();
  10739  if (fType == LayoutFrameType::ScrollContainer ||
  10740      fType == LayoutFrameType::ListControl ||
  10741      fType == LayoutFrameType::SVGOuterSVG) {
  10742    return u;
  10743  }
  10744 
  10745  auto overflowClipAxes = aFrame->ShouldApplyOverflowClipping(disp);
  10746  auto overflowClipMargin = aFrame->OverflowClipMargin(
  10747      overflowClipAxes, /* aAllowNegative = */ false);
  10748  if (overflowClipAxes == kPhysicalAxesBoth && overflowClipMargin.IsAllZero()) {
  10749    return u;
  10750  }
  10751 
  10752  const nsStyleEffects* effects = aFrame->StyleEffects();
  10753  Maybe<nsRect> clipPropClipRect =
  10754      aFrame->GetClipPropClipRect(disp, effects, bounds.Size());
  10755 
  10756  // Iterate over all children except pop-up, absolutely-positioned,
  10757  // float, and overflow ones.
  10758  const FrameChildListIDs skip = {
  10759      FrameChildListID::Absolute, FrameChildListID::Fixed,
  10760      FrameChildListID::Float, FrameChildListID::Overflow};
  10761  for (const auto& [list, listID] : aFrame->ChildLists()) {
  10762    if (skip.contains(listID)) {
  10763      continue;
  10764    }
  10765 
  10766    for (nsIFrame* child : list) {
  10767      if (child->IsPlaceholderFrame()) {
  10768        continue;
  10769      }
  10770 
  10771      // Note that passing ApplyTransform::Yes when
  10772      // child->Combines3DTransformWithAncestors() returns true is incorrect if
  10773      // our aApplyTransform is No... but the opposite would be as well.
  10774      // This is because elements within a preserve-3d scene are always
  10775      // transformed up to the top of the scene.  This means we don't have a
  10776      // mechanism for getting a transform up to an intermediate point within
  10777      // the scene.  We choose to over-transform rather than under-transform
  10778      // because this is consistent with other overflow areas.
  10779      bool validRect = true;
  10780      nsRect childRect =
  10781          ComputeOutlineInnerRect(child, ApplyTransform::Yes, validRect) +
  10782          child->GetPosition();
  10783 
  10784      if (!validRect) {
  10785        continue;
  10786      }
  10787 
  10788      if (clipPropClipRect) {
  10789        // Intersect with the clip before transforming.
  10790        childRect.IntersectRect(childRect, *clipPropClipRect);
  10791      }
  10792 
  10793      // Note that we transform each child separately according to
  10794      // aFrame's transform, and then union, which gives a different
  10795      // (smaller) result from unioning and then transforming the
  10796      // union.  This doesn't match the way we handle overflow areas
  10797      // with 2-D transforms, though it does match the way we handle
  10798      // overflow areas in preserve-3d 3-D scenes.
  10799      if (doTransform && !child->Combines3DTransformWithAncestors()) {
  10800        childRect =
  10801            nsDisplayTransform::TransformRect(childRect, aFrame, boundsRefBox);
  10802      }
  10803 
  10804      // If a SVGContainer has a non-SVGContainer child, we assign
  10805      // its child's outline to this SVGContainer directly.
  10806      if (!aOutValid && validRect) {
  10807        u = childRect;
  10808        aOutValid = true;
  10809      } else {
  10810        u = u.UnionEdges(childRect);
  10811      }
  10812    }
  10813  }
  10814 
  10815  if (!overflowClipAxes.isEmpty()) {
  10816    OverflowAreas::ApplyOverflowClippingOnRect(u, bounds, overflowClipAxes,
  10817                                               overflowClipMargin);
  10818  }
  10819  return u;
  10820 }
  10821 
  10822 static void ComputeAndIncludeOutlineArea(nsIFrame* aFrame,
  10823                                         OverflowAreas& aOverflowAreas,
  10824                                         const nsSize& aNewSize) {
  10825  const nsStyleOutline* outline = aFrame->StyleOutline();
  10826  if (!outline->ShouldPaintOutline()) {
  10827    return;
  10828  }
  10829 
  10830  // When the outline property is set on a :-moz-block-inside-inline-wrapper
  10831  // pseudo-element, it inherited that outline from the inline that was broken
  10832  // because it contained a block.  In that case, we don't want a really wide
  10833  // outline if the block inside the inline is narrow, so union the actual
  10834  // contents of the anonymous blocks.
  10835  nsIFrame* frameForArea = aFrame;
  10836  do {
  10837    PseudoStyleType pseudoType = frameForArea->Style()->GetPseudoType();
  10838    if (pseudoType != PseudoStyleType::mozBlockInsideInlineWrapper) {
  10839      break;
  10840    }
  10841    // If we're done, we really want it and all its later siblings.
  10842    frameForArea = frameForArea->PrincipalChildList().FirstChild();
  10843    NS_ASSERTION(frameForArea, "anonymous block with no children?");
  10844  } while (frameForArea);
  10845 
  10846  // Find the union of the border boxes of all descendants, or in
  10847  // the block-in-inline case, all descendants we care about.
  10848  //
  10849  // Note that the interesting perspective-related cases are taken
  10850  // care of by the code that handles those issues for overflow
  10851  // calling FinishAndStoreOverflow again, which in turn calls this
  10852  // function again.  We still need to deal with preserve-3d a bit.
  10853  nsRect innerRect;
  10854  bool validRect = false;
  10855  if (frameForArea == aFrame) {
  10856    innerRect = ComputeOutlineInnerRect(aFrame, ApplyTransform::No, validRect,
  10857                                        &aNewSize, &aOverflowAreas);
  10858  } else {
  10859    for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
  10860      nsRect r =
  10861          ComputeOutlineInnerRect(frameForArea, ApplyTransform::Yes, validRect);
  10862 
  10863      // Adjust for offsets transforms up to aFrame's pre-transform
  10864      // (i.e., normal) coordinate space; see comments in
  10865      // UnionBorderBoxes for some of the subtlety here.
  10866      for (nsIFrame *f = frameForArea, *parent = f->GetParent();
  10867           /* see middle of loop */; f = parent, parent = f->GetParent()) {
  10868        r += f->GetPosition();
  10869        if (parent == aFrame) {
  10870          break;
  10871        }
  10872        if (parent->IsTransformed() && !f->Combines3DTransformWithAncestors()) {
  10873          TransformReferenceBox refBox(parent);
  10874          r = nsDisplayTransform::TransformRect(r, parent, refBox);
  10875        }
  10876      }
  10877 
  10878      innerRect.UnionRect(innerRect, r);
  10879    }
  10880  }
  10881 
  10882  // Keep this code in sync with nsDisplayOutline::GetInnerRect.
  10883  if (innerRect == aFrame->GetRectRelativeToSelf()) {
  10884    aFrame->RemoveProperty(nsIFrame::OutlineInnerRectProperty());
  10885  } else {
  10886    aFrame->SetOrUpdateDeletableProperty(nsIFrame::OutlineInnerRectProperty(),
  10887                                         innerRect);
  10888  }
  10889 
  10890  nsRect outerRect(innerRect);
  10891  outerRect.Inflate(outline->EffectiveOffsetFor(outerRect));
  10892 
  10893  if (outline->mOutlineStyle.IsAuto()) {
  10894    nsPresContext* pc = aFrame->PresContext();
  10895 
  10896    pc->Theme()->GetWidgetOverflow(pc->DeviceContext(), aFrame,
  10897                                   StyleAppearance::FocusOutline, &outerRect);
  10898  } else {
  10899    outerRect.Inflate(outline->mOutlineWidth);
  10900  }
  10901 
  10902  nsRect& vo = aOverflowAreas.InkOverflow();
  10903  vo = vo.UnionEdges(innerRect.Union(outerRect));
  10904 }
  10905 
  10906 bool nsIFrame::FinishAndStoreOverflow(OverflowAreas& aOverflowAreas,
  10907                                      nsSize aNewSize, nsSize* aOldSize,
  10908                                      const nsStyleDisplay* aStyleDisplay) {
  10909  MOZ_ASSERT(FrameMaintainsOverflow(),
  10910             "Don't call - overflow rects not maintained on these SVG frames");
  10911 
  10912  const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
  10913  bool hasTransform = IsTransformed();
  10914 
  10915  nsRect bounds(nsPoint(0, 0), aNewSize);
  10916  // Store the passed in overflow area if we are a preserve-3d frame or we have
  10917  // a transform, and it's not just the frame bounds.
  10918  if (hasTransform || Combines3DTransformWithAncestors()) {
  10919    if (!aOverflowAreas.InkOverflow().IsEqualEdges(bounds) ||
  10920        !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
  10921      OverflowAreas* initial = GetProperty(nsIFrame::InitialOverflowProperty());
  10922      if (!initial) {
  10923        AddProperty(nsIFrame::InitialOverflowProperty(),
  10924                    new OverflowAreas(aOverflowAreas));
  10925      } else if (initial != &aOverflowAreas) {
  10926        *initial = aOverflowAreas;
  10927      }
  10928    } else {
  10929      RemoveProperty(nsIFrame::InitialOverflowProperty());
  10930    }
  10931 #ifdef DEBUG
  10932    SetProperty(nsIFrame::DebugInitialOverflowPropertyApplied(), true);
  10933 #endif
  10934  } else {
  10935 #ifdef DEBUG
  10936    RemoveProperty(nsIFrame::DebugInitialOverflowPropertyApplied());
  10937 #endif
  10938  }
  10939 
  10940  nsSize oldSize = mRect.Size();
  10941  bool sizeChanged = ((aOldSize ? *aOldSize : oldSize) != aNewSize);
  10942 
  10943  // Our frame size may not have been computed and set yet, but code under
  10944  // functions such as ComputeEffectsRect (which we're about to call) use the
  10945  // values that are stored in our frame rect to compute their results.  We
  10946  // need the results from those functions to be based on the frame size that
  10947  // we *will* have, so we temporarily set our frame size here before calling
  10948  // those functions.
  10949  //
  10950  // XXX Someone should document here why we revert the frame size before we
  10951  // return rather than just leaving it set.
  10952  //
  10953  // We pass false here to avoid invalidating display items for this temporary
  10954  // change. We sometimes reflow frames multiple times, with the final size
  10955  // being the same as the initial. The single call to SetSize after reflow is
  10956  // done will take care of invalidating display items if the size has actually
  10957  // changed.
  10958  SetSize(aNewSize, false);
  10959 
  10960  const auto overflowClipAxes = ShouldApplyOverflowClipping(disp);
  10961 
  10962  if (ChildrenHavePerspective(disp) && sizeChanged) {
  10963    RecomputePerspectiveChildrenOverflow(this);
  10964 
  10965    if (overflowClipAxes != kPhysicalAxesBoth) {
  10966      aOverflowAreas.SetAllTo(bounds);
  10967      DebugOnly<bool> ok = ComputeCustomOverflow(aOverflowAreas);
  10968 
  10969      // ComputeCustomOverflow() should not return false, when
  10970      // FrameMaintainsOverflow() returns true.
  10971      MOZ_ASSERT(ok, "FrameMaintainsOverflow() != ComputeCustomOverflow()");
  10972 
  10973      UnionChildOverflow(aOverflowAreas);
  10974    }
  10975  }
  10976 
  10977  // This is now called FinishAndStoreOverflow() instead of
  10978  // StoreOverflow() because frame-generic ways of adding overflow
  10979  // can happen here, e.g. CSS2 outline and native theme.
  10980  // If the overflow area width or height is nscoord_MAX, then a saturating
  10981  // union may have encountered an overflow, so the overflow may not contain the
  10982  // frame border-box. Don't warn in that case.
  10983  // Don't warn for SVG either, since SVG doesn't need the overflow area
  10984  // to contain the frame bounds.
  10985 #ifdef DEBUG
  10986  for (const auto otype : AllOverflowTypes()) {
  10987    const nsRect& r = aOverflowAreas.Overflow(otype);
  10988    NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 ||
  10989                     r.width == nscoord_MAX || r.height == nscoord_MAX ||
  10990                     HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||
  10991                     r.Contains(nsRect(nsPoint(), aNewSize)),
  10992                 "Computed overflow area must contain frame bounds");
  10993  }
  10994 #endif
  10995 
  10996  // Overflow area must always include the frame's top-left and bottom-right,
  10997  // even if the frame rect is empty (so we can scroll to those positions).
  10998  const bool shouldIncludeBounds = [&] {
  10999    if (aNewSize.width == 0 && IsInlineFrame()) {
  11000      // Pending a real fix for bug 426879, don't do this for inline frames with
  11001      // zero width.
  11002      return false;
  11003    }
  11004    if (HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
  11005      // Do not do this for SVG either, since it will usually massively increase
  11006      // the area unnecessarily (except for SVG that applies clipping, since
  11007      // that's the pre-existing behavior, and breaks pre-rendering otherwise).
  11008      // FIXME(bug 1770704): This check most likely wants to be removed or check
  11009      // for specific frame types at least.
  11010      return !overflowClipAxes.isEmpty();
  11011    }
  11012    return true;
  11013  }();
  11014 
  11015  if (shouldIncludeBounds) {
  11016    for (const auto otype : AllOverflowTypes()) {
  11017      nsRect& o = aOverflowAreas.Overflow(otype);
  11018      o = o.UnionEdges(bounds);
  11019    }
  11020  }
  11021 
  11022  // If we clip our children, clear accumulated overflow area in the affected
  11023  // dimension(s). The children are actually clipped to the padding-box, but
  11024  // since the overflow area should include the entire border-box, just set it
  11025  // to the border-box size here.
  11026  if (!overflowClipAxes.isEmpty()) {
  11027    aOverflowAreas.ApplyClipping(
  11028        bounds, overflowClipAxes,
  11029        OverflowClipMargin(overflowClipAxes, /* aAllowNegative = */ false));
  11030  }
  11031 
  11032  ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
  11033 
  11034  // Nothing in here should affect scrollable overflow.
  11035  aOverflowAreas.InkOverflow() =
  11036      ComputeEffectsRect(this, aOverflowAreas.InkOverflow(), aNewSize);
  11037 
  11038  // Absolute position clipping
  11039  const nsStyleEffects* effects = StyleEffects();
  11040  Maybe<nsRect> clipPropClipRect = GetClipPropClipRect(disp, effects, aNewSize);
  11041  if (clipPropClipRect) {
  11042    for (const auto otype : AllOverflowTypes()) {
  11043      nsRect& o = aOverflowAreas.Overflow(otype);
  11044      o.IntersectRect(o, *clipPropClipRect);
  11045    }
  11046  }
  11047 
  11048  /* If we're transformed, transform the overflow rect by the current
  11049   * transformation. */
  11050  if (hasTransform) {
  11051    SetProperty(nsIFrame::PreTransformOverflowAreasProperty(),
  11052                new OverflowAreas(aOverflowAreas));
  11053 
  11054    if (Combines3DTransformWithAncestors()) {
  11055      /* If we're a preserve-3d leaf frame, then our pre-transform overflow
  11056       * should be correct. Our post-transform overflow is empty though, because
  11057       * we only contribute to the overflow area of the preserve-3d root frame.
  11058       * If we're an intermediate frame then the pre-transform overflow should
  11059       * contain all our non-preserve-3d children, which is what we want. Again
  11060       * we have no post-transform overflow.
  11061       */
  11062      aOverflowAreas.SetAllTo(nsRect());
  11063    } else {
  11064      TransformReferenceBox refBox(this);
  11065      for (const auto otype : AllOverflowTypes()) {
  11066        nsRect& o = aOverflowAreas.Overflow(otype);
  11067        // If the overflow is empty, it can still have a non-zero length in one
  11068        // axis. Transforming such axis-bound rect can cause the resulting rect
  11069        // to be non-empty, e.g. by rotating the rect.
  11070        if (!o.IsEmpty()) {
  11071          o = nsDisplayTransform::TransformRect(o, this, refBox);
  11072        }
  11073      }
  11074 
  11075      /* If we're the root of the 3d context, then we want to include the
  11076       * overflow areas of all the participants. This won't have happened yet as
  11077       * the code above set their overflow area to empty. Manually collect these
  11078       * overflow areas now.
  11079       */
  11080      if (Extend3DContext(disp, effects)) {
  11081        ComputePreserve3DChildrenOverflow(aOverflowAreas);
  11082      }
  11083    }
  11084  } else {
  11085    RemoveProperty(nsIFrame::PreTransformOverflowAreasProperty());
  11086  }
  11087 
  11088  /* Revert the size change in case some caller is depending on this. */
  11089  SetSize(oldSize, false);
  11090 
  11091  bool anyOverflowChanged;
  11092  if (aOverflowAreas != OverflowAreas(bounds, bounds)) {
  11093    anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
  11094  } else {
  11095    anyOverflowChanged = ClearOverflowRects();
  11096  }
  11097 
  11098  if (anyOverflowChanged) {
  11099    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
  11100    if (nsBlockFrame* block = do_QueryFrame(this)) {
  11101      // NOTE(emilio): we need to use BeforeReflow::Yes, because we want to
  11102      // invalidate in cases where we _used_ to have an overflow marker and no
  11103      // longer do.
  11104      if (TextOverflow::CanHaveOverflowMarkers(
  11105              block, TextOverflow::BeforeReflow::Yes)) {
  11106        DiscardDisplayItems(this, [](nsDisplayItem* aItem) {
  11107          return aItem->GetType() == DisplayItemType::TYPE_TEXT_OVERFLOW;
  11108        });
  11109        SchedulePaint(PAINT_DEFAULT);
  11110      }
  11111    }
  11112  }
  11113  return anyOverflowChanged;
  11114 }
  11115 
  11116 void nsIFrame::RecomputePerspectiveChildrenOverflow(
  11117    const nsIFrame* aStartFrame) {
  11118  for (const auto& childList : ChildLists()) {
  11119    for (nsIFrame* child : childList.mList) {
  11120      if (!child->FrameMaintainsOverflow()) {
  11121        continue;  // frame does not maintain overflow rects
  11122      }
  11123      if (child->HasPerspective()) {
  11124        OverflowAreas* overflow =
  11125            child->GetProperty(nsIFrame::InitialOverflowProperty());
  11126        nsRect bounds(nsPoint(0, 0), child->GetSize());
  11127        if (overflow) {
  11128          OverflowAreas overflowCopy = *overflow;
  11129          child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
  11130        } else {
  11131          OverflowAreas boundsOverflow;
  11132          boundsOverflow.SetAllTo(bounds);
  11133          child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
  11134        }
  11135      } else if (child->GetContent() == aStartFrame->GetContent() ||
  11136                 child->GetClosestFlattenedTreeAncestorPrimaryFrame() ==
  11137                     aStartFrame) {
  11138        // If a frame is using perspective, then the size used to compute
  11139        // perspective-origin is the size of the frame belonging to its parent
  11140        // style. We must find any descendant frames using our size
  11141        // (by recursing into frames that have the same containing block)
  11142        // to update their overflow rects too.
  11143        child->RecomputePerspectiveChildrenOverflow(aStartFrame);
  11144      }
  11145    }
  11146  }
  11147 }
  11148 
  11149 void nsIFrame::ComputePreserve3DChildrenOverflow(
  11150    OverflowAreas& aOverflowAreas) {
  11151  // Find all descendants that participate in the 3d context, and include their
  11152  // overflow. These descendants have an empty overflow, so won't have been
  11153  // included in the normal overflow calculation. Any children that don't
  11154  // participate have normal overflow, so will have been included already.
  11155 
  11156  nsRect childVisual;
  11157  nsRect childScrollable;
  11158  for (const auto& childList : ChildLists()) {
  11159    for (nsIFrame* child : childList.mList) {
  11160      // If this child participates in the 3d context, then take the
  11161      // pre-transform region (which contains all descendants that aren't
  11162      // participating in the 3d context) and transform it into the 3d context
  11163      // root coordinate space.
  11164      if (child->Combines3DTransformWithAncestors()) {
  11165        OverflowAreas childOverflow = child->GetOverflowAreasRelativeToSelf();
  11166        TransformReferenceBox refBox(child);
  11167        for (const auto otype : AllOverflowTypes()) {
  11168          nsRect& o = childOverflow.Overflow(otype);
  11169          o = nsDisplayTransform::TransformRect(o, child, refBox);
  11170        }
  11171 
  11172        aOverflowAreas.UnionWith(childOverflow);
  11173 
  11174        // If this child also extends the 3d context, then recurse into it
  11175        // looking for more participants.
  11176        if (child->Extend3DContext()) {
  11177          child->ComputePreserve3DChildrenOverflow(aOverflowAreas);
  11178        }
  11179      }
  11180    }
  11181  }
  11182 }
  11183 
  11184 bool nsIFrame::ZIndexApplies() const {
  11185  return StyleDisplay()->IsPositionedStyle() || IsFlexOrGridItem() ||
  11186         IsMenuPopupFrame();
  11187 }
  11188 
  11189 Maybe<int32_t> nsIFrame::ZIndex() const {
  11190  if (!ZIndexApplies()) {
  11191    return Nothing();
  11192  }
  11193  const auto& zIndex = StylePosition()->mZIndex;
  11194  if (zIndex.IsAuto()) {
  11195    return Nothing();
  11196  }
  11197  return Some(zIndex.AsInteger());
  11198 }
  11199 
  11200 bool nsIFrame::IsScrollAnchor(ScrollAnchorContainer** aOutContainer) {
  11201  if (!mInScrollAnchorChain) {
  11202    return false;
  11203  }
  11204 
  11205  nsIFrame* f = this;
  11206 
  11207  // FIXME(emilio, bug 1629280): We should find a non-null anchor if we have the
  11208  // flag set, but bug 1629280 makes it so that we cannot really assert it /
  11209  // make this just a `while (true)`, and uncomment the below assertion.
  11210  while (auto* container = ScrollAnchorContainer::FindFor(f)) {
  11211    // MOZ_ASSERT(f->IsInScrollAnchorChain());
  11212    if (nsIFrame* anchor = container->AnchorNode()) {
  11213      if (anchor != this) {
  11214        return false;
  11215      }
  11216      if (aOutContainer) {
  11217        *aOutContainer = container;
  11218      }
  11219      return true;
  11220    }
  11221 
  11222    f = container->Frame();
  11223  }
  11224 
  11225  return false;
  11226 }
  11227 
  11228 bool nsIFrame::IsInScrollAnchorChain() const { return mInScrollAnchorChain; }
  11229 
  11230 void nsIFrame::SetInScrollAnchorChain(bool aInChain) {
  11231  mInScrollAnchorChain = aInChain;
  11232 }
  11233 
  11234 uint32_t nsIFrame::GetDepthInFrameTree() const {
  11235  uint32_t result = 0;
  11236  for (nsContainerFrame* ancestor = GetParent(); ancestor;
  11237       ancestor = ancestor->GetParent()) {
  11238    result++;
  11239  }
  11240  return result;
  11241 }
  11242 
  11243 /**
  11244 * This function takes a frame that is part of a block-in-inline split,
  11245 * and _if_ that frame is an anonymous block created by an ib split it
  11246 * returns the block's preceding inline.  This is needed because the
  11247 * split inline's style is the parent of the anonymous block's style.
  11248 *
  11249 * If aFrame is not an anonymous block, null is returned.
  11250 */
  11251 static nsIFrame* GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) {
  11252  MOZ_ASSERT(aFrame, "Must have a non-null frame!");
  11253  NS_ASSERTION(aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),
  11254               "GetIBSplitSibling should only be called on ib-split frames");
  11255 
  11256  if (aFrame->Style()->GetPseudoType() !=
  11257      PseudoStyleType::mozBlockInsideInlineWrapper) {
  11258    // it's not an anonymous block
  11259    return nullptr;
  11260  }
  11261 
  11262  // Find the first continuation of the frame.  (Ugh.  This ends up
  11263  // being O(N^2) when it is called O(N) times.)
  11264  aFrame = aFrame->FirstContinuation();
  11265 
  11266  /*
  11267   * Now look up the nsGkAtoms::IBSplitPrevSibling
  11268   * property.
  11269   */
  11270  nsIFrame* ibSplitSibling =
  11271      aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
  11272  NS_ASSERTION(ibSplitSibling, "Broken frame tree?");
  11273  return ibSplitSibling;
  11274 }
  11275 
  11276 /**
  11277 * Get the parent, corrected for the mangled frame tree resulting from
  11278 * having a block within an inline.  The result only differs from the
  11279 * result of |GetParent| when |GetParent| returns an anonymous block
  11280 * that was created for an element that was 'display: inline' because
  11281 * that element contained a block.
  11282 *
  11283 * Also skip anonymous scrolled-content parents; inherit directly from the
  11284 * outer scroll frame.
  11285 *
  11286 * Also skip NAC parents if the child frame is NAC.
  11287 */
  11288 static nsIFrame* GetCorrectedParent(const nsIFrame* aFrame) {
  11289  nsIFrame* parent = aFrame->GetParent();
  11290  if (!parent) {
  11291    return nullptr;
  11292  }
  11293 
  11294  // For a table caption we want the _inner_ table frame (unless it's anonymous)
  11295  // as the style parent.
  11296  if (aFrame->IsTableCaption()) {
  11297    MOZ_ASSERT(parent->IsTableWrapperFrame());
  11298    nsTableFrame* innerTable =
  11299        static_cast<const nsTableWrapperFrame*>(parent)->InnerTableFrame();
  11300    if (!innerTable->Style()->IsAnonBox()) {
  11301      return innerTable;
  11302    }
  11303  }
  11304 
  11305  // Table wrappers are always anon boxes; if we're in here for an outer
  11306  // table, that actually means its the _inner_ table that wants to
  11307  // know its parent. So get the pseudo of the inner in that case.
  11308  auto pseudo = aFrame->Style()->GetPseudoType();
  11309  if (pseudo == PseudoStyleType::tableWrapper) {
  11310    MOZ_ASSERT(aFrame->IsTableWrapperFrame());
  11311    nsTableFrame* innerTable =
  11312        static_cast<const nsTableWrapperFrame*>(aFrame)->InnerTableFrame();
  11313    pseudo = innerTable->Style()->GetPseudoType();
  11314  }
  11315 
  11316  // Prevent a NAC pseudo-element from inheriting from its NAC parent, and
  11317  // inherit from the NAC generator element instead. (We exclude element-backed
  11318  // pseudos from this check, since they're not NAC.)
  11319  if (pseudo != PseudoStyleType::NotPseudo &&
  11320      !PseudoStyle::IsElementBackedPseudo(pseudo)) {
  11321    MOZ_ASSERT(aFrame->GetContent());
  11322    Element* element = Element::FromNode(aFrame->GetContent());
  11323    // Make sure to only do the fixup for anonymous content pseudos (i.e. avoid
  11324    // fixup for ::first-line and such).
  11325    if (element && !element->IsRootOfNativeAnonymousSubtree() &&
  11326        element->GetPseudoElementType() == aFrame->Style()->GetPseudoType()) {
  11327      while (parent->GetContent() &&
  11328             !parent->GetContent()->IsRootOfNativeAnonymousSubtree()) {
  11329        parent = parent->GetInFlowParent();
  11330      }
  11331      parent = parent->GetInFlowParent();
  11332    }
  11333  }
  11334 
  11335  return nsIFrame::CorrectStyleParentFrame(parent, pseudo);
  11336 }
  11337 
  11338 /* static */
  11339 nsIFrame* nsIFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
  11340                                            PseudoStyleType aChildPseudo) {
  11341  MOZ_ASSERT(aProspectiveParent, "Must have a prospective parent");
  11342 
  11343  if (aChildPseudo != PseudoStyleType::NotPseudo) {
  11344    // Non-inheriting anon boxes have no style parent frame at all.
  11345    if (PseudoStyle::IsNonInheritingAnonBox(aChildPseudo)) {
  11346      return nullptr;
  11347    }
  11348 
  11349    // Other anon boxes are parented to their actual parent already, except
  11350    // for non-elements.  Those should not be treated as an anon box.
  11351    if (PseudoStyle::IsAnonBox(aChildPseudo) &&
  11352        !nsCSSAnonBoxes::IsNonElement(aChildPseudo)) {
  11353      NS_ASSERTION(aChildPseudo != PseudoStyleType::mozBlockInsideInlineWrapper,
  11354                   "Should have dealt with kids that have "
  11355                   "NS_FRAME_PART_OF_IBSPLIT elsewhere");
  11356      return aProspectiveParent;
  11357    }
  11358  }
  11359 
  11360  // Otherwise, walk up out of all anon boxes.  For placeholder frames, walk out
  11361  // of all pseudo-elements as well.  Otherwise ReparentComputedStyle could
  11362  // cause style data to be out of sync with the frame tree.
  11363  nsIFrame* parent = aProspectiveParent;
  11364  do {
  11365    if (parent->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
  11366      nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent);
  11367 
  11368      if (sibling) {
  11369        // |parent| was a block in an {ib} split; use the inline as
  11370        // |the style parent.
  11371        parent = sibling;
  11372      }
  11373    }
  11374 
  11375    if (!parent->Style()->IsPseudoOrAnonBox()) {
  11376      return parent;
  11377    }
  11378 
  11379    if (!parent->Style()->IsAnonBox() && aChildPseudo != PseudoStyleType::MAX) {
  11380      // nsPlaceholderFrame passes in PseudoStyleType::MAX for
  11381      // aChildPseudo (even though that's not a valid pseudo-type) just to
  11382      // trigger this behavior of walking up to the nearest non-pseudo
  11383      // ancestor.
  11384      return parent;
  11385    }
  11386 
  11387    parent = parent->GetInFlowParent();
  11388  } while (parent);
  11389 
  11390  if (aProspectiveParent->Style()->GetPseudoType() ==
  11391      PseudoStyleType::viewportScroll) {
  11392    // aProspectiveParent is the scrollframe for a viewport
  11393    // and the kids are the anonymous scrollbars
  11394    return aProspectiveParent;
  11395  }
  11396 
  11397  // We can get here if the root element is absolutely positioned.
  11398  // We can't test for this very accurately, but it can only happen
  11399  // when the prospective parent is a canvas frame.
  11400  NS_ASSERTION(aProspectiveParent->IsCanvasFrame(),
  11401               "Should have found a parent before this");
  11402  return nullptr;
  11403 }
  11404 
  11405 ComputedStyle* nsIFrame::DoGetParentComputedStyle(
  11406    nsIFrame** aProviderFrame) const {
  11407  *aProviderFrame = nullptr;
  11408 
  11409  // Handle display:contents and the root frame, when there's no parent frame
  11410  // to inherit from.
  11411  if (MOZ_LIKELY(mContent)) {
  11412    Element* parentElement = mContent->GetFlattenedTreeParentElement();
  11413    if (MOZ_LIKELY(parentElement)) {
  11414      auto pseudo = Style()->GetPseudoType();
  11415      if (pseudo == PseudoStyleType::NotPseudo || !mContent->IsElement() ||
  11416          (!PseudoStyle::IsAnonBox(pseudo) &&
  11417           // Ensure that we don't return the display:contents style
  11418           // of the parent content for pseudos that have the same content
  11419           // as their primary frame (like -moz-list-bullets do):
  11420           IsPrimaryFrame()) ||
  11421          /* if next is true then it's really a request for the table frame's
  11422             parent context, see nsTable[Outer]Frame::GetParentComputedStyle. */
  11423          pseudo == PseudoStyleType::tableWrapper) {
  11424        // In some edge cases involving display: contents, we may end up here
  11425        // for something that's pending to be reframed. In this case we return
  11426        // the wrong style from here (because we've already lost track of it!),
  11427        // but it's not a big deal as we're going to be reframed anyway.
  11428        if (MOZ_LIKELY(parentElement->HasServoData()) &&
  11429            Servo_Element_IsDisplayContents(parentElement)) {
  11430          RefPtr<ComputedStyle> style =
  11431              ServoStyleSet::ResolveServoStyle(*parentElement);
  11432          // NOTE(emilio): we return a weak reference because the element also
  11433          // holds the style context alive. This is a bit silly (we could've
  11434          // returned a weak ref directly), but it's probably not worth
  11435          // optimizing, given this function has just one caller which is rare,
  11436          // and this path is rare itself.
  11437          return style;
  11438        }
  11439      }
  11440    } else {
  11441      if (Style()->GetPseudoType() == PseudoStyleType::NotPseudo) {
  11442        // We're a frame for the root.  We have no style parent.
  11443        return nullptr;
  11444      }
  11445    }
  11446  }
  11447 
  11448  if (!HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
  11449    /*
  11450     * If this frame is an anonymous block created when an inline with a block
  11451     * inside it got split, then the parent style is on its preceding inline. We
  11452     * can get to it using GetIBSplitSiblingForAnonymousBlock.
  11453     */
  11454    if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
  11455      nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this);
  11456      if (ibSplitSibling) {
  11457        return (*aProviderFrame = ibSplitSibling)->Style();
  11458      }
  11459    }
  11460 
  11461    // If this frame is one of the blocks that split an inline, we must
  11462    // return the "special" inline parent, i.e., the parent that this
  11463    // frame would have if we didn't mangle the frame structure.
  11464    *aProviderFrame = GetCorrectedParent(this);
  11465    return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
  11466  }
  11467 
  11468  // We're an out-of-flow frame.  For out-of-flow frames, we must
  11469  // resolve underneath the placeholder's parent.  The placeholder is
  11470  // reached from the first-in-flow.
  11471  nsPlaceholderFrame* placeholder = FirstInFlow()->GetPlaceholderFrame();
  11472  if (!placeholder) {
  11473    MOZ_ASSERT_UNREACHABLE("no placeholder frame for out-of-flow frame");
  11474    *aProviderFrame = GetCorrectedParent(this);
  11475    return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
  11476  }
  11477  return placeholder->GetParentComputedStyleForOutOfFlow(aProviderFrame);
  11478 }
  11479 
  11480 void nsIFrame::GetLastLeaf(nsIFrame** aFrame) {
  11481  if (!aFrame || !*aFrame) {
  11482    return;
  11483  }
  11484  for (nsIFrame* maybeLastLeaf = (*aFrame)->PrincipalChildList().LastChild();
  11485       maybeLastLeaf;) {
  11486    nsIFrame* lastChildNotInSubTree = nullptr;
  11487    for (nsIFrame* child = maybeLastLeaf; child;
  11488         child = child->GetPrevSibling()) {
  11489      // ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
  11490      // see bug 278197 comment #12 #13 for details
  11491      if (!child->ContentIsRootOfNativeAnonymousSubtree()) {
  11492        lastChildNotInSubTree = child;
  11493        break;
  11494      }
  11495    }
  11496    if (!lastChildNotInSubTree) {
  11497      return;
  11498    }
  11499    *aFrame = lastChildNotInSubTree;
  11500    maybeLastLeaf = lastChildNotInSubTree->PrincipalChildList().LastChild();
  11501  }
  11502 }
  11503 
  11504 void nsIFrame::GetFirstLeaf(nsIFrame** aFrame) {
  11505  if (!aFrame || !*aFrame) {
  11506    return;
  11507  }
  11508  nsIFrame* child = *aFrame;
  11509  while (true) {
  11510    child = child->PrincipalChildList().FirstChild();
  11511    if (!child) {
  11512      return;  // nothing to do
  11513    }
  11514    *aFrame = child;
  11515  }
  11516 }
  11517 
  11518 bool nsIFrame::IsFocusableDueToScrollFrame() {
  11519  if (!IsScrollContainerFrame()) {
  11520    if (nsFieldSetFrame* fieldset = do_QueryFrame(this)) {
  11521      // TODO: Do we have similar special-cases like this where we can have
  11522      // anonymous scrollable boxes hanging off a primary frame?
  11523      if (nsIFrame* inner = fieldset->GetInner()) {
  11524        return inner->IsFocusableDueToScrollFrame();
  11525      }
  11526    }
  11527    return false;
  11528  }
  11529  if (!mContent->IsHTMLElement()) {
  11530    return false;
  11531  }
  11532  if (mContent->IsRootOfNativeAnonymousSubtree()) {
  11533    return false;
  11534  }
  11535  if (!mContent->GetParent()) {
  11536    return false;
  11537  }
  11538  if (mContent->AsElement()->HasAttr(nsGkAtoms::tabindex)) {
  11539    return false;
  11540  }
  11541  // Elements with scrollable view are focusable with script & tabbable
  11542  // Otherwise you couldn't scroll them with keyboard, which is an accessibility
  11543  // issue (e.g. Section 508 rules) However, we don't make them to be focusable
  11544  // with the mouse, because the extra focus outlines are considered
  11545  // unnecessarily ugly.  When clicked on, the selection position within the
  11546  // element will be enough to make them keyboard scrollable.
  11547  auto* scrollContainer = static_cast<ScrollContainerFrame*>(this);
  11548  if (scrollContainer->GetScrollStyles().IsHiddenInBothDirections()) {
  11549    return false;
  11550  }
  11551  if (scrollContainer->GetScrollRange().IsEqualEdges(nsRect())) {
  11552    return false;
  11553  }
  11554  return true;
  11555 }
  11556 
  11557 Focusable nsIFrame::IsFocusable(IsFocusableFlags aFlags) {
  11558  // cannot focus content in print preview mode. Only the root can be focused,
  11559  // but that's handled elsewhere.
  11560  if (PresContext()->Type() == nsPresContext::eContext_PrintPreview) {
  11561    return {};
  11562  }
  11563 
  11564  if (!mContent || !mContent->IsElement()) {
  11565    return {};
  11566  }
  11567 
  11568  if (!(aFlags & IsFocusableFlags::IgnoreVisibility) &&
  11569      !IsVisibleConsideringAncestors()) {
  11570    return {};
  11571  }
  11572 
  11573  const StyleUserFocus uf = StyleUI()->UserFocus();
  11574  if (uf == StyleUserFocus::None) {
  11575    return {};
  11576  }
  11577  MOZ_ASSERT(!StyleUI()->IsInert(), "inert implies -moz-user-focus: none");
  11578 
  11579  const PseudoStyleType pseudo = Style()->GetPseudoType();
  11580  if (pseudo == PseudoStyleType::anonymousItem) {
  11581    return {};
  11582  }
  11583 
  11584  Focusable focusable;
  11585  if (auto* xul = nsXULElement::FromNode(mContent)) {
  11586    // As a legacy special-case, -moz-user-focus controls focusability and
  11587    // tabability of XUL elements in some circumstances (which default to
  11588    // -moz-user-focus: ignore).
  11589    auto focusability = xul->GetXULFocusability(aFlags);
  11590    focusable.mFocusable =
  11591        focusability.mForcedFocusable.valueOr(uf == StyleUserFocus::Normal);
  11592    if (focusable) {
  11593      focusable.mTabIndex = focusability.mForcedTabIndexIfFocusable.valueOr(0);
  11594    }
  11595  } else {
  11596    focusable = mContent->IsFocusableWithoutStyle(aFlags);
  11597  }
  11598 
  11599  if (focusable) {
  11600    return focusable;
  11601  }
  11602 
  11603  // If we're focusing with the mouse we never focus scroll areas.
  11604  if (!(aFlags & IsFocusableFlags::WithMouse) &&
  11605      IsFocusableDueToScrollFrame()) {
  11606    return {true, 0};
  11607  }
  11608 
  11609  // FIXME(emilio): some callers rely on somewhat broken return values
  11610  // (focusable = false, but non-negative tab-index) from
  11611  // IsFocusableWithoutStyle (for image maps in particular).
  11612  return focusable;
  11613 }
  11614 
  11615 /**
  11616 * @return true if this text frame ends with a newline character which is
  11617 * treated as preformatted. It should return false if this is not a text frame.
  11618 */
  11619 bool nsIFrame::HasSignificantTerminalNewline() const { return false; }
  11620 
  11621 static StyleVerticalAlignKeyword ConvertSVGDominantBaselineToVerticalAlign(
  11622    StyleDominantBaseline aDominantBaseline) {
  11623  // Most of these are approximate mappings.
  11624  switch (aDominantBaseline) {
  11625    case StyleDominantBaseline::Hanging:
  11626    case StyleDominantBaseline::TextBeforeEdge:
  11627      return StyleVerticalAlignKeyword::TextTop;
  11628    case StyleDominantBaseline::TextAfterEdge:
  11629    case StyleDominantBaseline::Ideographic:
  11630      return StyleVerticalAlignKeyword::TextBottom;
  11631    case StyleDominantBaseline::Central:
  11632    case StyleDominantBaseline::Middle:
  11633    case StyleDominantBaseline::Mathematical:
  11634      return StyleVerticalAlignKeyword::Middle;
  11635    case StyleDominantBaseline::Auto:
  11636    case StyleDominantBaseline::Alphabetic:
  11637      return StyleVerticalAlignKeyword::Baseline;
  11638    default:
  11639      MOZ_ASSERT_UNREACHABLE("unexpected aDominantBaseline value");
  11640      return StyleVerticalAlignKeyword::Baseline;
  11641  }
  11642 }
  11643 
  11644 Maybe<StyleVerticalAlignKeyword> nsIFrame::VerticalAlignEnum() const {
  11645  if (IsInSVGTextSubtree()) {
  11646    StyleDominantBaseline dominantBaseline = StyleSVG()->mDominantBaseline;
  11647    return Some(ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline));
  11648  }
  11649 
  11650  const auto& verticalAlign = StyleDisplay()->mVerticalAlign;
  11651  if (verticalAlign.IsKeyword()) {
  11652    return Some(verticalAlign.AsKeyword());
  11653  }
  11654 
  11655  return Nothing();
  11656 }
  11657 
  11658 void nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
  11659                                         ServoRestyleState& aRestyleState) {
  11660 #ifdef DEBUG
  11661  nsIFrame* parent = aChildFrame->GetInFlowParent();
  11662  if (aChildFrame->IsTableFrame()) {
  11663    parent = parent->GetParent();
  11664  }
  11665  if (parent->IsLineFrame()) {
  11666    parent = parent->GetParent();
  11667  }
  11668  MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this,
  11669             "This should only be used for children!");
  11670 #endif  // DEBUG
  11671  MOZ_ASSERT(!GetContent() || !aChildFrame->GetContent() ||
  11672                 aChildFrame->GetContent() == GetContent(),
  11673             "What content node is it a frame for?");
  11674  MOZ_ASSERT(!aChildFrame->GetPrevContinuation(),
  11675             "Only first continuations should end up here");
  11676 
  11677  // We could force the caller to pass in the pseudo, since some callers know it
  11678  // statically...  But this API is a bit nicer.
  11679  auto pseudo = aChildFrame->Style()->GetPseudoType();
  11680  MOZ_ASSERT(PseudoStyle::IsAnonBox(pseudo), "Child is not an anon box?");
  11681  MOZ_ASSERT(!PseudoStyle::IsNonInheritingAnonBox(pseudo),
  11682             "Why did the caller bother calling us?");
  11683 
  11684  // Anon boxes inherit from their parent; that's us.
  11685  RefPtr<ComputedStyle> newContext =
  11686      aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo,
  11687                                                                  Style());
  11688 
  11689  nsChangeHint childHint =
  11690      UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aRestyleState);
  11691 
  11692  // Now that we've updated the style on aChildFrame, check whether it itself
  11693  // has anon boxes to deal with.
  11694  ServoRestyleState childrenState(*aChildFrame, aRestyleState, childHint,
  11695                                  ServoRestyleState::CanUseHandledHints::Yes);
  11696  aChildFrame->UpdateStyleOfOwnedAnonBoxes(childrenState);
  11697 
  11698  // Assuming anon boxes don't have ::backdrop associated with them... if that
  11699  // ever changes, we'd need to handle that here, like we do in
  11700  // RestyleManager::ProcessPostTraversal
  11701 
  11702  // We do need to handle block pseudo-elements here, though.  Especially list
  11703  // bullets.
  11704  if (nsBlockFrame* block = do_QueryFrame(aChildFrame)) {
  11705    block->UpdatePseudoElementStyles(childrenState);
  11706  }
  11707 }
  11708 
  11709 /* static */
  11710 nsChangeHint nsIFrame::UpdateStyleOfOwnedChildFrame(
  11711    nsIFrame* aChildFrame, ComputedStyle* aNewComputedStyle,
  11712    ServoRestyleState& aRestyleState,
  11713    const Maybe<ComputedStyle*>& aContinuationComputedStyle) {
  11714  MOZ_ASSERT(!aChildFrame->GetAdditionalComputedStyle(0),
  11715             "We don't handle additional styles here");
  11716 
  11717  // Figure out whether we have an actual change.  It's important that we do
  11718  // this, for several reasons:
  11719  //
  11720  // 1) Even if all the child's changes are due to properties it inherits from
  11721  //    us, it's possible that no one ever asked us for those style structs and
  11722  //    hence changes to them aren't reflected in the changes handled at all.
  11723  //
  11724  // 2) Content can change stylesheets that change the styles of pseudos, and
  11725  //    extensions can add/remove stylesheets that change the styles of
  11726  //    anonymous boxes directly.
  11727  uint32_t equalStructs;  // Not used, actually.
  11728  nsChangeHint childHint = aChildFrame->Style()->CalcStyleDifference(
  11729      *aNewComputedStyle, &equalStructs);
  11730 
  11731  // If aChildFrame is out of flow, then aRestyleState's "changes handled by the
  11732  // parent" doesn't apply to it, because it may have some other parent in the
  11733  // frame tree.
  11734  if (!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
  11735    childHint = NS_RemoveSubsumedHints(
  11736        childHint, aRestyleState.ChangesHandledFor(aChildFrame));
  11737  }
  11738  if (childHint) {
  11739    if (childHint & nsChangeHint_ReconstructFrame) {
  11740      // If we generate a reconstruct here, remove any non-reconstruct hints we
  11741      // may have already generated for this content.
  11742      aRestyleState.ChangeList().PopChangesForContent(
  11743          aChildFrame->GetContent());
  11744    }
  11745    aRestyleState.ChangeList().AppendChange(
  11746        aChildFrame, aChildFrame->GetContent(), childHint);
  11747  }
  11748 
  11749  aChildFrame->SetComputedStyle(aNewComputedStyle);
  11750  ComputedStyle* continuationStyle = aContinuationComputedStyle
  11751                                         ? *aContinuationComputedStyle
  11752                                         : aNewComputedStyle;
  11753  for (nsIFrame* kid = aChildFrame->GetNextContinuation(); kid;
  11754       kid = kid->GetNextContinuation()) {
  11755    MOZ_ASSERT(!kid->GetAdditionalComputedStyle(0));
  11756    kid->SetComputedStyle(continuationStyle);
  11757  }
  11758 
  11759  return childHint;
  11760 }
  11761 
  11762 /* static */
  11763 void nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame) {
  11764  if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
  11765      aFrame->TrackingVisibility()) {
  11766    // Assume all frames in popups are visible.
  11767    aFrame->IncApproximateVisibleCount();
  11768  }
  11769 
  11770  aFrame->AddStateBits(NS_FRAME_IN_POPUP);
  11771 
  11772  for (const auto& childList : aFrame->CrossDocChildLists()) {
  11773    for (nsIFrame* child : childList.mList) {
  11774      AddInPopupStateBitToDescendants(child);
  11775    }
  11776  }
  11777 }
  11778 
  11779 /* static */
  11780 void nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame) {
  11781  if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
  11782      nsLayoutUtils::IsPopup(aFrame)) {
  11783    return;
  11784  }
  11785 
  11786  aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
  11787 
  11788  if (aFrame->TrackingVisibility()) {
  11789    // We assume all frames in popups are visible, so this decrement balances
  11790    // out the increment in AddInPopupStateBitToDescendants above.
  11791    aFrame->DecApproximateVisibleCount();
  11792  }
  11793  for (const auto& childList : aFrame->CrossDocChildLists()) {
  11794    for (nsIFrame* child : childList.mList) {
  11795      RemoveInPopupStateBitFromDescendants(child);
  11796    }
  11797  }
  11798 }
  11799 
  11800 void nsIFrame::SetParent(nsContainerFrame* aParent) {
  11801  // If our parent is a wrapper anon box, our new parent should be too.  We
  11802  // _can_ change parent if our parent is a wrapper anon box, because some
  11803  // wrapper anon boxes can have continuations.
  11804  MOZ_ASSERT_IF(ParentIsWrapperAnonBox(),
  11805                aParent->Style()->IsInheritingAnonBox());
  11806 
  11807  // Note that the current mParent may already be destroyed at this point.
  11808  mParent = aParent;
  11809  MOZ_ASSERT(!mParent || PresShell() == mParent->PresShell());
  11810 
  11811  nsFrameState flagsToPropagateSameDoc =
  11812      GetStateBits() & (NS_FRAME_CONTAINS_RELATIVE_BSIZE |
  11813                        NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
  11814  if (flagsToPropagateSameDoc) {
  11815    for (nsIFrame* f = aParent; f; f = f->GetParent()) {
  11816      if (f->HasAllStateBits(flagsToPropagateSameDoc)) {
  11817        break;
  11818      }
  11819      f->AddStateBits(flagsToPropagateSameDoc);
  11820    }
  11821  }
  11822 
  11823  if (HasInvalidFrameInSubtree()) {
  11824    for (nsIFrame* f = aParent;
  11825         f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT |
  11826                                  NS_FRAME_IS_NONDISPLAY);
  11827         f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
  11828      f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
  11829    }
  11830  }
  11831 
  11832  if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
  11833    AddInPopupStateBitToDescendants(this);
  11834  } else {
  11835    RemoveInPopupStateBitFromDescendants(this);
  11836  }
  11837 
  11838  // If our new parent only has invalid children, then we just invalidate
  11839  // ourselves too. This is probably faster than clearing the flag all
  11840  // the way up the frame tree.
  11841  if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
  11842    InvalidateFrame();
  11843  } else {
  11844    SchedulePaint();
  11845  }
  11846 }
  11847 
  11848 bool nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
  11849                                 const nsStyleEffects* aStyleEffects) {
  11850  // Properties that influence the output of this function should be handled in
  11851  // change_bits_for_longhand as well.
  11852  if (HasOpacity(aStyleDisplay, aStyleEffects, nullptr)) {
  11853    return true;
  11854  }
  11855  if (IsTransformed()) {
  11856    return true;
  11857  }
  11858  auto willChange = aStyleDisplay->mWillChange.bits;
  11859  if (aStyleDisplay->IsContainPaint() || aStyleDisplay->IsContainLayout() ||
  11860      willChange & StyleWillChangeBits::CONTAIN) {
  11861    if (SupportsContainLayoutAndPaint()) {
  11862      return true;
  11863    }
  11864  }
  11865 
  11866  if (ForcesStackingContextForViewTransition()) {
  11867    return true;
  11868  }
  11869 
  11870  // strictly speaking, 'perspective' doesn't require visual atomicity,
  11871  // but the spec says it acts like the rest of these
  11872  if (aStyleDisplay->HasPerspectiveStyle() ||
  11873      willChange & StyleWillChangeBits::PERSPECTIVE) {
  11874    if (SupportsCSSTransforms()) {
  11875      return true;
  11876    }
  11877  }
  11878  if (!StylePosition()->mZIndex.IsAuto() ||
  11879      willChange & StyleWillChangeBits::Z_INDEX) {
  11880    if (ZIndexApplies()) {
  11881      return true;
  11882    }
  11883  }
  11884  // Elements captured in a view transition during a view transition or whose
  11885  // view-transition-name computed value is not none (at any time) form a s
  11886  // https://drafts.csswg.org/css-view-transitions-1/#named-and-transitioning
  11887  return aStyleEffects->mMixBlendMode != StyleBlend::Normal ||
  11888         SVGIntegrationUtils::UsingEffectsForFrame(this) ||
  11889         aStyleDisplay->IsPositionForcingStackingContext() ||
  11890         aStyleDisplay->mIsolation != StyleIsolation::Auto ||
  11891         willChange & StyleWillChangeBits::STACKING_CONTEXT_UNCONDITIONAL;
  11892 }
  11893 
  11894 bool nsIFrame::IsStackingContext() {
  11895  return IsStackingContext(StyleDisplay(), StyleEffects());
  11896 }
  11897 
  11898 static bool IsFrameRectScrolledOutOfView(const nsIFrame* aTarget,
  11899                                         const nsRect& aTargetRect,
  11900                                         const nsIFrame* aParent) {
  11901  // The ancestor frame we are checking if it clips out aTargetRect relative to
  11902  // aTarget.
  11903  nsIFrame* clipParent = nullptr;
  11904 
  11905  // find the first scrollable frame or root frame if we are in a fixed pos
  11906  // subtree
  11907  for (nsIFrame* f = const_cast<nsIFrame*>(aParent); f;
  11908       f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
  11909    ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(f);
  11910    if (scrollContainerFrame) {
  11911      clipParent = f;
  11912      break;
  11913    }
  11914    if (f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
  11915        nsLayoutUtils::IsReallyFixedPos(f)) {
  11916      clipParent = f->GetParent();
  11917      break;
  11918    }
  11919  }
  11920 
  11921  if (!clipParent) {
  11922    // Even if we couldn't find the nearest scrollable frame, it might mean we
  11923    // are in an out-of-process iframe, try to see if |aTarget| frame is
  11924    // scrolled out of view in an scrollable frame in a cross-process ancestor
  11925    // document.
  11926    return nsLayoutUtils::FrameRectIsScrolledOutOfViewInCrossProcess(
  11927        aTarget, aTargetRect);
  11928  }
  11929 
  11930  nsRect clipRect = clipParent->InkOverflowRectRelativeToSelf();
  11931  // We consider that the target is scrolled out if the scrollable (or root)
  11932  // frame is empty.
  11933  if (clipRect.IsEmpty()) {
  11934    return true;
  11935  }
  11936 
  11937  nsRect transformedRect = nsLayoutUtils::TransformFrameRectToAncestor(
  11938      aTarget, aTargetRect, clipParent);
  11939 
  11940  if (transformedRect.IsEmpty()) {
  11941    // If the transformed rect is empty it represents a line or a point that we
  11942    // should check is outside the the scrollable rect.
  11943    if (transformedRect.x > clipRect.XMost() ||
  11944        transformedRect.y > clipRect.YMost() ||
  11945        clipRect.x > transformedRect.XMost() ||
  11946        clipRect.y > transformedRect.YMost()) {
  11947      return true;
  11948    }
  11949  } else if (!transformedRect.Intersects(clipRect)) {
  11950    return true;
  11951  }
  11952 
  11953  nsIFrame* parent = clipParent->GetParent();
  11954  if (!parent) {
  11955    return false;
  11956  }
  11957 
  11958  return IsFrameRectScrolledOutOfView(clipParent, transformedRect, parent);
  11959 }
  11960 
  11961 bool nsIFrame::IsScrolledOutOfView() const {
  11962  nsRect rect = InkOverflowRectRelativeToSelf();
  11963  return IsFrameRectScrolledOutOfView(this, rect, this);
  11964 }
  11965 
  11966 gfx::Matrix nsIFrame::ComputeWidgetTransform() const {
  11967  const nsStyleUIReset* uiReset = StyleUIReset();
  11968  if (uiReset->mMozWindowTransform.IsNone()) {
  11969    return gfx::Matrix();
  11970  }
  11971 
  11972  TransformReferenceBox refBox(nullptr, nsRect(nsPoint(), GetSize()));
  11973 
  11974  int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
  11975  gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
  11976      uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel));
  11977 
  11978  gfx::Matrix result2d;
  11979  if (!matrix.CanDraw2D(&result2d)) {
  11980    // FIXME: It would be preferable to reject non-2D transforms at parse time.
  11981    NS_WARNING(
  11982        "-moz-window-transform does not describe a 2D transform, "
  11983        "but only 2d transforms are supported");
  11984    return gfx::Matrix();
  11985  }
  11986 
  11987  return result2d;
  11988 }
  11989 
  11990 void nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState& aRestyleState) {
  11991  // As a special case, we check for {ib}-split block frames here, rather
  11992  // than have an nsInlineFrame::AppendDirectlyOwnedAnonBoxes implementation
  11993  // that returns them.
  11994  //
  11995  // (If we did handle them in AppendDirectlyOwnedAnonBoxes, we would have to
  11996  // return *all* of the in-flow {ib}-split block frames, not just the first
  11997  // one.  For restyling, we really just need the first in flow, and the other
  11998  // user of the AppendOwnedAnonBoxes API, AllChildIterator, doesn't need to
  11999  // know about them at all, since these block frames never create NAC.  So we
  12000  // avoid any unncessary hashtable lookups for the {ib}-split frames by calling
  12001  // UpdateStyleOfOwnedAnonBoxesForIBSplit directly here.)
  12002  if (IsInlineFrame()) {
  12003    if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
  12004      static_cast<nsInlineFrame*>(this)->UpdateStyleOfOwnedAnonBoxesForIBSplit(
  12005          aRestyleState);
  12006    }
  12007    return;
  12008  }
  12009 
  12010  AutoTArray<OwnedAnonBox, 4> frames;
  12011  AppendDirectlyOwnedAnonBoxes(frames);
  12012  for (OwnedAnonBox& box : frames) {
  12013    if (box.mUpdateStyleFn) {
  12014      box.mUpdateStyleFn(this, box.mAnonBoxFrame, aRestyleState);
  12015    } else {
  12016      UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
  12017    }
  12018  }
  12019 }
  12020 
  12021 /* virtual */
  12022 void nsIFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
  12023  MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES));
  12024  MOZ_ASSERT_UNREACHABLE(
  12025      "Subclasses that have directly owned anonymous boxes should override "
  12026      "this method!");
  12027 }
  12028 
  12029 void nsIFrame::DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
  12030  size_t i = aResult.Length();
  12031  AppendDirectlyOwnedAnonBoxes(aResult);
  12032 
  12033  // After appending the directly owned anonymous boxes of this frame to
  12034  // aResult above, we need to check each of them to see if they own
  12035  // any anonymous boxes themselves.  Note that we keep progressing
  12036  // through aResult, looking for additional entries in aResult from these
  12037  // subsequent AppendDirectlyOwnedAnonBoxes calls.  (Thus we can't
  12038  // use a ranged for loop here.)
  12039 
  12040  while (i < aResult.Length()) {
  12041    nsIFrame* f = aResult[i].mAnonBoxFrame;
  12042    if (f->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES)) {
  12043      f->AppendDirectlyOwnedAnonBoxes(aResult);
  12044    }
  12045    ++i;
  12046  }
  12047 }
  12048 
  12049 nsIFrame::CaretPosition::CaretPosition() : mContentOffset(0) {}
  12050 
  12051 nsIFrame::CaretPosition::~CaretPosition() = default;
  12052 
  12053 bool nsIFrame::HasCSSAnimations() {
  12054  auto* collection = AnimationCollection<CSSAnimation>::Get(this);
  12055  return collection && !collection->mAnimations.IsEmpty();
  12056 }
  12057 
  12058 bool nsIFrame::HasCSSTransitions() {
  12059  auto* collection = AnimationCollection<CSSTransition>::Get(this);
  12060  return collection && !collection->mAnimations.IsEmpty();
  12061 }
  12062 
  12063 void nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const {
  12064  aSizes.mLayoutFramePropertiesSize +=
  12065      mProperties.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
  12066 
  12067  // We don't do this for Gecko because this stuff is stored in the nsPresArena
  12068  // and so measured elsewhere.
  12069  if (!aSizes.mState.HaveSeenPtr(mComputedStyle)) {
  12070    mComputedStyle->AddSizeOfIncludingThis(aSizes,
  12071                                           &aSizes.mLayoutComputedValuesNonDom);
  12072  }
  12073 
  12074  // And our additional styles.
  12075  int32_t index = 0;
  12076  while (auto* extra = GetAdditionalComputedStyle(index++)) {
  12077    if (!aSizes.mState.HaveSeenPtr(extra)) {
  12078      extra->AddSizeOfIncludingThis(aSizes,
  12079                                    &aSizes.mLayoutComputedValuesNonDom);
  12080    }
  12081  }
  12082 
  12083  for (const auto& childList : ChildLists()) {
  12084    for (const nsIFrame* f : childList.mList) {
  12085      f->AddSizeOfExcludingThisForTree(aSizes);
  12086    }
  12087  }
  12088 }
  12089 
  12090 nsRect nsIFrame::GetCompositorHitTestArea(nsDisplayListBuilder* aBuilder) {
  12091  nsRect area;
  12092 
  12093  ScrollContainerFrame* scrollContainerFrame =
  12094      nsLayoutUtils::GetScrollContainerFrameFor(this);
  12095  if (scrollContainerFrame) {
  12096    // If this frame is the scrolled frame of a scroll container frame, then we
  12097    // need to pick up the area corresponding to the overflow rect as well.
  12098    // Otherwise the parts of the overflow that are not occupied by descendants
  12099    // get skipped and the APZ code sends touch events to the content underneath
  12100    // instead. See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
  12101    area = ScrollableOverflowRect();
  12102  } else {
  12103    area = GetRectRelativeToSelf();
  12104  }
  12105 
  12106  if (!area.IsEmpty()) {
  12107    return area + aBuilder->ToReferenceFrame(this);
  12108  }
  12109 
  12110  return area;
  12111 }
  12112 
  12113 CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
  12114    nsDisplayListBuilder* aBuilder) {
  12115  CompositorHitTestInfo result = CompositorHitTestInvisibleToHit;
  12116  if (Style()->PointerEvents() == StylePointerEvents::None) {
  12117    return result;
  12118  }
  12119  return GetCompositorHitTestInfoWithoutPointerEvents(aBuilder);
  12120 }
  12121 
  12122 CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfoWithoutPointerEvents(
  12123    nsDisplayListBuilder* aBuilder) {
  12124  CompositorHitTestInfo result = CompositorHitTestInvisibleToHit;
  12125 
  12126  if (aBuilder->IsInsidePointerEventsNoneDoc() ||
  12127      aBuilder->IsInViewTransitionCapture()) {
  12128    // Somewhere up the parent document chain is a subdocument with pointer-
  12129    // events:none set on it, or we're getting captured in a view transition.
  12130    return result;
  12131  }
  12132  if (!GetParent()) {
  12133    MOZ_ASSERT(IsViewportFrame());
  12134    // Viewport frames are never event targets, other frames, like canvas
  12135    // frames, are the event targets for any regions viewport frames may cover.
  12136    return result;
  12137  }
  12138  if (!StyleVisibility()->IsVisible()) {
  12139    return result;
  12140  }
  12141 
  12142  // Anything that didn't match the above conditions is visible to hit-testing.
  12143  result = CompositorHitTestFlags::eVisibleToHitTest;
  12144  SVGUtils::MaskUsage maskUsage = SVGUtils::DetermineMaskUsage(this, false);
  12145  if (maskUsage.UsingMaskOrClipPath()) {
  12146    // If WebRender is enabled, simple clip-paths can be converted into WR
  12147    // clips that WR knows how to hit-test against, so we don't need to mark
  12148    // it as an irregular area.
  12149    if (!maskUsage.IsSimpleClipShape()) {
  12150      result += CompositorHitTestFlags::eIrregularArea;
  12151    }
  12152  }
  12153 
  12154  if (aBuilder->IsBuildingNonLayerizedScrollbar()) {
  12155    // Scrollbars may be painted into a layer below the actual layer they will
  12156    // scroll, and therefore wheel events may be dispatched to the outer frame
  12157    // instead of the intended scrollframe. To address this, we force a d-t-c
  12158    // region on scrollbar frames that won't be placed in their own layer. See
  12159    // bug 1213324 for details.
  12160    result += CompositorHitTestFlags::eInactiveScrollframe;
  12161  } else if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
  12162    result += CompositorHitTestFlags::eApzAwareListeners;
  12163  } else if (IsRangeFrame()) {
  12164    // Range frames handle touch events directly without having a touch listener
  12165    // so we need to let APZ know that this area cares about events.
  12166    result += CompositorHitTestFlags::eApzAwareListeners;
  12167  }
  12168 
  12169  if (aBuilder->IsTouchEventPrefEnabledDoc()) {
  12170    // Inherit the touch-action flags from the parent, if there is one. We do
  12171    // this because of how the touch-action on a frame combines the touch-action
  12172    // from ancestor DOM elements. Refer to the documentation in
  12173    // TouchActionHelper.cpp for details; this code is meant to be equivalent to
  12174    // that code, but woven into the top-down recursive display list building
  12175    // process.
  12176    CompositorHitTestInfo inheritedTouchAction =
  12177        aBuilder->GetInheritedCompositorHitTestInfo() &
  12178        CompositorHitTestTouchActionMask;
  12179 
  12180    nsIFrame* touchActionFrame = this;
  12181    if (ScrollContainerFrame* scrollContainerFrame =
  12182            nsLayoutUtils::GetScrollContainerFrameFor(this)) {
  12183      ScrollStyles ss = scrollContainerFrame->GetScrollStyles();
  12184      if (ss.mVertical != StyleOverflow::Hidden ||
  12185          ss.mHorizontal != StyleOverflow::Hidden) {
  12186        touchActionFrame = scrollContainerFrame;
  12187        // On scrollframes, stop inheriting the pan-x and pan-y flags; instead,
  12188        // reset them back to zero to allow panning on the scrollframe unless we
  12189        // encounter an element that disables it that's inside the scrollframe.
  12190        // This is equivalent to the |considerPanning| variable in
  12191        // TouchActionHelper.cpp, but for a top-down traversal.
  12192        CompositorHitTestInfo panMask(
  12193            CompositorHitTestFlags::eTouchActionPanXDisabled,
  12194            CompositorHitTestFlags::eTouchActionPanYDisabled);
  12195        inheritedTouchAction -= panMask;
  12196      }
  12197    }
  12198 
  12199    result += inheritedTouchAction;
  12200 
  12201    const StyleTouchAction touchAction = touchActionFrame->UsedTouchAction();
  12202    // The CSS allows the syntax auto | none | [pan-x || pan-y] | manipulation
  12203    // so we can eliminate some combinations of things.
  12204    if (touchAction == StyleTouchAction::AUTO) {
  12205      // nothing to do
  12206    } else if (touchAction & StyleTouchAction::MANIPULATION) {
  12207      result += CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled;
  12208    } else {
  12209      // This path handles the cases none | [pan-x || pan-y || pinch-zoom] so
  12210      // double-tap is disabled in here.
  12211      if (!(touchAction & StyleTouchAction::PINCH_ZOOM)) {
  12212        result += CompositorHitTestFlags::eTouchActionPinchZoomDisabled;
  12213      }
  12214 
  12215      result += CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled;
  12216 
  12217      if (!(touchAction & StyleTouchAction::PAN_X)) {
  12218        result += CompositorHitTestFlags::eTouchActionPanXDisabled;
  12219      }
  12220      if (!(touchAction & StyleTouchAction::PAN_Y)) {
  12221        result += CompositorHitTestFlags::eTouchActionPanYDisabled;
  12222      }
  12223      if (touchAction & StyleTouchAction::NONE) {
  12224        // all the touch-action disabling flags will already have been set above
  12225        MOZ_ASSERT(result.contains(CompositorHitTestTouchActionMask));
  12226      }
  12227    }
  12228  }
  12229 
  12230  const Maybe<ScrollDirection> scrollDirection =
  12231      aBuilder->GetCurrentScrollbarDirection();
  12232  if (scrollDirection.isSome()) {
  12233    if (GetContent()->IsXULElement(nsGkAtoms::thumb)) {
  12234      const bool thumbGetsLayer = aBuilder->GetCurrentScrollbarTarget() !=
  12235                                  layers::ScrollableLayerGuid::NULL_SCROLL_ID;
  12236      if (thumbGetsLayer) {
  12237        result += CompositorHitTestFlags::eScrollbarThumb;
  12238      } else {
  12239        result += CompositorHitTestFlags::eInactiveScrollframe;
  12240      }
  12241    }
  12242 
  12243    if (*scrollDirection == ScrollDirection::eVertical) {
  12244      result += CompositorHitTestFlags::eScrollbarVertical;
  12245    }
  12246 
  12247    // includes the ScrollbarFrame, SliderFrame, anything else that
  12248    // might be inside the xul:scrollbar
  12249    result += CompositorHitTestFlags::eScrollbar;
  12250  }
  12251 
  12252  return result;
  12253 }
  12254 
  12255 // Returns true if we can guarantee there is no visible descendants.
  12256 static bool HasNoVisibleDescendants(const nsIFrame* aFrame) {
  12257  for (const auto& childList : aFrame->ChildLists()) {
  12258    for (nsIFrame* f : childList.mList) {
  12259      if (nsPlaceholderFrame::GetRealFrameFor(f)
  12260              ->IsVisibleOrMayHaveVisibleDescendants()) {
  12261        return false;
  12262      }
  12263    }
  12264  }
  12265  return true;
  12266 }
  12267 
  12268 void nsIFrame::UpdateVisibleDescendantsState() {
  12269  if (StyleVisibility()->IsVisible()) {
  12270    // Notify invisible ancestors that a visible descendant exists now.
  12271    nsIFrame* ancestor;
  12272    for (ancestor = GetInFlowParent();
  12273         ancestor && !ancestor->StyleVisibility()->IsVisible();
  12274         ancestor = ancestor->GetInFlowParent()) {
  12275      ancestor->mAllDescendantsAreInvisible = false;
  12276    }
  12277  } else {
  12278    mAllDescendantsAreInvisible = HasNoVisibleDescendants(this);
  12279  }
  12280 }
  12281 
  12282 PhysicalAxes nsIFrame::ShouldApplyOverflowClipping(
  12283    const nsStyleDisplay* aDisp) const {
  12284  MOZ_ASSERT(aDisp == StyleDisplay(), "Wrong display struct");
  12285 
  12286  if (IsScrollContainerOrSubclass()) {
  12287    // Scrollers deal with overflow on their own.
  12288    return {};
  12289  }
  12290 
  12291  // 'contain:paint', which we handle as 'overflow:clip' here. 'contain:paint'
  12292  // should prevent all means of escaping that clipping (e.g. because it forms a
  12293  // fixed-pos containing block).
  12294  if (aDisp->IsContainPaint() && SupportsContainLayoutAndPaint()) {
  12295    return kPhysicalAxesBoth;
  12296  }
  12297 
  12298  // and overflow: scrollable that we should interpret as clip
  12299  if (aDisp->IsScrollableOverflow()) {
  12300    // REVIEW: these are the frame types that set up clipping.
  12301    LayoutFrameType type = Type();
  12302    switch (type) {
  12303      case LayoutFrameType::CheckboxRadio:
  12304      case LayoutFrameType::ComboboxControl:
  12305      case LayoutFrameType::Progress:
  12306      case LayoutFrameType::Range:
  12307      case LayoutFrameType::SubDocument:
  12308      case LayoutFrameType::SVGForeignObject:
  12309      case LayoutFrameType::SVGInnerSVG:
  12310      case LayoutFrameType::SVGOuterSVG:
  12311      case LayoutFrameType::SVGSymbol:
  12312      case LayoutFrameType::Image:
  12313      case LayoutFrameType::TableCell:
  12314        return kPhysicalAxesBoth;
  12315      case LayoutFrameType::Table:
  12316        // Tables, for legacy reasons only clip when hidden in both directions,
  12317        // and treat all other scrollable overflow values as `visible`.
  12318        // This is (somewhat, since other browsers make this change at
  12319        // computed-value time, see bug 1918789) interoperable css2-era
  12320        // behavior. We might be able to change this, but not today.
  12321        // See layout/reftests/table-overflow/bug785684-x.html
  12322        return aDisp->mOverflowX == StyleOverflow::Hidden &&
  12323                       aDisp->mOverflowY == StyleOverflow::Hidden
  12324                   ? kPhysicalAxesBoth
  12325                   : PhysicalAxes();
  12326      case LayoutFrameType::TextInput:
  12327        // It has an anonymous scroll container frame that handles any overflow.
  12328        return PhysicalAxes();
  12329      default:
  12330        break;
  12331    }
  12332    if (IsSuppressedScrollableBlockForPrint()) {
  12333      return kPhysicalAxesBoth;
  12334    }
  12335  }
  12336 
  12337  if (aDisp->mOverflowX == StyleOverflow::Clip ||
  12338      aDisp->mOverflowY == StyleOverflow::Clip) {
  12339    // FIXME: we could use GetViewportScrollStylesOverrideElement() here instead
  12340    // if that worked correctly in a print context. (see bug 1654667)
  12341    const auto* element = Element::FromNodeOrNull(GetContent());
  12342    if (!element ||
  12343        !PresContext()->ElementWouldPropagateScrollStyles(*element)) {
  12344      PhysicalAxes axes;
  12345      if (aDisp->mOverflowX == StyleOverflow::Clip) {
  12346        axes += PhysicalAxis::Horizontal;
  12347      }
  12348      if (aDisp->mOverflowY == StyleOverflow::Clip) {
  12349        axes += PhysicalAxis::Vertical;
  12350      }
  12351      return axes;
  12352    }
  12353  }
  12354 
  12355  return PhysicalAxes();
  12356 }
  12357 
  12358 bool nsIFrame::IsSuppressedScrollableBlockForPrint() const {
  12359  // This condition needs to match the suppressScrollFrame logic in the frame
  12360  // constructor.
  12361  if (!PresContext()->IsPaginated() || !IsBlockFrame() ||
  12362      !StyleDisplay()->IsScrollableOverflow() ||
  12363      !StyleDisplay()->IsBlockOutsideStyle() ||
  12364      mContent->IsInNativeAnonymousSubtree()) {
  12365    return false;
  12366  }
  12367  if (auto* element = Element::FromNode(mContent);
  12368      element && PresContext()->ElementWouldPropagateScrollStyles(*element)) {
  12369    return false;
  12370  }
  12371  return true;
  12372 }
  12373 
  12374 PhysicalAxes nsIFrame::GetAnchorPosCompensatingForScroll() const {
  12375  if (!HasAnchorPosReference()) {
  12376    return {};
  12377  }
  12378  const auto* prop = GetProperty(AnchorPosReferences());
  12379  if (!prop) {
  12380    return {};
  12381  }
  12382 
  12383  return prop->CompensatingForScrollAxes();
  12384 }
  12385 
  12386 bool nsIFrame::HasUnreflowedContainerQueryAncestor() const {
  12387  // If this frame has done the first reflow, its ancestors are guaranteed to
  12388  // have as well.
  12389  if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW) ||
  12390      !PresContext()->HasContainerQueryFrames()) {
  12391    return false;
  12392  }
  12393  for (nsIFrame* cur = GetInFlowParent(); cur; cur = cur->GetInFlowParent()) {
  12394    if (!cur->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
  12395      // Done first reflow from this ancestor up, including query containers.
  12396      return false;
  12397    }
  12398    if (cur->StyleDisplay()->IsQueryContainer()) {
  12399      return true;
  12400    }
  12401  }
  12402  // No query container from this frame up to root.
  12403  return false;
  12404 }
  12405 
  12406 bool nsIFrame::ShouldBreakBefore(
  12407    const ReflowInput::BreakType aBreakType) const {
  12408  const auto* display = StyleDisplay();
  12409  return ShouldBreakBetween(display, display->mBreakBefore, aBreakType);
  12410 }
  12411 
  12412 bool nsIFrame::ShouldBreakAfter(const ReflowInput::BreakType aBreakType) const {
  12413  const auto* display = StyleDisplay();
  12414  return ShouldBreakBetween(display, display->mBreakAfter, aBreakType);
  12415 }
  12416 
  12417 bool nsIFrame::ShouldBreakBetween(
  12418    const nsStyleDisplay* aDisplay, const StyleBreakBetween aBreakBetween,
  12419    const ReflowInput::BreakType aBreakType) const {
  12420  const bool shouldBreakBetween = [&] {
  12421    switch (aBreakBetween) {
  12422      case StyleBreakBetween::Always:
  12423        return true;
  12424      case StyleBreakBetween::Auto:
  12425      case StyleBreakBetween::Avoid:
  12426        return false;
  12427      case StyleBreakBetween::Page:
  12428      case StyleBreakBetween::Left:
  12429      case StyleBreakBetween::Right:
  12430        return aBreakType == ReflowInput::BreakType::Page;
  12431    }
  12432    MOZ_ASSERT_UNREACHABLE("Unknown break-between value!");
  12433    return false;
  12434  }();
  12435 
  12436  if (!shouldBreakBetween) {
  12437    return false;
  12438  }
  12439  if (IsAbsolutelyPositioned(aDisplay)) {
  12440    // 'break-before' and 'break-after' properties does not apply to
  12441    // absolutely-positioned boxes.
  12442    return false;
  12443  }
  12444  return true;
  12445 }
  12446 
  12447 #ifdef DEBUG
  12448 static void GetTagName(nsIFrame* aFrame, nsIContent* aContent, int aResultSize,
  12449                       char* aResult) {
  12450  if (aContent) {
  12451    snprintf(aResult, aResultSize, "%s@%p",
  12452             nsAtomCString(aContent->NodeInfo()->NameAtom()).get(), aFrame);
  12453  } else {
  12454    snprintf(aResult, aResultSize, "@%p", aFrame);
  12455  }
  12456 }
  12457 
  12458 void nsIFrame::Trace(const char* aMethod, bool aEnter) {
  12459  if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
  12460    char tagbuf[40];
  12461    GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
  12462    printf_stderr("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
  12463  }
  12464 }
  12465 
  12466 void nsIFrame::Trace(const char* aMethod, bool aEnter,
  12467                     const nsReflowStatus& aStatus) {
  12468  if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
  12469    char tagbuf[40];
  12470    GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
  12471    printf_stderr("%s: %s %s, status=%scomplete%s", tagbuf,
  12472                  aEnter ? "enter" : "exit", aMethod,
  12473                  aStatus.IsIncomplete() ? "not" : "",
  12474                  (aStatus.NextInFlowNeedsReflow()) ? "+reflow" : "");
  12475  }
  12476 }
  12477 
  12478 void nsIFrame::TraceMsg(const char* aFormatString, ...) {
  12479  if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
  12480    // Format arguments into a buffer
  12481    char argbuf[200];
  12482    va_list ap;
  12483    va_start(ap, aFormatString);
  12484    VsprintfLiteral(argbuf, aFormatString, ap);
  12485    va_end(ap);
  12486 
  12487    char tagbuf[40];
  12488    GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
  12489    printf_stderr("%s: %s", tagbuf, argbuf);
  12490  }
  12491 }
  12492 
  12493 void nsIFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) {
  12494  for (nsIFrame* f : aFrameList) {
  12495    NS_ASSERTION(f->HasAnyStateBits(NS_FRAME_IS_DIRTY), "dirty bit not set");
  12496  }
  12497 }
  12498 
  12499 // Validation of SideIsVertical.
  12500 #  define CASE(side, result) \
  12501    static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong")
  12502 CASE(eSideTop, false);
  12503 CASE(eSideRight, true);
  12504 CASE(eSideBottom, false);
  12505 CASE(eSideLeft, true);
  12506 #  undef CASE
  12507 
  12508 // Validation of HalfCornerIsX.
  12509 #  define CASE(corner, result) \
  12510    static_assert(HalfCornerIsX(corner) == result, "HalfCornerIsX is wrong")
  12511 CASE(eCornerTopLeftX, true);
  12512 CASE(eCornerTopLeftY, false);
  12513 CASE(eCornerTopRightX, true);
  12514 CASE(eCornerTopRightY, false);
  12515 CASE(eCornerBottomRightX, true);
  12516 CASE(eCornerBottomRightY, false);
  12517 CASE(eCornerBottomLeftX, true);
  12518 CASE(eCornerBottomLeftY, false);
  12519 #  undef CASE
  12520 
  12521 // Validation of HalfToFullCorner.
  12522 #  define CASE(corner, result)                        \
  12523    static_assert(HalfToFullCorner(corner) == result, \
  12524                  "HalfToFullCorner is "              \
  12525                  "wrong")
  12526 CASE(eCornerTopLeftX, eCornerTopLeft);
  12527 CASE(eCornerTopLeftY, eCornerTopLeft);
  12528 CASE(eCornerTopRightX, eCornerTopRight);
  12529 CASE(eCornerTopRightY, eCornerTopRight);
  12530 CASE(eCornerBottomRightX, eCornerBottomRight);
  12531 CASE(eCornerBottomRightY, eCornerBottomRight);
  12532 CASE(eCornerBottomLeftX, eCornerBottomLeft);
  12533 CASE(eCornerBottomLeftY, eCornerBottomLeft);
  12534 #  undef CASE
  12535 
  12536 // Validation of FullToHalfCorner.
  12537 #  define CASE(corner, vert, result)                        \
  12538    static_assert(FullToHalfCorner(corner, vert) == result, \
  12539                  "FullToHalfCorner is wrong")
  12540 CASE(eCornerTopLeft, false, eCornerTopLeftX);
  12541 CASE(eCornerTopLeft, true, eCornerTopLeftY);
  12542 CASE(eCornerTopRight, false, eCornerTopRightX);
  12543 CASE(eCornerTopRight, true, eCornerTopRightY);
  12544 CASE(eCornerBottomRight, false, eCornerBottomRightX);
  12545 CASE(eCornerBottomRight, true, eCornerBottomRightY);
  12546 CASE(eCornerBottomLeft, false, eCornerBottomLeftX);
  12547 CASE(eCornerBottomLeft, true, eCornerBottomLeftY);
  12548 #  undef CASE
  12549 
  12550 // Validation of SideToFullCorner.
  12551 #  define CASE(side, second, result)                        \
  12552    static_assert(SideToFullCorner(side, second) == result, \
  12553                  "SideToFullCorner is wrong")
  12554 CASE(eSideTop, false, eCornerTopLeft);
  12555 CASE(eSideTop, true, eCornerTopRight);
  12556 
  12557 CASE(eSideRight, false, eCornerTopRight);
  12558 CASE(eSideRight, true, eCornerBottomRight);
  12559 
  12560 CASE(eSideBottom, false, eCornerBottomRight);
  12561 CASE(eSideBottom, true, eCornerBottomLeft);
  12562 
  12563 CASE(eSideLeft, false, eCornerBottomLeft);
  12564 CASE(eSideLeft, true, eCornerTopLeft);
  12565 #  undef CASE
  12566 
  12567 // Validation of SideToHalfCorner.
  12568 #  define CASE(side, second, parallel, result)                        \
  12569    static_assert(SideToHalfCorner(side, second, parallel) == result, \
  12570                  "SideToHalfCorner is wrong")
  12571 CASE(eSideTop, false, true, eCornerTopLeftX);
  12572 CASE(eSideTop, false, false, eCornerTopLeftY);
  12573 CASE(eSideTop, true, true, eCornerTopRightX);
  12574 CASE(eSideTop, true, false, eCornerTopRightY);
  12575 
  12576 CASE(eSideRight, false, false, eCornerTopRightX);
  12577 CASE(eSideRight, false, true, eCornerTopRightY);
  12578 CASE(eSideRight, true, false, eCornerBottomRightX);
  12579 CASE(eSideRight, true, true, eCornerBottomRightY);
  12580 
  12581 CASE(eSideBottom, false, true, eCornerBottomRightX);
  12582 CASE(eSideBottom, false, false, eCornerBottomRightY);
  12583 CASE(eSideBottom, true, true, eCornerBottomLeftX);
  12584 CASE(eSideBottom, true, false, eCornerBottomLeftY);
  12585 
  12586 CASE(eSideLeft, false, false, eCornerBottomLeftX);
  12587 CASE(eSideLeft, false, true, eCornerBottomLeftY);
  12588 CASE(eSideLeft, true, false, eCornerTopLeftX);
  12589 CASE(eSideLeft, true, true, eCornerTopLeftY);
  12590 #  undef CASE
  12591 
  12592 #endif